diff --git a/.editorconfig b/.editorconfig index ee55bb502..319299684 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ -# http://editorconfig.org root = true [*] indent_style = space indent_size = 2 +end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true diff --git a/.eslintignore b/.eslintignore index bf7fdf065..129fb3332 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,17 @@ -.idea/ -.vscode/ -build/ +# 忽略目录 +node_modules +test-cases +test +output +build +dist +demo +es +lib .* ~* -node_modules +# 忽略文件 +**/*.min.js +**/*-min.js +**/*.bundle.js diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 847ef2c13..000000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./node_modules/@ali/lowcode-config/.eslintrc" -} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..dab7837f3 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: 'eslint-config-ali/typescript/react', +}; diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 28af681fc..000000000 --- a/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -**/test/**/*.ts -**/test/**/*.js -**/template/**/*.template -**/template/**/*.tpl diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index ba026f068..000000000 --- a/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "trailingComma": "all", - "tabWidth": 2, - "semi": true, - "singleQuote": true, - "printWidth": 120, - "arrowParens": "always" -} diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..24c5859e6 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + printWidth: 100, + tabWidth: 2, + semi: true, + singleQuote: true, + trailingComma: 'all', +}; diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 000000000..bec25cf29 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,9 @@ +# 忽略目录 +node_modules/ +build/ +dist/ + +# 忽略文件 +**/*.min.css +**/*-min.css +**/*.bundle.css diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 000000000..74a5a54e3 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: 'stylelint-config-ali', +}; diff --git a/.vscode/launch.json b/.vscode/launch.json index f242fc1f3..98523af22 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "node", "request": "launch", "runtimeExecutable": "${workspaceFolder}/packages/material-parser/node_modules/.bin/ava", - "runtimeArgs": ["debug", "--break", "${file}"] + "runtimeArgs": ["debug", "--break", "${workspaceFolder}/packages/material-parser/test/antd.ts"] } ] } diff --git a/README.md b/README.md index 34ff152c5..6cfc5a252 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ ## Ali Lowcode Engine(阿里低代码引擎) -[Lerna](https://github.com/lerna/lerna) + [TS](https://www.typescriptlang.org/) - ## 开发 -#### 创建新包: +#### 创建新包 - `./scripts/create.sh ` -#### 跑起来: +#### 运行示例 - `npm run setup` - `npm start` -#### 开发提交: +#### 开发提交 - `git add ` -- `npm run commit` # 在根目录 +- `git commit -a "feat: xxx"` ## 发布 @@ -24,7 +22,6 @@ ## 注意 -- Commit 动作尽量使用 `npm run commit`,其内部调用了 `git cz`,方便按语义化版本自动递增,以及自动生成 `CHANGELOG.md` - `packages` 工程里一些开发时公共依赖(比如:`typescript`、`ava` 等)会放到工程顶层 - 工程里的 `.md`、`test/` 等文件修改不会产生新的发布 - 当工程里存在多个 ts 文件的目录时,最终产生的文件会按文件夹形式放到 `lib` 下 diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 000000000..52f3b754b --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ['ali'], +}; diff --git a/package.json b/package.json index ac1ca5168..a8f50276a 100644 --- a/package.json +++ b/package.json @@ -12,40 +12,34 @@ "scripts": { "build": "./scripts/build.sh", "clean": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/dist ./packages/*/build", - "commit": "git-cz", + "lint": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet", + "lint:fix": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet --fix", "pub": "lerna publish --force-publish --cd-version prepatch", "setup": "./scripts/setup.sh", "start": "./scripts/start.sh", "start:server": "./scripts/start-server.sh", "test": "lerna run test --stream", - "test:snapshot": "lerna run test:snapshot" + "test:snapshot": "lerna run test:snapshot", + "xima:fix": "xima fix", + "xima:scan": "xima scan" }, - "lint-staged": { - "*.{tsx,ts}": [ - "eslint --quiet", - "git add" - ] - }, - "config": { - "commitizen": { - "path": "node_modules/cz-conventional-changelog" + "husky": { + "hooks": { + "commit-msg": "xima exec commitlint -E HUSKY_GIT_PARAMS", + "pre-commit": "xima exec lint-staged" } }, + "lint-staged": { + "**/*.{js,jsx,ts,tsx}": "xima exec eslint", + "**/*.{css,scss,less}": "xima exec stylelint" + }, "devDependencies": { - "@ali/lowcode-config": "^2.0.5", "ava": "^1.0.1", - "babel-eslint": "^10.0.3", - "commitizen": "^3.0.5", - "cz-conventional-changelog": "^2.1.0", - "eslint": "^6.5.1", - "git-cz": "^4.3.1", - "husky": "^4.2.3", "lerna": "^2.11.0", - "lint-staged": "^8.1.0", - "prettier": "^1.15.3", "ts-node": "^7.0.1", "tslib": "^1.9.3", - "typescript": "^3.2.2" + "typescript": "^3.2.2", + "xima": "^0.2.15" }, "engines": { "node": ">=10.0.0" @@ -54,4 +48,4 @@ "mode": "yarn", "lockfile": "enable" } -} +} \ No newline at end of file diff --git a/packages/code-generator/demo/demo.js b/packages/code-generator/demo/demo.js index a3ca9ca46..1c760446d 100644 --- a/packages/code-generator/demo/demo.js +++ b/packages/code-generator/demo/demo.js @@ -59,7 +59,7 @@ function main() { builder.generateProject(schemaJson).then((result) => { displayResultInConsole(result); - writeResultToDisk(result, 'output/lowcodeDemo').then((response) => console.log('Write to disk: ', JSON.stringify(response)),); + writeResultToDisk(result, 'output/lowcodeDemo').then((response) => console.log('Write to disk: ', JSON.stringify(response))); return result; }); } @@ -160,7 +160,7 @@ function exportProject() { builder.generateProject(schemaJson).then((result) => { displayResultInConsole(result); - writeResultToDisk(result, 'output/lowcodeDemo').then((response) => console.log('Write to disk: ', JSON.stringify(response)),); + writeResultToDisk(result, 'output/lowcodeDemo').then((response) => console.log('Write to disk: ', JSON.stringify(response))); return result; }); } diff --git a/packages/code-generator/src/@types/ali__my-prettier/index.d.ts b/packages/code-generator/src/@types/ali__my-prettier/index.d.ts index d21a2b18a..1751a79ff 100644 --- a/packages/code-generator/src/@types/ali__my-prettier/index.d.ts +++ b/packages/code-generator/src/@types/ali__my-prettier/index.d.ts @@ -1,4 +1,4 @@ -/// +// / declare module '@ali/my-prettier' { function format(text: string, type: string): string; diff --git a/packages/code-generator/src/const/generator.ts b/packages/code-generator/src/const/generator.ts index 9028b9226..3d3950420 100644 --- a/packages/code-generator/src/const/generator.ts +++ b/packages/code-generator/src/const/generator.ts @@ -109,6 +109,6 @@ export const DEFAULT_LINK_AFTER = { [COMMON_CHUNK_NAME.StyleDepsImport]: [], [COMMON_CHUNK_NAME.StyleCssContent]: [COMMON_CHUNK_NAME.StyleDepsImport], [COMMON_CHUNK_NAME.HtmlContent]: [], -} +}; export const COMMON_SUB_MODULE_NAME = 'index'; diff --git a/packages/code-generator/src/const/index.ts b/packages/code-generator/src/const/index.ts index d637ad03c..790fa23e0 100644 --- a/packages/code-generator/src/const/index.ts +++ b/packages/code-generator/src/const/index.ts @@ -1,4 +1,4 @@ -export const NATIVE_ELE_PKG: string = 'native'; +export const NATIVE_ELE_PKG = 'native'; export const CONTAINER_TYPE = { COMPONENT: 'Component', diff --git a/packages/code-generator/src/generator/CodeBuilder.ts b/packages/code-generator/src/generator/CodeBuilder.ts index ad7f33622..96fbb5710 100644 --- a/packages/code-generator/src/generator/CodeBuilder.ts +++ b/packages/code-generator/src/generator/CodeBuilder.ts @@ -12,7 +12,7 @@ export default class Builder implements ICodeBuilder { private generators: { [key: string]: CodeGeneratorFunction } = { [ChunkType.STRING]: (str: string) => str, // no-op for string chunks - [ChunkType.JSON]: (json: object) => JSON.stringify(json), // stringify json to string + [ChunkType.JSON]: (json: Record) => JSON.stringify(json), // stringify json to string }; constructor(chunkDefinitions: ICodeChunk[] = []) { @@ -58,14 +58,15 @@ export default class Builder implements ICodeBuilder { const { type, content, name } = unprocessedChunks[indexToRemove]; const compiledContent = this.generateByType(type, content); if (compiledContent) { - resultingString.push(compiledContent + '\n'); + resultingString.push(`${compiledContent }\n`); } unprocessedChunks.splice(indexToRemove, 1); if (!unprocessedChunks.some(ch => ch.name === name)) { unprocessedChunks.forEach( // remove the processed chunk from all the linkAfter arrays from the remaining chunks - ch => (ch.linkAfter = ch.linkAfter.filter(after => after !== name)), + // eslint-disable-next-line no-param-reassign + ch => { ch.linkAfter = ch.linkAfter.filter(after => after !== name); }, ); } } @@ -95,8 +96,6 @@ export default class Builder implements ICodeBuilder { // remove invalid chunks (which did not end up being created) from the linkAfter fields // one use-case is when you want to remove the import plugin private cleanupInvalidChunks(linkAfter: string[], chunks: ICodeChunk[]) { - return linkAfter.filter(chunkName => - chunks.some(chunk => chunk.name === chunkName), - ); + return linkAfter.filter(chunkName => chunks.some(chunk => chunk.name === chunkName)); } } diff --git a/packages/code-generator/src/generator/ModuleBuilder.ts b/packages/code-generator/src/generator/ModuleBuilder.ts index 063434cb4..f70abc669 100644 --- a/packages/code-generator/src/generator/ModuleBuilder.ts +++ b/packages/code-generator/src/generator/ModuleBuilder.ts @@ -51,7 +51,7 @@ export function createModuleBuilder( if (options.postProcessors.length > 0) { files = files.map((file) => { - let content = file.content; + let { content } = file; const type = file.ext; options.postProcessors.forEach((processer) => { content = processer(content, type); diff --git a/packages/code-generator/src/generator/ProjectBuilder.ts b/packages/code-generator/src/generator/ProjectBuilder.ts index 34211d0e5..a10ad67e8 100644 --- a/packages/code-generator/src/generator/ProjectBuilder.ts +++ b/packages/code-generator/src/generator/ProjectBuilder.ts @@ -40,7 +40,9 @@ function getDirFromRoot(root: IResultDir, path: string[]): IResultDir { export class ProjectBuilder implements IProjectBuilder { private template: IProjectTemplate; + private plugins: IProjectPlugins; + private postProcessors: PostProcessor[]; constructor({ diff --git a/packages/code-generator/src/model/ResultDir.ts b/packages/code-generator/src/model/ResultDir.ts index 3888859c1..c456a9459 100644 --- a/packages/code-generator/src/model/ResultDir.ts +++ b/packages/code-generator/src/model/ResultDir.ts @@ -2,7 +2,9 @@ import { CodeGeneratorError, IResultDir, IResultFile } from '../types'; export default class ResultDir implements IResultDir { public name: string; + public dirs: IResultDir[]; + public files: IResultFile[]; constructor(name: string) { diff --git a/packages/code-generator/src/model/ResultFile.ts b/packages/code-generator/src/model/ResultFile.ts index 08b3032b8..b8064bfab 100644 --- a/packages/code-generator/src/model/ResultFile.ts +++ b/packages/code-generator/src/model/ResultFile.ts @@ -2,10 +2,12 @@ import { IResultFile } from '../types'; export default class ResultFile implements IResultFile { public name: string; + public ext: string; + public content: string; - constructor(name: string, ext: string = 'jsx', content: string = '') { + constructor(name: string, ext = 'jsx', content = '') { this.name = name; this.ext = ext; this.content = content; diff --git a/packages/code-generator/src/parser/SchemaParser.ts b/packages/code-generator/src/parser/SchemaParser.ts index ddb9e4c1c..1c3ef0eb0 100644 --- a/packages/code-generator/src/parser/SchemaParser.ts +++ b/packages/code-generator/src/parser/SchemaParser.ts @@ -20,7 +20,6 @@ import { IExternalDependency, IInternalDependency, InternalDependencyType, - IPageMeta, IParseResult, IProjectSchema, ISchemaParser, @@ -95,7 +94,7 @@ class SchemaParser implements ISchemaParser { }); } } else { - throw new CodeGeneratorError(`Can't find anything to generate.`); + throw new CodeGeneratorError('Can\'t find anything to generate.'); } // 建立所有容器的内部依赖索引 @@ -160,7 +159,7 @@ class SchemaParser implements ISchemaParser { const routes = containers .filter((container) => container.containerType === 'Page') .map((page) => { - const meta = page.meta as IPageMeta; + const { meta } = page; if (meta) { return { path: meta.router, @@ -190,7 +189,9 @@ class SchemaParser implements ISchemaParser { let npms: INpmPackage[] = []; containers.forEach((con) => { const p = (con.deps || []) - .map((dep) => (dep.dependencyType === DependencyType.External ? dep : null)) + .map((dep) => { + return dep.dependencyType === DependencyType.External ? dep : null; + }) .filter((dep) => dep !== null); npms.push(...((p as unknown) as INpmPackage[])); }); diff --git a/packages/code-generator/src/plugins/common/esmodule.ts b/packages/code-generator/src/plugins/common/esmodule.ts index 2fbff2fed..e270988de 100644 --- a/packages/code-generator/src/plugins/common/esmodule.ts +++ b/packages/code-generator/src/plugins/common/esmodule.ts @@ -88,7 +88,9 @@ function buildPackageImport(pkg: string, deps: IDependency[], targetFileType: st } }); - const items = Object.keys(imports).map((src) => (src === imports[src] ? src : `${src} as ${imports[src]}`)); + const items = Object.keys(imports).map((src) => { + return src === imports[src] ? src : `${src} as ${imports[src]}`; + }); const statementL = ['import']; if (defaultImport) { diff --git a/packages/code-generator/src/plugins/common/requireUtils.ts b/packages/code-generator/src/plugins/common/requireUtils.ts index 9da850a4c..6d2fcfe92 100644 --- a/packages/code-generator/src/plugins/common/requireUtils.ts +++ b/packages/code-generator/src/plugins/common/requireUtils.ts @@ -19,7 +19,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.JSX, name: COMMON_CHUNK_NAME.InternalDepsImport, - content: `import * from 'react';`, + content: 'import * from \'react\';', linkAfter: [], }); diff --git a/packages/code-generator/src/plugins/component/rax/containerInitState.ts b/packages/code-generator/src/plugins/component/rax/containerInitState.ts new file mode 100644 index 000000000..0d635963c --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerInitState.ts @@ -0,0 +1,68 @@ +import { CLASS_DEFINE_CHUNK_NAME, DEFAULT_LINK_AFTER } from '../../../const/generator'; + +import { generateCompositeType } from '../../../utils/compositeType'; +import Scope from '../../../utils/Scope'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IContainerInfo, +} from '../../../types'; + +type PluginConfig = { + fileType: string; + implementType: 'inConstructor' | 'insMember' | 'hooks'; +}; + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + implementType: 'insMember', + ...config, + }; + + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IContainerInfo; + const scope = Scope.createRootScope(); + + if (ir.state) { + const { state } = ir; + const fields = Object.keys(state).map((stateName) => { + // TODO: 这里用什么 handlers? + const value = generateCompositeType(state[stateName], scope); + return `${stateName}: ${value}`; + }); + + if (cfg.implementType === 'inConstructor') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, + content: `this.state = { ${fields.join(',')} };`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.ConstructorContent]], + }); + } else if (cfg.implementType === 'insMember') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: `state = { ${fields.join(',')} };`, + linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsVar]], + }); + } + // TODO: hooks state?? + } + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/rax/containerInjectDataSourceEngine.ts b/packages/code-generator/src/plugins/component/rax/containerInjectDataSourceEngine.ts new file mode 100644 index 000000000..6115730b0 --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerInjectDataSourceEngine.ts @@ -0,0 +1,157 @@ +/* eslint-disable @typescript-eslint/indent */ + +import { CompositeValue, JSExpression, DataSourceConfig, isJSExpression, isJSFunction } from '@ali/lowcode-types'; +import changeCase from 'change-case'; + +import { CLASS_DEFINE_CHUNK_NAME, COMMON_CHUNK_NAME } from '../../../const/generator'; +import Scope from '../../../utils/Scope'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IScope, +} from '../../../types'; + +import { generateCompositeType } from '../../../utils/compositeType'; +import { parseExpressionConvertThis2Context } from '../../../utils/expressionParser'; +import { isContainerSchema } from '../../../utils/schema'; +import { RAX_CHUNK_NAME } from './const'; + +type PluginConfig = { + fileType: string; +}; + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + ...config, + }; + + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const scope = Scope.createRootScope(); + const dataSourceConfig = isContainerSchema(pre.ir) ? pre.ir.dataSource : null; + const dataSourceItems: DataSourceConfig[] = (dataSourceConfig && dataSourceConfig.list) || []; + const dataSourceEngineOptions = { runtimeConfig: true }; + if (dataSourceItems.length > 0) { + const requestHandlersMap = {} as Record; + + dataSourceItems.forEach((ds) => { + const dsType = ds.type || 'fetch'; + if (!(dsType in requestHandlersMap) && dsType !== 'custom') { + const handlerFactoryName = `__$$create${ changeCase.pascal(dsType) }RequestHandler`; + + requestHandlersMap[dsType] = { + type: 'JSExpression', + value: handlerFactoryName + (dsType === 'urlParams' ? '(this.props.location.search)' : '()'), + }; + + const handlerFactoryExportName = `create${changeCase.pascal(dsType)}Handler`; + const handlerPkgName = `@ali/lowcode-datasource-${changeCase.kebab(dsType)}-handler`; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.ExternalDepsImport, + content: ` + import { ${handlerFactoryExportName} as ${handlerFactoryName} } from '${handlerPkgName}'; + `, + linkAfter: [], + }); + } + }); + + Object.assign(dataSourceEngineOptions, { requestHandlersMap }); + } + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSX, + name: COMMON_CHUNK_NAME.ExternalDepsImport, + content: ` + import { create as __$$createDataSourceEngine } from '@ali/lowcode-datasource-engine/runtime'; + `, + linkAfter: [], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: ` + _dataSourceConfig = this._defineDataSourceConfig(); + _dataSourceEngine = __$$createDataSourceEngine( + this._dataSourceConfig, + this._context, + ${generateCompositeType(dataSourceEngineOptions, scope)} + );`, + linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.ClassDidMountContent, + content: ` + this._dataSourceEngine.reloadDataSource(); + `, + linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod, + content: ` +_defineDataSourceConfig() { + const __$$context = this._context; + return (${generateCompositeType( + { + ...dataSourceConfig, + list: [ + ...dataSourceItems.map((item) => ({ + ...item, + isInit: wrapAsFunction(item.isInit, scope), + options: wrapAsFunction(item.options, scope), + })), + ], + }, + scope, + { + handlers: { + function: (jsFunc) => parseExpressionConvertThis2Context(jsFunc.value, '__$$context'), + expression: (jsExpr) => parseExpressionConvertThis2Context(jsExpr.value, '__$$context'), + }, + }, + )}); +} + `, + linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd], + }); + + return next; + }; + return plugin; +}; + +export default pluginFactory; + +function wrapAsFunction(value: CompositeValue, scope: IScope): CompositeValue { + if (isJSExpression(value) || isJSFunction(value)) { + return { + type: 'JSExpression', + value: `function(){ return ((${value.value}))}`, + }; + } + + return { + type: 'JSExpression', + value: `function(){return((${generateCompositeType(value, scope)}))}`, + }; +} diff --git a/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts new file mode 100644 index 000000000..28cfac83d --- /dev/null +++ b/packages/code-generator/src/plugins/component/rax/containerLifeCycle.ts @@ -0,0 +1,120 @@ +import _ from 'lodash'; + +import { CLASS_DEFINE_CHUNK_NAME } from '../../../const/generator'; +import { RAX_CHUNK_NAME } from './const'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + FileType, + ChunkType, + ICodeStruct, + IContainerInfo, +} from '../../../types'; + +type PluginConfig = { + fileType: string; + exportNameMapping: Record; + normalizeNameMapping: Record; +}; + +const pluginFactory: BuilderComponentPluginFactory = (config?) => { + const cfg: PluginConfig = { + fileType: FileType.JSX, + exportNameMapping: {}, + normalizeNameMapping: {}, + ...config, + }; + + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + // Rax 先只支持 didMount 和 willUnmount 吧 + + const ir = next.ir as IContainerInfo; + const { lifeCycles } = ir; + + if (lifeCycles && !_.isEmpty(lifeCycles)) { + Object.entries(lifeCycles).forEach(([lifeCycleName, lifeCycleMethodExpr]) => { + const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName; + const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.LifeCyclesContent, + content: `${exportName}: (${lifeCycleMethodExpr.value}),`, + linkAfter: [RAX_CHUNK_NAME.LifeCyclesBegin], + }); + + if (normalizeName === 'didMount') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.ClassDidMountContent, + content: `this._lifeCycles.${exportName}();`, + linkAfter: [RAX_CHUNK_NAME.ClassDidMountBegin], + }); + } else if (normalizeName === 'willUnmount') { + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.ClassWillUnmountContent, + content: `this._lifeCycles.${exportName}();`, + linkAfter: [RAX_CHUNK_NAME.ClassWillUnmountBegin], + }); + } else { + // TODO: print warnings? Unknown life cycle + } + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: CLASS_DEFINE_CHUNK_NAME.InsVar, + content: '_lifeCycles = this._defineLifeCycles();', + linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.LifeCyclesBegin, + content: ` + _defineLifeCycles() { + const __$$lifeCycles = ({ + `, + linkAfter: [RAX_CHUNK_NAME.ClassRenderEnd, CLASS_DEFINE_CHUNK_NAME.InsPrivateMethod], + }); + + next.chunks.push({ + type: ChunkType.STRING, + fileType: cfg.fileType, + name: RAX_CHUNK_NAME.LifeCyclesEnd, + content: ` + }); + + // 为所有的方法绑定上下文 + Object.entries(__$$lifeCycles).forEach(([lifeCycleName, lifeCycleMethod]) => { + if (typeof lifeCycleMethod === 'function') { + __$$lifeCycles[lifeCycleName] = (...args) => { + return lifeCycleMethod.apply(this._context, args); + } + } + }); + + return __$$lifeCycles; + } + `, + linkAfter: [RAX_CHUNK_NAME.LifeCyclesBegin, RAX_CHUNK_NAME.LifeCyclesContent], + }); + } + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/component/react/containerClass.ts b/packages/code-generator/src/plugins/component/react/containerClass.ts index 7ca5e685f..b84e7aa6e 100644 --- a/packages/code-generator/src/plugins/component/react/containerClass.ts +++ b/packages/code-generator/src/plugins/component/react/containerClass.ts @@ -35,7 +35,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.JSX, name: CLASS_DEFINE_CHUNK_NAME.End, - content: `}`, + content: '}', linkAfter: [CLASS_DEFINE_CHUNK_NAME.Start, REACT_CHUNK_NAME.ClassRenderEnd], }); diff --git a/packages/code-generator/src/plugins/component/react/containerDataSource.ts b/packages/code-generator/src/plugins/component/react/containerDataSource.ts index b60cb9539..8907dea36 100644 --- a/packages/code-generator/src/plugins/component/react/containerDataSource.ts +++ b/packages/code-generator/src/plugins/component/react/containerDataSource.ts @@ -29,7 +29,7 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => const ir = next.ir as IContainerInfo; if (ir.state) { - const state = ir.state; + const { state } = ir; const fields = Object.keys(state).map((stateName) => { const value = generateCompositeType(state[stateName]); return `${stateName}: ${value},`; diff --git a/packages/code-generator/src/plugins/component/react/containerInitState.ts b/packages/code-generator/src/plugins/component/react/containerInitState.ts index 9550244f1..3ab10463c 100644 --- a/packages/code-generator/src/plugins/component/react/containerInitState.ts +++ b/packages/code-generator/src/plugins/component/react/containerInitState.ts @@ -31,7 +31,7 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => const ir = next.ir as IContainerInfo; if (ir.state) { - const state = ir.state; + const { state } = ir; const fields = Object.keys(state).map((stateName) => { const value = generateCompositeType(state[stateName]); return `${stateName}: ${value},`; diff --git a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts index 8cded167b..bf065b77d 100644 --- a/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts +++ b/packages/code-generator/src/plugins/component/react/containerInjectUtils.ts @@ -10,7 +10,7 @@ import { type PluginConfig = { fileType: string; -} +}; const pluginFactory: BuilderComponentPluginFactory = (config?) => { const cfg: PluginConfig = { @@ -27,7 +27,7 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => type: ChunkType.STRING, fileType: cfg.fileType, name: CLASS_DEFINE_CHUNK_NAME.ConstructorContent, - content: `this.utils = utils;`, + content: 'this.utils = utils;', linkAfter: [CLASS_DEFINE_CHUNK_NAME.ConstructorStart], }); diff --git a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts index 000355b7d..585cd6d7e 100644 --- a/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts +++ b/packages/code-generator/src/plugins/component/react/containerLifeCycle.ts @@ -35,7 +35,7 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => const ir = next.ir as IContainerInfo; if (ir.lifeCycles) { - const lifeCycles = ir.lifeCycles; + const { lifeCycles } = ir; const chunks = Object.keys(lifeCycles).map((lifeCycleName) => { const normalizeName = cfg.normalizeNameMapping[lifeCycleName] || lifeCycleName; const exportName = cfg.exportNameMapping[lifeCycleName] || lifeCycleName; diff --git a/packages/code-generator/src/plugins/component/react/containerMethod.ts b/packages/code-generator/src/plugins/component/react/containerMethod.ts index 148f2cffd..ecc89d828 100644 --- a/packages/code-generator/src/plugins/component/react/containerMethod.ts +++ b/packages/code-generator/src/plugins/component/react/containerMethod.ts @@ -30,7 +30,7 @@ const pluginFactory: BuilderComponentPluginFactory = (config?) => const ir = next.ir as IContainerInfo; if (ir.methods) { - const methods = ir.methods; + const { methods } = ir; const chunks = Object.keys(methods).map((methodName) => ({ type: ChunkType.STRING, fileType: cfg.fileType, diff --git a/packages/code-generator/src/plugins/component/react/jsx.ts b/packages/code-generator/src/plugins/component/react/jsx.ts index 3f2c7b0e2..78ee3f790 100644 --- a/packages/code-generator/src/plugins/component/react/jsx.ts +++ b/packages/code-generator/src/plugins/component/react/jsx.ts @@ -14,7 +14,7 @@ import { createReactNodeGenerator } from '../../../utils/nodeToJSX'; type PluginConfig = { fileType?: string; nodeTypeMapping?: Record; -} +}; const pluginFactory: BuilderComponentPluginFactory = (config?) => { const cfg = { diff --git a/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts b/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts index 2e75a5b7e..2e71f8d90 100644 --- a/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts +++ b/packages/code-generator/src/plugins/component/react/reactCommonDeps.ts @@ -6,7 +6,6 @@ import { ChunkType, FileType, ICodeStruct, - IContainerInfo, } from '../../../types'; const pluginFactory: BuilderComponentPluginFactory = () => { @@ -19,7 +18,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.JSX, name: COMMON_CHUNK_NAME.ExternalDepsImport, - content: `import React from 'react';`, + content: 'import React from \'react\';', linkAfter: [], }); diff --git a/packages/code-generator/src/plugins/component/recore/pageFrame.ts b/packages/code-generator/src/plugins/component/recore/pageFrame.ts index e302a64bc..80aa08287 100644 --- a/packages/code-generator/src/plugins/component/recore/pageFrame.ts +++ b/packages/code-generator/src/plugins/component/recore/pageFrame.ts @@ -21,7 +21,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.TS, name: COMMON_CHUNK_NAME.ExternalDepsImport, - content: `import { BaseController } from '@ali/recore-renderer';`, + content: 'import { BaseController } from \'@ali/recore-renderer\';', linkAfter: [], }); @@ -37,7 +37,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.TS, name: CLASS_DEFINE_CHUNK_NAME.End, - content: `}`, + content: '}', linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.End]], }); diff --git a/packages/code-generator/src/plugins/component/recore/pageStyle.ts b/packages/code-generator/src/plugins/component/recore/pageStyle.ts index fc8ca41b5..7f243487a 100644 --- a/packages/code-generator/src/plugins/component/recore/pageStyle.ts +++ b/packages/code-generator/src/plugins/component/recore/pageStyle.ts @@ -22,7 +22,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: FileType.TS, name: CLASS_DEFINE_CHUNK_NAME.StaticVar, - content: `static cssText = '${ir.css.replace(/\'/g, '\\\'')}';`, + content: `static cssText = '${ir.css.replace(/'/g, '\\\'')}';`, linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.StaticVar]], }); } diff --git a/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts b/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts index 6ef8ac44f..71ee2335c 100644 --- a/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts +++ b/packages/code-generator/src/plugins/component/recore/pageVmHeader.ts @@ -17,7 +17,7 @@ const pluginFactory: BuilderComponentPluginFactory = () => { type: ChunkType.STRING, fileType: 'vx', name: COMMON_CHUNK_NAME.CustomContent, - content: `
`, + content: '
', linkAfter: [], }); diff --git a/packages/code-generator/src/plugins/component/style/css.ts b/packages/code-generator/src/plugins/component/style/css.ts index a2e8b46f0..167d9be41 100644 --- a/packages/code-generator/src/plugins/component/style/css.ts +++ b/packages/code-generator/src/plugins/component/style/css.ts @@ -12,7 +12,7 @@ import { type PluginConfig = { fileType: string; moduleFileType: string; -} +}; const pluginFactory: BuilderComponentPluginFactory = (config?) => { const cfg: PluginConfig = { diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts index f238b6af0..8d4a864c5 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/packageJSON.ts @@ -73,7 +73,9 @@ const pluginFactory: BuilderComponentPluginFactory = () => { originTemplate: '@alifd/scaffold-lite-js', }; - ir.packages.forEach((packageInfo) => (packageJson.dependencies[packageInfo.package] = packageInfo.version)); + ir.packages.forEach((packageInfo) => { + packageJson.dependencies[packageInfo.package] = packageInfo.version; + }); next.chunks.push({ type: ChunkType.JSON, diff --git a/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts b/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts index b4f2d718f..bb2d7c47f 100644 --- a/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts +++ b/packages/code-generator/src/plugins/project/framework/icejs/plugins/router.ts @@ -38,15 +38,15 @@ const pluginFactory: BuilderComponentPluginFactory = () => { component: BasicLayout, children: [ ${ir.routes - .map( - route => ` + .map( + route => ` { path: '${route.path}', component: ${route.componentName}, } `, - ) - .join(',')} + ) + .join(',')} ], }, ]; diff --git a/packages/code-generator/src/plugins/project/framework/rax/plugins/buildConfig.ts b/packages/code-generator/src/plugins/project/framework/rax/plugins/buildConfig.ts new file mode 100644 index 000000000..2afb2e97d --- /dev/null +++ b/packages/code-generator/src/plugins/project/framework/rax/plugins/buildConfig.ts @@ -0,0 +1,52 @@ +import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; + +import { + BuilderComponentPlugin, + BuilderComponentPluginFactory, + ChunkType, + FileType, + ICodeStruct, + IParseResult, +} from '../../../../../types'; + +const pluginFactory: BuilderComponentPluginFactory = () => { + const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { + const next: ICodeStruct = { + ...pre, + }; + + const ir = next.ir as IParseResult; + const miniAppBuildType = ir.project?.config.miniAppBuildType; + + const buildCfg = { + inlineStyle: false, + plugins: [ + [ + 'build-plugin-rax-app', + { + targets: ['web', 'miniapp'], + miniapp: miniAppBuildType + ? { + buildType: miniAppBuildType, + } + : undefined, + }, + ], + '@ali/build-plugin-rax-app-def', + ], + }; + + next.chunks.push({ + type: ChunkType.STRING, + fileType: FileType.JSON, + name: COMMON_CHUNK_NAME.CustomContent, + content: `${JSON.stringify(buildCfg, null, 2) }\n`, + linkAfter: [], + }); + + return next; + }; + return plugin; +}; + +export default pluginFactory; diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts index b7523b5f9..d83eeccfb 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/abc.json.ts @@ -25,4 +25,3 @@ export default function getFile(): [string[], IResultFile] { return [[], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts index 08c1ab78a..16590134d 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/build.json.ts @@ -29,4 +29,3 @@ export default function getFile(): [string[], IResultFile] { return [[], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts index d8da2daaa..2e82c8266 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/package.json.ts @@ -63,4 +63,3 @@ export default function getFile(): [string[], IResultFile] { return [[], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts index af229a7e0..c0d09de31 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/public/index.html.ts @@ -48,4 +48,3 @@ export default function getFile(): [string[], IResultFile] { return [['public'], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts index 784662e28..b4d86e5e6 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/app.ts.ts @@ -72,6 +72,5 @@ export default { `, ); - return [['src','config'], file]; + return [['src', 'config'], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts index c894e92aa..b08b4593b 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/components.ts.ts @@ -38,6 +38,5 @@ export default componentsMap; `, ); - return [['src','config'], file]; + return [['src', 'config'], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts index 84237ae1d..241186e48 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/config/utils.ts.ts @@ -24,6 +24,5 @@ export default { `, ); - return [['src','config'], file]; + return [['src', 'config'], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts index 979b85a20..8c12c90bf 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/src/index.ts.ts @@ -98,4 +98,3 @@ app.run(); return [['src'], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts b/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts index c30c3e46b..cb75cf556 100644 --- a/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts +++ b/packages/code-generator/src/plugins/project/framework/recore/template/files/tsconfig.json.ts @@ -50,4 +50,3 @@ export default function getFile(): [string[], IResultFile] { return [[], file]; } - \ No newline at end of file diff --git a/packages/code-generator/src/postprocessor/prettier/index.ts b/packages/code-generator/src/postprocessor/prettier/index.ts index 85c2f9741..082a3d022 100644 --- a/packages/code-generator/src/postprocessor/prettier/index.ts +++ b/packages/code-generator/src/postprocessor/prettier/index.ts @@ -7,7 +7,7 @@ const PARSERS = ['css', 'scss', 'less', 'json', 'html', 'vue']; type ProcessorConfig = { customFileTypeParser: Record; -} +}; const factory: PostProcessorFactory = (config?: ProcessorConfig) => { const cfg: ProcessorConfig = { @@ -23,7 +23,7 @@ const factory: PostProcessorFactory = (config?: ProcessorConfig parser = 'typescript'; } else if (PARSERS.indexOf(fileType) >= 0) { parser = fileType as prettier.BuiltInParserName; - } else if (cfg.customFileTypeParser[fileType]){ + } else if (cfg.customFileTypeParser[fileType]) { parser = cfg.customFileTypeParser[fileType] as prettier.BuiltInParserName; } else if (fileType === 'vx') { return mypretter(content, fileType); diff --git a/packages/code-generator/src/publisher/disk/index.ts b/packages/code-generator/src/publisher/disk/index.ts index b63649ab9..bc12ed96f 100644 --- a/packages/code-generator/src/publisher/disk/index.ts +++ b/packages/code-generator/src/publisher/disk/index.ts @@ -19,8 +19,8 @@ export interface IDiskPublisher extends IPublisher { } export const createDiskPublisher: PublisherFactory< - IDiskFactoryParams, - IDiskPublisher +IDiskFactoryParams, +IDiskPublisher > = (params: IDiskFactoryParams = {}): IDiskPublisher => { let { project, outputPath = './' } = params; diff --git a/packages/code-generator/src/publisher/disk/utils.ts b/packages/code-generator/src/publisher/disk/utils.ts index 35fbbc2ad..b07cb83c9 100644 --- a/packages/code-generator/src/publisher/disk/utils.ts +++ b/packages/code-generator/src/publisher/disk/utils.ts @@ -61,7 +61,7 @@ const createDirectory = (pathToDir: string): Promise => { const writeContentToFile = ( filePath: string, fileContent: string, - encoding: string = 'utf8', + encoding = 'utf8', ): Promise => { return new Promise((resolve, reject) => { writeFile(filePath, fileContent, encoding, err => { diff --git a/packages/code-generator/src/publisher/zip/index.ts b/packages/code-generator/src/publisher/zip/index.ts index c400c6849..9e2cbe36f 100644 --- a/packages/code-generator/src/publisher/zip/index.ts +++ b/packages/code-generator/src/publisher/zip/index.ts @@ -5,7 +5,7 @@ import { IPublisherFactoryParams, PublisherError, } from '../../types'; -import { isNodeProcess, writeZipToDisk, generateProjectZip } from './utils' +import { isNodeProcess, writeZipToDisk, generateProjectZip } from './utils'; // export type ZipBuffer = Buffer | Blob; export type ZipBuffer = Buffer; @@ -30,12 +30,12 @@ export const createZipPublisher: PublisherFactory project; const setProject = (projectToSet: IResultDir) => { project = projectToSet; - } + }; const getOutputPath = () => outputPath; const setOutputPath = (path: string) => { outputPath = path; - } + }; const publish = async (options: ZipFactoryParams = {}) => { const projectToPublish = options.project || project; @@ -57,7 +57,7 @@ export const createZipPublisher: PublisherFactory { typeof process.versions === 'object' && typeof process.versions.node !== 'undefined' ); -} +}; export const writeZipToDisk = ( zipFolderPath: string, @@ -27,7 +27,7 @@ export const writeZipToDisk = ( const writeStream = fs.createWriteStream(zipPath); writeStream.write(content); writeStream.end(); -} +}; export const generateProjectZip = async (project: IResultDir): Promise => { let zip = new JSZip(); @@ -35,12 +35,12 @@ export const generateProjectZip = async (project: IResultDir): Promise { const zipFolder = ignoreFolder ? parentFolder : parentFolder.folder(folder.name); if (zipFolder !== null) { @@ -57,4 +57,4 @@ const writeFolderToZip = ( } return parentFolder; -} +}; diff --git a/packages/code-generator/src/types/error.ts b/packages/code-generator/src/types/error.ts index 8c0393a88..7bfdfab69 100644 --- a/packages/code-generator/src/types/error.ts +++ b/packages/code-generator/src/types/error.ts @@ -7,21 +7,12 @@ export class CodeGeneratorError extends Error { // tslint:disable-next-line: max-classes-per-file export class ComponentValidationError extends CodeGeneratorError { - constructor(errorString: string) { - super(errorString); - } } // tslint:disable-next-line: max-classes-per-file export class CompatibilityError extends CodeGeneratorError { - constructor(errorString: string) { - super(errorString); - } } // tslint:disable-next-line: max-classes-per-file export class PublisherError extends CodeGeneratorError { - constructor(errorString: string) { - super(errorString); - } } diff --git a/packages/code-generator/src/types/intermediate.ts b/packages/code-generator/src/types/intermediate.ts index 317ef7843..5a9c2c8b1 100644 --- a/packages/code-generator/src/types/intermediate.ts +++ b/packages/code-generator/src/types/intermediate.ts @@ -4,7 +4,6 @@ import { IContainerNodeItem, IDependency, II18nMap, - IInternalDependency, INpmPackage, IUtilItem, } from './index'; diff --git a/packages/code-generator/src/types/schema.ts b/packages/code-generator/src/types/schema.ts index e102c54cd..44d4e3476 100644 --- a/packages/code-generator/src/types/schema.ts +++ b/packages/code-generator/src/types/schema.ts @@ -227,7 +227,7 @@ export interface IAppConfig { historyMode: 'brower' | 'hash'; // 浏览器路由:brower 哈希路由:hash targetRootID: string; // 渲染根节点 ID layout: IComponentNodeItem; - theme: object; // 主题配置,根据接入的主题模块不同 + theme: Record; // 主题配置,根据接入的主题模块不同 } export interface IAppMeta { diff --git a/packages/code-generator/src/utils/OrderedSet.ts b/packages/code-generator/src/utils/OrderedSet.ts new file mode 100644 index 000000000..cb5393b77 --- /dev/null +++ b/packages/code-generator/src/utils/OrderedSet.ts @@ -0,0 +1,34 @@ +export class OrderedSet { + private _set = new Set(); + + private _arr: T[] = []; + + constructor(items?: T[]) { + if (items) { + this._set = new Set(items); + this._arr = items.slice(0); + } + } + + add(item: T) { + if (!this._set.has(item)) { + this._set.add(item); + this._arr.push(item); + } + } + + delete(item: T) { + if (this._set.has(item)) { + this._set.delete(item); + this._arr.splice(this._arr.indexOf(item), 1); + } + } + + has(item: T) { + return this._set.has(item); + } + + toArray() { + return this._arr.slice(0); + } +} diff --git a/packages/code-generator/src/utils/ScopeBindings.ts b/packages/code-generator/src/utils/ScopeBindings.ts new file mode 100644 index 000000000..3b182ed01 --- /dev/null +++ b/packages/code-generator/src/utils/ScopeBindings.ts @@ -0,0 +1,56 @@ +import { OrderedSet } from './OrderedSet'; + +export interface IScopeBindings { + readonly parent: IScopeBindings | null; + + hasBinding(varName: string): boolean; + hasOwnBinding(varName: string): boolean; + + addBinding(varName: string): void; + removeBinding(varName: string): void; + + getAllBindings(): string[]; + getAllOwnedBindings(): string[]; +} + +export class ScopeBindings implements IScopeBindings { + readonly parent: IScopeBindings | null; + + private _bindings = new OrderedSet(); + + constructor(p: IScopeBindings | null = null) { + this.parent = p; + } + + hasBinding(varName: string): boolean { + return this._bindings.has(varName) || !!this.parent?.hasBinding(varName); + } + + hasOwnBinding(varName: string): boolean { + return this._bindings.has(varName); + } + + addBinding(varName: string): void { + this._bindings.add(varName); + } + + removeBinding(varName: string): void { + this._bindings.delete(varName); + } + + getAllBindings(): string[] { + const allBindings = new OrderedSet(this._bindings.toArray()); + + for (let { parent } = this; parent; parent = parent?.parent) { + parent.getAllOwnedBindings().forEach((varName) => { + allBindings.add(varName); + }); + } + + return allBindings.toArray(); + } + + getAllOwnedBindings(): string[] { + return this._bindings.toArray(); + } +} diff --git a/packages/code-generator/src/utils/common.ts b/packages/code-generator/src/utils/common.ts index 5d4619cdb..fe05ce423 100644 --- a/packages/code-generator/src/utils/common.ts +++ b/packages/code-generator/src/utils/common.ts @@ -24,7 +24,9 @@ export function upperCaseFirst(inputValue: string): string { export function uniqueArray(arr: T[], by: (i: T) => string) { const map: Record = {}; - arr.forEach((item) => (map[by(item)] = item)); + arr.forEach((item) => { + map[by(item)] = item; + }); const uniqueKeys = [...new Set(Object.keys(map))]; const uniqueItems = uniqueKeys.map((key) => map[key]); return uniqueItems; diff --git a/packages/code-generator/src/utils/expressionParser.ts b/packages/code-generator/src/utils/expressionParser.ts new file mode 100644 index 000000000..f47e4cd0f --- /dev/null +++ b/packages/code-generator/src/utils/expressionParser.ts @@ -0,0 +1,243 @@ +import * as parser from '@babel/parser'; +import generate from '@babel/generator'; +import traverse, { NodePath } from '@babel/traverse'; +import * as t from '@babel/types'; +import { isIdentifier, Node } from '@babel/types'; + +import { OrderedSet } from './OrderedSet'; + +export class ParseError extends Error { + constructor(expr: string | t.Expression) { + super(`Failed to parse expression "${typeof expr === 'string' ? expr : generate(expr)}"`); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +const MAYBE_EXPRESSIONS: { + [k in Node['type']]?: { + // fields: Array + fields: string[] | ((node: Node) => string[]); + }; +} = { + ArrayExpression: { fields: ['elements'] }, + AssignmentExpression: { fields: ['left', 'right'] }, + BinaryExpression: { fields: ['left', 'right'] }, + CallExpression: { fields: ['arguments', 'callee'] }, + ConditionalExpression: { fields: ['test', 'consequent', 'alternate'] }, + DoWhileStatement: { fields: ['test'] }, + ExpressionStatement: { fields: ['expression'] }, + ForInStatement: { fields: ['right'] }, + ForStatement: { fields: ['init', 'test', 'update'] }, + IfStatement: { fields: ['test'] }, + LogicalExpression: { fields: ['left', 'right'] }, + MemberExpression: { + fields: (node) => { return (node.type === 'MemberExpression' && node.computed) ? ['object', 'property'] : ['object']; }, + }, + NewExpression: { fields: ['callee', 'arguments'] }, + ObjectMethod: { + fields: (node) => { return (node.type === 'ObjectMethod' && node.computed) ? ['key'] : []; }, + }, + ObjectProperty: { + fields: (node) => { return (node.type === 'ObjectProperty' && node.computed) ? ['key', 'value'] : ['value']; }, + }, + ReturnStatement: { fields: ['argument'] }, + SequenceExpression: { fields: ['expressions'] }, + ParenthesizedExpression: { fields: ['expression'] }, + SwitchCase: { fields: ['test'] }, + SwitchStatement: { fields: ['discriminant'] }, + ThrowStatement: { fields: ['argument'] }, + UnaryExpression: { fields: ['argument'] }, + UpdateExpression: { fields: ['argument'] }, + VariableDeclarator: { fields: ['init'] }, + WhileStatement: { fields: ['test'] }, + WithStatement: { fields: ['object'] }, + AssignmentPattern: { fields: ['right'] }, + ArrowFunctionExpression: { fields: ['body'] }, + ClassExpression: { fields: ['superClass'] }, + ClassDeclaration: { fields: ['superClass'] }, + ExportDefaultDeclaration: { fields: ['declaration'] }, + ForOfStatement: { fields: ['right'] }, + ClassMethod: { fields: (node) => { return (node.type === 'ClassMethod' && node.computed) ? ['key'] : []; } }, + SpreadElement: { fields: ['argument'] }, + TaggedTemplateExpression: { fields: ['tag'] }, + TemplateLiteral: { fields: ['expressions'] }, + YieldExpression: { fields: ['argument'] }, + AwaitExpression: { fields: ['argument'] }, + OptionalMemberExpression: { + fields: (node) => { return (node.type === 'OptionalMemberExpression' && node.computed) ? ['object', 'property'] : ['object']; }, + }, + OptionalCallExpression: { fields: ['callee', 'arguments'] }, + JSXSpreadAttribute: { fields: ['argument'] }, + BindExpression: { fields: ['object', 'callee'] }, + ClassProperty: { fields: (node) => { return (node.type === 'ClassProperty' && node.computed) ? ['key', 'value'] : ['value']; } }, + PipelineTopicExpression: { fields: ['expression'] }, + PipelineBareFunction: { fields: ['callee'] }, + ClassPrivateProperty: { fields: ['value'] }, + Decorator: { fields: ['expression'] }, + TupleExpression: { fields: ['elements'] }, + TSDeclareMethod: { fields: (node) => { return (node.type === 'TSDeclareMethod' && node.computed) ? ['key'] : []; } }, + TSPropertySignature: { + fields: (node) => { return (node.type === 'TSPropertySignature' && node.computed) ? ['key', 'initializer'] : ['initializer']; }, + }, + + TSMethodSignature: { + fields: (node) => { return (node.type === 'TSMethodSignature' && node.computed) ? ['key'] : []; }, + }, + TSAsExpression: { fields: ['expression'] }, + TSTypeAssertion: { fields: ['expression'] }, + TSEnumDeclaration: { fields: ['initializer'] }, + TSEnumMember: { fields: ['initializer'] }, + TSNonNullExpression: { fields: ['expression'] }, + TSExportAssignment: { fields: ['expression'] }, +}; + +export type ParseExpressionGetGlobalVariablesOptions = { filter?: (varName: string) => boolean }; + +const CROSS_THIS_SCOPE_TYPE_NODE: { + [k in Node['type']]?: boolean; +} = { + ArrowFunctionExpression: false, // 箭头函数不跨越 this 的 scope + FunctionExpression: true, + FunctionDeclaration: true, + // FunctionTypeAnnotation: false, // 这是 TS 定义 + // FunctionTypeParam: false, // 这是 TS 定义 + ClassDeclaration: true, + ClassExpression: true, + ClassBody: true, + ClassImplements: true, + ClassMethod: true, + ClassPrivateMethod: true, + ClassProperty: true, + ClassPrivateProperty: true, + DeclareClass: true, +}; + +export function parseExpressionGetGlobalVariables( + expr: string | null | undefined, + { filter = () => true }: ParseExpressionGetGlobalVariablesOptions = {}, +): string[] { + if (!expr) { + return []; + } + + try { + const undeclaredVars = new OrderedSet(); + + const ast = parser.parse(`!(${expr});`); + + const addUndeclaredIdentifierIfNeeded = + (x: Record | null | undefined, path: NodePath) => { + if (isIdentifier(x) && !path.scope.hasBinding(x.name)) { + undeclaredVars.add(x.name); + } + }; + + traverse(ast, { + enter(path) { + const { node } = path; + const expressionFields = MAYBE_EXPRESSIONS[node.type]?.fields; + if (expressionFields) { + (typeof expressionFields === 'function' ? expressionFields(node) : expressionFields).forEach((fieldName) => { + const fieldValue = node[fieldName as keyof typeof node]; + if (typeof fieldValue === 'object') { + if (Array.isArray(fieldValue)) { + fieldValue.forEach((item) => { + addUndeclaredIdentifierIfNeeded(item, path); + }); + } else { + addUndeclaredIdentifierIfNeeded(fieldValue, path); + } + } + }); + } + }, + }); + + return undeclaredVars.toArray().filter(filter); + } catch (e) { + throw new ParseError(expr); + } +} + +export function parseExpressionConvertThis2Context( + expr: string | t.Expression, + contextName = '__$$context', + localVariables: string[] = [], +): string { + if (!expr) { + return expr; + } + + try { + const exprAst = typeof expr === 'string' ? parser.parseExpression(expr) : expr; + const exprWrapAst = t.expressionStatement(exprAst); + const fileAst = t.file(t.program([exprWrapAst])); + + const localVariablesSet = new Set(localVariables); + + let thisScopeLevel = CROSS_THIS_SCOPE_TYPE_NODE[exprAst.type] ? -1 : 0; + traverse(fileAst, { + enter(path) { + if (CROSS_THIS_SCOPE_TYPE_NODE[path.node.type]) { + thisScopeLevel++; + } + }, + exit(path) { + if (CROSS_THIS_SCOPE_TYPE_NODE[path.node.type]) { + thisScopeLevel--; + } + }, + MemberExpression(path) { + if (!path.isMemberExpression()) { + return; + } + + const obj = path.get('object'); + if (!obj.isThisExpression()) { + return; + } + + // 处理局部变量 + if (!path.node.computed) { + const prop = path.get('property'); + if (prop.isIdentifier() && localVariablesSet.has(prop.node.name)) { + path.replaceWith(t.identifier(prop.node.name)); + return; + } + } + + // 替换 this (只在顶层替换) + if (thisScopeLevel <= 0) { + obj.replaceWith(t.identifier(contextName)); + } + }, + ThisExpression(path) { + if (!path.isThisExpression()) { + return; + } + + // MemberExpression 中的 this.xxx 已经处理过了 + if (path.parent.type === 'MemberExpression') { + return; + } + + if (thisScopeLevel <= 0) { + path.replaceWith(t.identifier(contextName)); + } + }, + }); + + const { code } = generate(exprWrapAst.expression, { sourceMaps: false }); + return code; + } catch (e) { + throw new ParseError(expr); + } +} + +export function parseExpression(expr: string) { + try { + return parser.parseExpression(expr); + } catch (e) { + throw new ParseError(expr); + } +} diff --git a/packages/code-generator/src/utils/nodeToJSX.ts b/packages/code-generator/src/utils/nodeToJSX.ts index 39791d7cd..a7edb8903 100644 --- a/packages/code-generator/src/utils/nodeToJSX.ts +++ b/packages/code-generator/src/utils/nodeToJSX.ts @@ -124,7 +124,9 @@ export function generateAttrs(ctx: INodeGeneratorContext, nodeItem: IComponentNo let pieces: CodePiece[] = []; Object.keys(props).forEach( - (propName: string) => (pieces = pieces.concat(generateAttr(ctx, propName, props[propName]))), + (propName: string) => { + pieces = pieces.concat(generateAttr(ctx, propName, props[propName])); + }, ); return pieces; @@ -236,7 +238,7 @@ export function linkPieces(pieces: CodePiece[]): string { .map((p) => p.value) .join(' '); - attrsParts = !!attrsParts ? ` ${attrsParts}` : ''; + attrsParts = attrsParts ? ` ${attrsParts}` : ''; if (childrenParts) { return `${beforeParts}<${tagName}${attrsParts}>${childrenParts}${afterParts}`; diff --git a/packages/code-generator/src/utils/schema.ts b/packages/code-generator/src/utils/schema.ts new file mode 100644 index 000000000..234297579 --- /dev/null +++ b/packages/code-generator/src/utils/schema.ts @@ -0,0 +1,87 @@ +import { + JSSlot, + JSExpression, + NodeData, + NodeSchema, + PropsMap, + isJSExpression, + isJSSlot, + isDOMText, + ContainerSchema, + NpmInfo, +} from '@ali/lowcode-types'; + +export function isContainerSchema(x: any): x is ContainerSchema { + return typeof x === 'object' && x && typeof x.componentName === 'string' && typeof x.fileName === 'string'; +} + +export function isNpmInfo(x: any): x is NpmInfo { + return typeof x === 'object' && x && typeof x.package === 'string'; +} + +// tslint:disable-next-line: no-empty +const noop = () => undefined; + +const handleChildrenDefaultOptions = { + rerun: false, +}; + +export function handleSubNodes( + children: unknown, + handlers: { + string?: (i: string) => T; + expression?: (i: JSExpression) => T; + node?: (i: NodeSchema) => T; + }, + options?: { + rerun?: boolean; + }, +): T[] { + const opt = { + ...handleChildrenDefaultOptions, + ...(options || {}), + }; + + if (Array.isArray(children)) { + const list: NodeData[] = children as NodeData[]; + return list.map( + (child) => handleSubNodes(child, handlers, opt), + ).reduce((p, c) => p.concat(c), []); + } + + let result: T | undefined; + const childrenRes: T[] = []; + if (isDOMText(children)) { + const handler = handlers.string || noop; + result = handler(children as string); + } else if (isJSExpression(children)) { + const handler = handlers.expression || noop; + result = handler(children as JSExpression); + } else { + const handler = handlers.node || noop; + const child = children as NodeSchema; + result = handler(child); + + if (opt.rerun && child.children) { + const childRes = handleSubNodes(child.children, handlers, opt); + childrenRes.push(...childRes); + } + if (child.props) { + // FIXME: currently only support PropsMap + const childProps = child.props as PropsMap; + Object.keys(childProps) + .filter((propName) => isJSSlot(childProps[propName])) + .forEach((propName) => { + const soltVals = (childProps[propName] as JSSlot).value; + const childRes = handleSubNodes(soltVals, handlers, opt); + childrenRes.push(...childRes); + }); + } + } + + if (result !== undefined) { + childrenRes.unshift(result); + } + + return childrenRes; +} diff --git a/packages/code-generator/tools/createTemplate.js b/packages/code-generator/tools/createTemplate.js index b7568f7a9..3b6a0799d 100644 --- a/packages/code-generator/tools/createTemplate.js +++ b/packages/code-generator/tools/createTemplate.js @@ -1,21 +1,21 @@ -const fs = require("fs"); +const fs = require('fs'); const path = require('path'); console.log(process.argv); -let root = ''; +let rootParams = ''; const pathArgIndex = process.argv.indexOf('--path'); if (pathArgIndex >= 0) { - root = process.argv[pathArgIndex + 1]; + rootParams = process.argv[pathArgIndex + 1]; } -if (!root) { +if (!rootParams) { throw new Error('Can\'t find path argument'); } function cloneStr(str, times) { let count = times; const arr = []; - while(count > 0) { + while (count > 0) { arr.push(str); count -= 1; } @@ -23,7 +23,7 @@ function cloneStr(str, times) { } function createTemplateFile(root, internalPath, fileName) { - //不是文件夹,则添加type属性为文件后缀名 + // 不是文件夹,则添加type属性为文件后缀名 const fileTypeSrc = path.extname(fileName); const fileType = fileTypeSrc.substring(1); const baseName = path.basename(fileName, fileTypeSrc); @@ -35,7 +35,7 @@ function createTemplateFile(root, internalPath, fileName) { encoding: 'utf8', }); const pathList = (internalPath.split(path.sep) || []).filter(p => !!p); - const modulePathStr = JSON.stringify(pathList).replace(/\"/g, '\''); + const modulePathStr = JSON.stringify(pathList).replace(/"/g, '\''); const templateContent = ` import ResultFile from '${depPrefix}model/ResultFile'; @@ -62,24 +62,24 @@ ${content} function fileDisplay(root, internalPath) { const dirPath = path.join(root, internalPath); const filesList = fs.readdirSync(dirPath); - for(let i = 0; i < filesList.length; i++){ - //描述此文件/文件夹的对象 + for (let i = 0; i < filesList.length; i++) { + // 描述此文件/文件夹的对象 const fileName = filesList[i]; - //拼接当前文件的路径(上一层路径+当前file的名字) + // 拼接当前文件的路径(上一层路径+当前file的名字) const filePath = path.join(dirPath, fileName); - //根据文件路径获取文件信息,返回一个fs.Stats对象 + // 根据文件路径获取文件信息,返回一个fs.Stats对象 const stats = fs.statSync(filePath); - if(stats.isDirectory()){ - //递归调用 + if (stats.isDirectory()) { + // 递归调用 fileDisplay(root, path.join(internalPath, fileName)); - }else{ + } else { createTemplateFile(root, internalPath, fileName); } } } -//调用函数遍历根目录,同时传递 文件夹路径和对应的数组 -//请使用同步读取 -fileDisplay(root, ''); -//读取完毕则写入到txt文件中 +// 调用函数遍历根目录,同时传递 文件夹路径和对应的数组 +// 请使用同步读取 +fileDisplay(rootParams, ''); +// 读取完毕则写入到txt文件中 // fs.writeFileSync('./data.txt', JSON.stringify(arr)); diff --git a/packages/datasource-engine/.eslintrc.js b/packages/datasource-engine/.eslintrc.js new file mode 100644 index 000000000..dd5b56a53 --- /dev/null +++ b/packages/datasource-engine/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + '@typescript-eslint/no-parameter-properties': 1, + } +} \ No newline at end of file diff --git a/packages/datasource-engine/src/core/DataSourceEngine.ts b/packages/datasource-engine/src/core/DataSourceEngine.ts new file mode 100644 index 000000000..d24ef2420 --- /dev/null +++ b/packages/datasource-engine/src/core/DataSourceEngine.ts @@ -0,0 +1,124 @@ +import { + DataSourceConfig, + DataSourceEngineOptions, + IDataSourceEngine, + IDataSourceEngineFactory, + IRuntimeContext, +} from '../types'; +import { RuntimeDataSource } from './RuntimeDataSource'; + +export class DataSourceEngine implements IDataSourceEngine { + private _dataSourceMap: Record = {}; + + constructor( + private _dataSourceConfig: DataSourceConfig, + private _runtimeContext: IRuntimeContext, + private _options?: DataSourceEngineOptions, + ) { + // eslint-disable-next-line no-unused-expressions + _dataSourceConfig.list?.forEach((ds) => { + // 确保数据源都有处理器 + const requestHandler = ds.requestHandler || _options?.requestHandlersMap?.[ds.type]; + if (!requestHandler) { + throw new Error(`No request handler for "${ds.type}" data source`); + } + + this._dataSourceMap[ds.id] = new RuntimeDataSource( + ds.id, + ds.type, + getValue(ds.options) || {}, + requestHandler.bind(_runtimeContext), + ds.dataHandler ? ds.dataHandler.bind(_runtimeContext) : undefined, + (data) => { + _runtimeContext.setState({ [ds.id]: data }); + }, + ); + }); + } + + public get dataSourceMap() { + return this._dataSourceMap; + } + + public async reloadDataSource() { + try { + const allDataSourceConfigList = this._dataSourceConfig.list || []; + + // urlParams 类型的优先加载 + for (const ds of allDataSourceConfigList) { + if (ds.type === 'urlParams' && (getValue(ds.isInit) ?? true)) { + await this._dataSourceMap[ds.id].load(); + } + } + + // 然后是所有其他的 + const remainDataSourceConfigList = allDataSourceConfigList.filter((x) => x.type !== 'urlParams'); + + // 先发起异步的 + const asyncLoadings: Array> = []; + for (const ds of remainDataSourceConfigList) { + if (getValue(ds.isInit) ?? true) { + const options = getValue(ds.options); + if (options && !options.isSync) { + this._dataSourceMap[ds.id].setOptions(options); + asyncLoadings.push(this._dataSourceMap[ds.id].load(options?.params).catch(() => {})); + } + } + } + + try { + // 再按先后顺序发起同步请求 + for (const ds of remainDataSourceConfigList) { + if (getValue(ds.isInit) ?? true) { + const options = getValue(ds.options); + if (options && options.isSync) { + this._dataSourceMap[ds.id].setOptions(options); + await this._dataSourceMap[ds.id].load(options?.params); + await sleep(0); // TODO: 如何优雅地解决 setState 的异步问题? + } + } + } + } catch (e) { + // ignore error + } + + await Promise.all(asyncLoadings); + } finally { + const allDataHandler = this._dataSourceConfig.dataHandler; + if (allDataHandler) { + await allDataHandler(this._getDataMapOfAll()); + } + } + } + + private _getDataMapOfAll(): Record { + const dataMap: Record = {}; + + Object.entries(this._dataSourceMap).forEach(([dsId, ds]) => { + dataMap[dsId] = ds.data; + }); + + return dataMap; + } +} + +export const create: IDataSourceEngineFactory['create'] = (dataSourceConfig, runtimeContext, options) => { + return new DataSourceEngine(dataSourceConfig, runtimeContext, options); +}; + +function getValue(valueOrValueGetter: T | (() => T)): T; +function getValue(valueOrValueGetter: T | (() => T)): T | undefined { + if (typeof valueOrValueGetter === 'function') { + try { + return valueOrValueGetter(); + } catch (e) { + return undefined; + } + } else { + return valueOrValueGetter; + } +} + +function sleep(ms = 0) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/packages/datasource-engine/src/core/RuntimeDataSource.ts b/packages/datasource-engine/src/core/RuntimeDataSource.ts new file mode 100644 index 000000000..89ec2d930 --- /dev/null +++ b/packages/datasource-engine/src/core/RuntimeDataSource.ts @@ -0,0 +1,94 @@ +import { DataSourceOptions, IRuntimeDataSource, RequestHandler, RuntimeDataSourceStatus } from '../types'; +import { DataSourceResponse } from '../types/DataSourceResponse'; + +export class RuntimeDataSource< + TParams extends Record = Record, + TRequestResult = unknown, + TResultData = unknown +> implements IRuntimeDataSource { + private _status: RuntimeDataSourceStatus = RuntimeDataSourceStatus.Initial; + + private _data?: TResultData; + + private _error?: Error; + + private _latestOptions: DataSourceOptions; + + constructor( + private _id: string, + private _type: string, + private _initialOptions: DataSourceOptions, + private _requestHandler: RequestHandler, DataSourceResponse>, + private _dataHandler: + | (( + data: DataSourceResponse | undefined, + error: unknown | undefined, + ) => TResultData | Promise) + | undefined, + private _onLoaded: (data: TResultData) => void, + ) { + this._latestOptions = _initialOptions; + } + + public get status() { + return this._status; + } + + public get data() { + return this._data; + } + + public get error() { + return this._error; + } + + public async load(params?: TParams): Promise { + try { + this._latestOptions = { + ...this._latestOptions, + params: { + ...this._latestOptions.params, + ...params, + } as TParams, + }; + + this._status = RuntimeDataSourceStatus.Loading; + + const data = await this._request(this._latestOptions); + + this._status = RuntimeDataSourceStatus.Loaded; + + this._onLoaded(data); + + this._data = data; + return data; + } catch (err) { + this._error = err; + this._status = RuntimeDataSourceStatus.Error; + throw err; + } + } + + public setOptions(options: DataSourceOptions) { + this._latestOptions = options; + } + + private async _request(options: DataSourceOptions) { + try { + const response = await this._requestHandler(options); + + const data = this._dataHandler + ? await this._dataHandler(response, undefined) + : ((response.data as unknown) as TResultData); + + return data; + } catch (err) { + if (this._dataHandler) { + const data = await this._dataHandler(undefined, err); + return data; + } + + throw err; + } + } +} diff --git a/packages/datasource-engine/src/types/IRuntimeContext.ts b/packages/datasource-engine/src/types/IRuntimeContext.ts new file mode 100644 index 000000000..a0cc022c9 --- /dev/null +++ b/packages/datasource-engine/src/types/IRuntimeContext.ts @@ -0,0 +1,24 @@ +import { IRuntimeDataSource } from './IRuntimeDataSource'; + +/** 运行时上下文 */ +export interface IRuntimeContext< + TState = Record +> { + /** 当前容器的状态 */ + readonly state: TState; + + /** 设置状态(浅合并) */ + setState(state: Partial): void; + + /** 数据源, key 是数据源的 ID */ + dataSourceMap: Record; + + /** 重新加载所有的数据源 */ + reloadDataSource(): Promise; + + /** 页面容器 */ + readonly page: IRuntimeContext & { props: Record }; + + /** 低代码业务组件容器 */ + readonly component: IRuntimeContext & { props: Record }; +} diff --git a/packages/datasource-engine/src/types/index.ts b/packages/datasource-engine/src/types/index.ts new file mode 100644 index 000000000..ad237a0a0 --- /dev/null +++ b/packages/datasource-engine/src/types/index.ts @@ -0,0 +1 @@ +export * from './DataSourceConfig'; export * from './DataSourceConfigItem'; export * from './DataSourceEngineOptions'; export * from './DataSourceOptions'; export * from './IDataSourceEngine'; export * from './IDataSourceEngineFactory'; export * from './IRuntimeContext'; export * from './IRuntimeDataSource'; export * from './RequestHandler'; export * from './RuntimeDataSourceStatus'; diff --git a/packages/demo-server/.eslintrc.js b/packages/demo-server/.eslintrc.js deleted file mode 100644 index 0ae17cac2..000000000 --- a/packages/demo-server/.eslintrc.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - 'prettier', - 'prettier/@typescript-eslint', - ], - root: true, - env: { - node: true, - jest: true, - }, - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-explicit-any': 'off', - }, -}; diff --git a/packages/demo-server/.prettierrc b/packages/demo-server/.prettierrc deleted file mode 100644 index dcb72794f..000000000 --- a/packages/demo-server/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/packages/demo-server/package.json b/packages/demo-server/package.json index 4a61251f7..5fc5e52da 100644 --- a/packages/demo-server/package.json +++ b/packages/demo-server/package.json @@ -5,8 +5,6 @@ "description": "低代码引擎 DEMO Server 端", "scripts": { "prebuild": "rimraf dist", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "start": "nest start", "start:debug": "nest start --debug --watch", "start:dev": "nest start --watch", @@ -49,13 +47,7 @@ "@types/jest": "25.2.3", "@types/node": "^13.9.1", "@types/supertest": "^2.0.8", - "@typescript-eslint/eslint-plugin": "3.0.2", - "@typescript-eslint/parser": "3.0.2", - "eslint": "7.1.0", - "eslint-config-prettier": "^6.10.0", - "eslint-plugin-import": "^2.20.1", "jest": "26.0.1", - "prettier": "^1.19.1", "supertest": "^4.0.2", "ts-jest": "26.1.0", "ts-loader": "^6.2.1", diff --git a/packages/demo-server/src/api/api.controller.ts b/packages/demo-server/src/api/api.controller.ts index c7a23ec5c..a09b9b162 100644 --- a/packages/demo-server/src/api/api.controller.ts +++ b/packages/demo-server/src/api/api.controller.ts @@ -5,7 +5,11 @@ import { GenerateProjectDto } from '../dto/generate-project.dto'; @Controller('api') export class ApiController { - constructor(private readonly apiService: ApiService) {} + private readonly apiService: ApiService; + + constructor(apiService: ApiService) { + this.apiService = apiService; + } @Get('generate/test') generateTest() { diff --git a/packages/demo-server/src/app.controller.ts b/packages/demo-server/src/app.controller.ts index cce879ee6..0d1d6c9d5 100644 --- a/packages/demo-server/src/app.controller.ts +++ b/packages/demo-server/src/app.controller.ts @@ -3,7 +3,11 @@ import { AppService } from './app.service'; @Controller() export class AppController { - constructor(private readonly appService: AppService) {} + private readonly appService: AppService; + + constructor(appService: AppService) { + this.appService = appService; + } @Get() getHello(): string { diff --git a/packages/demo-server/src/publisher/index.ts b/packages/demo-server/src/publisher/index.ts index 3bd62d4eb..828d7dc01 100644 --- a/packages/demo-server/src/publisher/index.ts +++ b/packages/demo-server/src/publisher/index.ts @@ -1,5 +1,5 @@ import { IResultDir } from '@ali/lowcode-code-generator'; -import { isNodeProcess, writeZipToDisk, generateProjectZip } from './utils' +import { isNodeProcess, writeZipToDisk, generateProjectZip } from './utils'; export type PublisherFactory = (configuration?: Partial) => U; @@ -17,7 +17,7 @@ export interface IPublisherResponse { payload?: T; } -declare type ZipPublisherResponse = string | Buffer | Blob +declare type ZipPublisherResponse = string | Buffer | Blob; export interface ZipFactoryParams extends IPublisherFactoryParams { outputPath?: string @@ -30,41 +30,41 @@ export interface ZipPublisher extends IPublisher = ( - params: ZipFactoryParams = {} + params: ZipFactoryParams = {}, ): ZipPublisher => { - let { project, outputPath } = params + let { project, outputPath } = params; - const getProject = () => project + const getProject = () => project; const setProject = (projectToSet: IResultDir) => { - project = projectToSet - } + project = projectToSet; + }; - const getOutputPath = () => outputPath + const getOutputPath = () => outputPath; const setOutputPath = (path: string) => { - outputPath = path - } + outputPath = path; + }; const publish = async (options: ZipFactoryParams = {}) => { - const projectToPublish = options.project || project + const projectToPublish = options.project || project; if (!projectToPublish) { throw new Error('MissingProject'); } - const zipName = options.projectSlug || params.projectSlug || projectToPublish.name + const zipName = options.projectSlug || params.projectSlug || projectToPublish.name; try { - const zipContent = await generateProjectZip(projectToPublish) + const zipContent = await generateProjectZip(projectToPublish); // If not output path is provided, zip is not written to disk - const projectOutputPath = options.outputPath || outputPath + const projectOutputPath = options.outputPath || outputPath; if (projectOutputPath && isNodeProcess()) { - await writeZipToDisk(projectOutputPath, zipContent, zipName) + await writeZipToDisk(projectOutputPath, zipContent, zipName); } - return { success: true, payload: zipContent } + return { success: true, payload: zipContent }; } catch (error) { throw new Error('ZipUnexpected'); } - } + }; return { publish, @@ -72,5 +72,5 @@ export const createZipPublisher: PublisherFactory { typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node !== 'undefined' - ) -} + ); +}; export const writeZipToDisk = ( zipFolderPath: string, content: Buffer | Blob, - zipName: string + zipName: string, ): void => { - const fs = require('fs') - const path = require('path') + const fs = require('fs'); + const path = require('path'); if (!fs.existsSync(zipFolderPath)) { - fs.mkdirSync(zipFolderPath, { recursive: true }) + fs.mkdirSync(zipFolderPath, { recursive: true }); } - const zipPath = path.join(zipFolderPath, `${zipName}.zip`) + const zipPath = path.join(zipFolderPath, `${zipName}.zip`); - const writeStream = fs.createWriteStream(zipPath) - writeStream.write(content) - writeStream.end() -} + const writeStream = fs.createWriteStream(zipPath); + writeStream.write(content); + writeStream.end(); +}; export const generateProjectZip = async (project: IResultDir): Promise => { - let zip = new JSZip() - zip = writeFolderToZip(project, zip, true) - const zipType = isNodeProcess() ? 'nodebuffer' : 'blob' - return zip.generateAsync({ type: zipType }) -} + let zip = new JSZip(); + zip = writeFolderToZip(project, zip, true); + const zipType = isNodeProcess() ? 'nodebuffer' : 'blob'; + return zip.generateAsync({ type: zipType }); +}; const writeFolderToZip = ( folder: IResultDir, parentFolder: JSZip, - ignoreFolder: boolean = false + ignoreFolder = false, ) => { - const zipFolder = ignoreFolder ? parentFolder : parentFolder.folder(folder.name) + const zipFolder = ignoreFolder ? parentFolder : parentFolder.folder(folder.name); folder.files.forEach((file: IResultFile) => { // const options = file.contentEncoding === 'base64' ? { base64: true } : {} const options = {}; - const fileName = file.ext ? `${file.name}.${file.ext}` : file.name - zipFolder.file(fileName, file.content, options) - }) + const fileName = file.ext ? `${file.name}.${file.ext}` : file.name; + zipFolder.file(fileName, file.content, options); + }); folder.dirs.forEach((subFolder: IResultDir) => { - writeFolderToZip(subFolder, zipFolder) - }) + writeFolderToZip(subFolder, zipFolder); + }); - return parentFolder -} + return parentFolder; +}; diff --git a/packages/demo/src/app/plugins/provider.ts b/packages/demo/src/app/plugins/provider.ts index 54bc61d23..474f7c774 100644 --- a/packages/demo/src/app/plugins/provider.ts +++ b/packages/demo/src/app/plugins/provider.ts @@ -34,7 +34,7 @@ export default class Preview extends ReactProvider { containerId, components: { ...builtInComps, ...buildComponents({ '@alifd/next': 'Next' }, componentsMap) }, componentsMap, - utils: utils, + utils, constants, }; } @@ -44,7 +44,7 @@ export default class Preview extends ReactProvider { const appSchemaStr = localStorage.getItem('lce-dev-store'); const appSchema = JSON.parse(appSchemaStr || ''); const idx = appSchema.componentsTree.findIndex( - (page: any, idx: number) => (page.fileName || `page${idx}`) === pageId, + (page: any, index: number) => (page.fileName || `page${index}`) === pageId, ); const schema = appSchema.componentsTree[idx]; return schema; diff --git a/packages/demo/src/editor/components.ts b/packages/demo/src/editor/components.ts index bf00bd149..bf5302491 100644 --- a/packages/demo/src/editor/components.ts +++ b/packages/demo/src/editor/components.ts @@ -2,7 +2,7 @@ import logo from '@ali/lowcode-plugin-sample-logo'; import samplePreview from '@ali/lowcode-plugin-sample-preview'; import undoRedo from '@ali/lowcode-plugin-undo-redo'; import componentsPane from '@ali/lowcode-plugin-components-pane'; -import outline, { OutlinePane } from '@ali/lowcode-plugin-outline-pane'; +import outline from '@ali/lowcode-plugin-outline-pane'; import zhEn from '@ali/lowcode-plugin-zh-en'; import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog'; import variableBindDialog from '@ali/lowcode-plugin-variable-bind-dialog'; diff --git a/packages/demo/src/editor/config.ts b/packages/demo/src/editor/config.ts index 6a3d83930..7223c6031 100644 --- a/packages/demo/src/editor/config.ts +++ b/packages/demo/src/editor/config.ts @@ -123,9 +123,6 @@ export default { const simulatorUrl = [ 'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.50/react-simulator-renderer.css', 'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.50/react-simulator-renderer.js', - // for debug simulator - // 'http://localhost:3333/js/react-simulator-renderer.js', - ]; editor.set('simulatorUrl', simulatorUrl); // editor.set('renderEnv', 'rax'); diff --git a/packages/demo/src/editor/index.tsx b/packages/demo/src/editor/index.tsx index b68f5d87a..041acd148 100644 --- a/packages/demo/src/editor/index.tsx +++ b/packages/demo/src/editor/index.tsx @@ -1,5 +1,5 @@ import { render } from 'react-dom'; -import GeneralWorkbench, { editor } from '@ali/lowcode-editor-preset-general'; +import GeneralWorkbench from '@ali/lowcode-editor-preset-general'; import config from './config'; import components from './components'; import './global.scss'; diff --git a/packages/demo/src/editor/plugins/codeout.tsx b/packages/demo/src/editor/plugins/codeout.tsx index c2046bd39..0a6777cc8 100644 --- a/packages/demo/src/editor/plugins/codeout.tsx +++ b/packages/demo/src/editor/plugins/codeout.tsx @@ -28,7 +28,7 @@ const Codeout = ({ editor }: PluginProps) => { const designer = editor.get(Designer); if (designer) { const assets = editor.get('assets') as { components: BasicSection[] }; - const components = assets.components; + const { components } = assets; const componentsMap = components .filter((c) => !!c.npm) diff --git a/packages/demo/src/editor/plugins/saveload.tsx b/packages/demo/src/editor/plugins/saveload.tsx index 75532802f..fe9dc817b 100644 --- a/packages/demo/src/editor/plugins/saveload.tsx +++ b/packages/demo/src/editor/plugins/saveload.tsx @@ -22,10 +22,7 @@ interface BasicSection { const Codeout = ({ editor }: PluginProps) => { const handleSaveClick = () => { - - debugger; - - let schema = editor.get('designer').project.getSchema(); + const schema = editor.get('designer').project.getSchema(); console.log(schema); diff --git a/packages/demo/src/vision/module.d.ts b/packages/demo/src/vision/module.d.ts index be53328de..7c7645ba6 100644 --- a/packages/demo/src/vision/module.d.ts +++ b/packages/demo/src/vision/module.d.ts @@ -9,5 +9,5 @@ declare module '@ali/ve-page-history'; declare module '@ali/ve-i18n-manage-pane'; declare module '@ali/ve-action-pane'; declare module '@ali/vu-legao-design-fetch-context'; -declare module "@ali/vu-function-parser"; -declare module "compare-versions"; +declare module '@ali/vu-function-parser'; +declare module 'compare-versions'; diff --git a/packages/designer/.eslintrc.js b/packages/designer/.eslintrc.js new file mode 100644 index 000000000..c6bbe2868 --- /dev/null +++ b/packages/designer/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'react/no-multi-comp': 0, + 'no-unused-expressions': 1, + 'implicit-arrow-linebreak': 1, + 'no-nested-ternary': 1, + 'no-mixed-operators': 1, + '@typescript-eslint/no-parameter-properties': 1, + '@typescript-eslint/ban-types': 1, + 'no-shadow': 1, + 'no-prototype-builtins': 1, + 'no-useless-constructor': 1, + 'no-empty-function': 1, + } +} \ No newline at end of file diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx index f6bd20460..d757e7eb8 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-detecting.tsx @@ -56,10 +56,10 @@ export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> { } @computed get current() { - const host = this.props.host; + const { host } = this.props; const doc = host.document; - const selection = doc.selection; - const current = host.designer.detecting.current; + const { selection } = doc; + const { current } = host.designer.detecting; if (!current || current.document !== doc || selection.has(current.id)) { return null; } @@ -67,8 +67,8 @@ export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> { } render() { - const host = this.props.host; - const current = this.current; + const { host } = this.props; + const { current } = this; if (!current || host.viewport.scrolling || host.liveEditing.editing) { return null; } diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx index a4947ad7d..118f0de63 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx @@ -23,7 +23,7 @@ export default class BoxResizing extends Component<{ host: BuiltinSimulatorHost if (doc.suspensed) { return null; } - const selection = doc.selection; + const { selection } = doc; return this.dragging ? selection.getTopNodes() : selection.getNodes(); } @@ -37,7 +37,7 @@ export default class BoxResizing extends Component<{ host: BuiltinSimulatorHost } render() { - const selecting = this.selecting; + const { selecting } = this; if (!selecting || selecting.length < 1) { // DIRTY FIX, recore has a bug! return ; @@ -79,7 +79,7 @@ export class BoxResizingForNode extends Component<{ host: BuiltinSimulatorHost; render() { const { instances } = this; const { node } = this.props; - const designer = this.host.designer; + const { designer } = this.host; if (!instances || instances.length < 1) { return null; @@ -112,8 +112,11 @@ export class BoxResizingInstance extends Component<{ }> { // private outline: any; private willUnbind: () => any; + private outlineRight: any; + private outlineLeft: any; + private dragEngine: DragResizeEngine; constructor(props: any) { diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx index 7341fc056..ae82e67b1 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx @@ -59,6 +59,7 @@ class Toolbar extends Component<{ observed: OffsetObserver }> { shouldComponentUpdate() { return false; } + render() { const { observed } = this.props; const { height, width } = observed.viewport; @@ -169,7 +170,7 @@ export class BorderSelectingForNode extends Component<{ host: BuiltinSimulatorHo render() { const { instances } = this; const { node } = this.props; - const designer = this.host.designer; + const { designer } = this.host; if (!instances || instances.length < 1) { return null; @@ -206,7 +207,7 @@ export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> { if (doc.suspensed || this.host.liveEditing.editing) { return null; } - const selection = doc.selection; + const { selection } = doc; return this.dragging ? selection.getTopNodes() : selection.getNodes(); } @@ -215,7 +216,7 @@ export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> { } render() { - const selecting = this.selecting; + const { selecting } = this; if (!selecting || selecting.length < 1) { return null; } diff --git a/packages/designer/src/builtin-simulator/bem-tools/drag-resize-engine.ts b/packages/designer/src/builtin-simulator/bem-tools/drag-resize-engine.ts index 895084878..7586bd17f 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/drag-resize-engine.ts +++ b/packages/designer/src/builtin-simulator/bem-tools/drag-resize-engine.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; -import { ISimulatorHost, isSimulatorHost } from '../../simulator'; +import { ISimulatorHost } from '../../simulator'; import { Designer, Point } from '../../designer'; -import { setNativeSelection, cursor } from '@ali/lowcode-utils'; +import { cursor } from '@ali/lowcode-utils'; // import Cursor from './cursor'; // import Pages from './pages'; @@ -19,7 +19,7 @@ function makeEventsHandler( // } docs.add(sourceDoc); // if (sourceDoc !== topDoc || isDragEvent(boostEvent)) { - sensors.forEach((sim) => { + sensors.forEach(sim => { const sdoc = sim.contentDocument; if (sdoc) { docs.add(sdoc); @@ -28,32 +28,21 @@ function makeEventsHandler( // } return (handle: (sdoc: Document) => void) => { - docs.forEach((doc) => handle(doc)); + docs.forEach(doc => handle(doc)); }; } // 拖动缩放 export default class DragResizeEngine { private emitter: EventEmitter; + private dragResizing = false; - constructor(readonly designer: Designer) { + constructor(designer: Designer) { this.designer = designer; this.emitter = new EventEmitter(); } - private getMasterSensors(): ISimulatorHost[] { - return this.designer.project.documents - .map((doc) => { - // TODO: not use actived, - if (doc.actived && doc.simulator?.sensorAvailable) { - return doc.simulator; - } - return null; - }) - .filter(Boolean) as any; - } - isDragResizing() { return this.dragResizing; } @@ -83,14 +72,12 @@ export default class DragResizeEngine { const masterSensors = this.getMasterSensors(); const createResizeEvent = (e: MouseEvent | DragEvent): Point => { - const evt: any = {}; - const sourceDocument = e.view?.document; if (!sourceDocument || sourceDocument === document) { return e; } - const srcSim = masterSensors.find((sim) => sim.contentDocument === sourceDocument); + const srcSim = masterSensors.find(sim => sim.contentDocument === sourceDocument); if (srcSim) { return srcSim.viewport.toGlobalPoint(e); } @@ -99,7 +86,7 @@ export default class DragResizeEngine { const over = (e: MouseEvent) => { const handleEvents = makeEventsHandler(e, masterSensors); - handleEvents((doc) => { + handleEvents(doc => { doc.removeEventListener('mousemove', move, true); doc.removeEventListener('mouseup', over, true); }); @@ -114,7 +101,7 @@ export default class DragResizeEngine { node = boost(e); startEvent = createResizeEvent(e); const handleEvents = makeEventsHandler(e, masterSensors); - handleEvents((doc) => { + handleEvents(doc => { doc.addEventListener('mousemove', move, true); doc.addEventListener('mouseup', over, true); }); @@ -136,7 +123,9 @@ export default class DragResizeEngine { }; } - onResize(func: (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => any) { + onResize( + func: (e: MouseEvent, direction: string, node: any, moveX: number, moveY: number) => any, + ) { this.emitter.on('resize', func); return () => { this.emitter.removeListener('resize', func); @@ -149,6 +138,18 @@ export default class DragResizeEngine { this.emitter.removeListener('resizeEnd', func); }; } + + private getMasterSensors(): ISimulatorHost[] { + return this.designer.project.documents + .map(doc => { + // TODO: not use actived, + if (doc.actived && doc.simulator?.sensorAvailable) { + return doc.simulator; + } + return null; + }) + .filter(Boolean) as any; + } } // new DragResizeEngine(); diff --git a/packages/designer/src/builtin-simulator/bem-tools/index.tsx b/packages/designer/src/builtin-simulator/bem-tools/index.tsx index c22951c2f..c8c3ff909 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/index.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/index.tsx @@ -15,7 +15,7 @@ export class BemTools extends Component<{ host: BuiltinSimulatorHost }> { } render() { - const host = this.props.host; + const { host } = this.props; const { scrollX, scrollY, scale } = host.viewport; return (
diff --git a/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx b/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx index 90b65ecc8..6ed6ef93d 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/insertion.tsx @@ -1,15 +1,14 @@ import { Component } from 'react'; -import { computed, observer } from '@ali/lowcode-editor-core'; -import { SimulatorContext } from '../context'; +import { observer } from '@ali/lowcode-editor-core'; import { BuiltinSimulatorHost } from '../host'; import { DropLocation, Rect, isLocationChildrenDetail, LocationChildrenDetail, - isVertical + isVertical, } from '../../designer'; -import { ISimulatorHost, } from '../../simulator'; +import { ISimulatorHost } from '../../simulator'; import { ParentalNode } from '../../document'; import './insertion.less'; diff --git a/packages/designer/src/builtin-simulator/create-simulator.ts b/packages/designer/src/builtin-simulator/create-simulator.ts index ab20d4fa1..d517879b6 100644 --- a/packages/designer/src/builtin-simulator/create-simulator.ts +++ b/packages/designer/src/builtin-simulator/create-simulator.ts @@ -8,8 +8,8 @@ import { isAssetItem, AssetType, assetItem, -} from '@ali/lowcode-utils'; -import { isCSSUrl } from '@ali/lowcode-utils'; + isCSSUrl } from '@ali/lowcode-utils'; + import { BuiltinSimulatorRenderer } from './renderer'; export function createSimulator( @@ -65,7 +65,7 @@ export function createSimulator( const styleFrags = Object.keys(styles) .map((key) => { - return styles[key].join('\n') + ``; + return `${styles[key].join('\n') }`; }) .join(''); const scriptFrags = Object.keys(scripts) diff --git a/packages/designer/src/builtin-simulator/host-view.tsx b/packages/designer/src/builtin-simulator/host-view.tsx index 4b418ead0..4c3dda73e 100644 --- a/packages/designer/src/builtin-simulator/host-view.tsx +++ b/packages/designer/src/builtin-simulator/host-view.tsx @@ -2,7 +2,6 @@ import { Component } from 'react'; import { observer } from '@ali/lowcode-editor-core'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; import { DocumentModel } from '../document'; -import { SimulatorContext } from './context'; import { BemTools } from './bem-tools'; import './host.less'; @@ -22,25 +21,29 @@ type SimulatorHostProps = BuiltinSimulatorProps & { export class BuiltinSimulatorHostView extends Component { readonly host: BuiltinSimulatorHost; + constructor(props: any) { super(props); const { documentContext } = this.props; this.host = (documentContext.simulator as BuiltinSimulatorHost) || new BuiltinSimulatorHost(documentContext); this.host.setProps(this.props); } + shouldComponentUpdate(nextProps: BuiltinSimulatorProps) { this.host.setProps(nextProps); return false; } + componentDidMount() { if (this.props.onMount) { this.props.onMount(this.host); } } + render() { return (
- {/*progressing.visible ? : null*/} + {/* progressing.visible ? : null */}
); @@ -73,7 +76,7 @@ class Canvas extends Component<{ host: BuiltinSimulatorHost }> { class Content extends Component<{ host: BuiltinSimulatorHost }> { render() { const sim = this.props.host; - const viewport = sim.viewport; + const { viewport } = sim; const frameStyle = { transform: `scale(${viewport.scale})`, height: viewport.contentHeight, diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index 01924b4e6..4679b3338 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -60,6 +60,7 @@ export interface BuiltinSimulatorProps { const defaultSimulatorUrl = (() => { 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/react-simulator-renderer.css`, `${prefix}/js/react-simulator-renderer.js`]; @@ -74,6 +75,7 @@ const defaultSimulatorUrl = (() => { 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`]; @@ -144,18 +146,21 @@ export class BuiltinSimulatorHost implements ISimulatorHost { doc.removeEventListener('mouseup', checkSelect, true); if (!isShaken(downEvent, e)) { - const id = node.id; + const { id } = node; designer.activeTracker.track({ node, instance: nodeInst?.instance }); if (isMulti && !isRootNode(node) && selection.has(id)) { selection.remove(id); @@ -411,12 +421,13 @@ export class BuiltinSimulatorHost implements ISimulatorHost void; + /** * 设置悬停处理 */ setupDetecting() { const doc = this.contentDocument!; - const detecting = this.document.designer.detecting; + const { detecting } = this.document.designer; const hover = (e: MouseEvent) => { if (!detecting.enable) { return; @@ -448,6 +459,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost(); + setInstance(id: string, instances: ComponentInstance[] | null) { if (instances == null) { this.instancesMap.delete(id); @@ -585,14 +596,14 @@ export class BuiltinSimulatorHost implements ISimulatorHost last.r) { last.r = rect.right; - computed = true; + _computed = true; } if (rect.bottom > last.b) { last.b = rect.bottom; - computed = true; + _computed = true; } } if (last) { const r: any = new DOMRect(last.x, last.y, last.r - last.x, last.b - last.y); r.elements = elements; - r.computed = computed; + r.computed = _computed; return r; } @@ -719,10 +730,11 @@ export class BuiltinSimulatorHost implements ISimulatorHost bounds.bottom) { scroll = true; } - }*/ + } */ } else { /* const rect = this.document.computeRect(node); @@ -768,7 +780,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost width ? rect.left > right || rect.right < left : rect.left < left || rect.right > right) { opt.left = Math.min(rect.left + rect.width / 2 + sl - left - width / 2, scrollWidth - width); scroll = true; - }*/ + } */ } if (scroll && this.scroller) { @@ -783,18 +795,21 @@ export class BuiltinSimulatorHost implements ISimulatorHost 1 - ? instances.find((inst) => this.getClosestNodeInstance(inst, container.id)?.instance === containerInstance) + ? instances.find((_inst) => this.getClosestNodeInstance(_inst, container.id)?.instance === containerInstance) : instances[0] : null; const rect = inst ? this.computeComponentInstanceRect(inst, node.componentMeta.rootSelector) : null; @@ -1014,7 +1031,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost(); if (isDragNodeObject(dragObject)) { - const nodes = dragObject.nodes; + const { nodes } = dragObject; let i = nodes.length; let p: any = container; while (i-- > 0) { @@ -1139,7 +1156,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost { matched = rule(target); - return matched ? true : false; + return !!matched; }); if (matched) { propTarget = matched.propTarget; @@ -79,15 +81,15 @@ export class LiveEditing { } } - if (!propTarget) { - // 自动纯文本编辑满足一下情况: - // 1. children 内容都是 Leaf 且都是文本(一期) - // 2. DOM 节点是单层容器,子集都是文本节点 (已满足) - const isAllText = node.children?.every(item => { - return item.isLeaf() && item.getProp('children')?.type === 'literal'; - }); - // TODO: - } + // if (!propTarget) { + // // 自动纯文本编辑满足一下情况: + // // 1. children 内容都是 Leaf 且都是文本(一期) + // // 2. DOM 节点是单层容器,子集都是文本节点 (已满足) + // const isAllText = node.children?.every(item => { + // return item.isLeaf() && item.getProp('children')?.type === 'literal'; + // }); + // // TODO: + // } if (propTarget && setterPropElement) { const prop = node.getProp(propTarget, true)!; @@ -119,8 +121,10 @@ export class LiveEditing { console.info(e.code); switch (e.code) { case 'Enter': + break; // TODO: check is richtext? case 'Escape': + break; case 'Tab': setterPropElement?.blur(); } @@ -128,7 +132,7 @@ export class LiveEditing { // enter // tab }; - const focusout = (e: FocusEvent) => { + const focusout = (/* e: FocusEvent */) => { this.saveAndDispose(); }; setterPropElement.addEventListener('focusout', focusout); @@ -147,8 +151,6 @@ export class LiveEditing { // TODO: process enter | esc events & joint the FocusTracker // TODO: upward testing for b/i/a html elements - - } get editing() { @@ -156,7 +158,9 @@ export class LiveEditing { } private _dispose?: () => void; + private _save?: () => void; + saveAndDispose() { if (this._save) { this._save(); diff --git a/packages/designer/src/builtin-simulator/node-selector/index.tsx b/packages/designer/src/builtin-simulator/node-selector/index.tsx index 554ef78e7..af1f6cb14 100644 --- a/packages/designer/src/builtin-simulator/node-selector/index.tsx +++ b/packages/designer/src/builtin-simulator/node-selector/index.tsx @@ -58,17 +58,20 @@ export default class InstanceNodeSelector extends React.Component (_: any, flag = true) => { if (node && typeof node.hover === 'function') { node.hover(flag); } }; + onMouseOut = (node: Node) => (_: any, flag = false) => { if (node && typeof node.hover === 'function') { node.hover(flag); } }; - renderNodes = (node: Node) => { + + renderNodes = (/* node: Node */) => { const nodes = this.state.parentNodes || []; const children = nodes.map((node, key) => { return ( diff --git a/packages/designer/src/builtin-simulator/resource-consumer.ts b/packages/designer/src/builtin-simulator/resource-consumer.ts index bf25d9068..38a1aa181 100644 --- a/packages/designer/src/builtin-simulator/resource-consumer.ts +++ b/packages/designer/src/builtin-simulator/resource-consumer.ts @@ -21,16 +21,19 @@ export type RendererConsumer = (renderer: BuiltinSimulatorRenderer, data: T) export default class ResourceConsumer { private emitter = new EventEmitter(); + @obx.ref private _data: T | typeof UNSET = UNSET; private _providing?: () => void; + + private _consuming?: () => void; + constructor(provider: () => T, private consumer?: RendererConsumer) { this._providing = autorun(() => { this._data = provider(); }); } - private _consuming?: () => void; consume(consumerOrRenderer: BuiltinSimulatorRenderer | ((data: T) => any)) { if (this._consuming) { return; @@ -72,6 +75,7 @@ export default class ResourceConsumer { } private _firstConsumed = false; + private resovleFirst?: () => void; waitFirstConsume(): Promise { diff --git a/packages/designer/src/builtin-simulator/utils/parse-metadata.ts b/packages/designer/src/builtin-simulator/utils/parse-metadata.ts index 0e0078be6..3bcf9d6d4 100644 --- a/packages/designer/src/builtin-simulator/utils/parse-metadata.ts +++ b/packages/designer/src/builtin-simulator/utils/parse-metadata.ts @@ -16,6 +16,7 @@ export const primitiveTypes = [ 'any', ]; +// eslint-disable-next-line @typescript-eslint/ban-types function makeRequired(propType: any, lowcodeType: string | object) { function lowcodeCheckTypeIsRequired(...rest: any[]) { return propType.isRequired(...rest); @@ -32,6 +33,7 @@ function makeRequired(propType: any, lowcodeType: string | object) { return lowcodeCheckTypeIsRequired; } +// eslint-disable-next-line @typescript-eslint/ban-types function define(propType: any = PropTypes.any, lowcodeType: string | object = {}) { if (!propType._inner && propType.name !== 'lowcodeCheckType') { propType.lowcodeType = lowcodeType; @@ -136,7 +138,7 @@ export function parseProps(component: any): PropConfig[] { Object.keys(propTypes).forEach(key => { const propTypeItem = propTypes[key]; const defaultValue = defaultProps[key]; - const lowcodeType = propTypeItem.lowcodeType; + const { lowcodeType } = propTypeItem; if (lowcodeType) { result[key] = { name: key, diff --git a/packages/designer/src/builtin-simulator/utils/path.ts b/packages/designer/src/builtin-simulator/utils/path.ts index c9a32e353..49f0c71af 100644 --- a/packages/designer/src/builtin-simulator/utils/path.ts +++ b/packages/designer/src/builtin-simulator/utils/path.ts @@ -158,7 +158,7 @@ export function joinPath(...segments: string[]) { if (path === '') { path += seg; } else { - path += '/' + seg; + path += `/${ seg}`; } } } diff --git a/packages/designer/src/builtin-simulator/utils/throttle.ts b/packages/designer/src/builtin-simulator/utils/throttle.ts index c426837d6..9dcf78c01 100644 --- a/packages/designer/src/builtin-simulator/utils/throttle.ts +++ b/packages/designer/src/builtin-simulator/utils/throttle.ts @@ -1,5 +1,6 @@ const useRAF = typeof requestAnimationFrame === 'function'; +// eslint-disable-next-line @typescript-eslint/ban-types export function throttle(func: Function, delay: number) { let lastArgs: any; let lastThis: any; diff --git a/packages/designer/src/builtin-simulator/viewport.ts b/packages/designer/src/builtin-simulator/viewport.ts index 900620377..523933cc4 100644 --- a/packages/designer/src/builtin-simulator/viewport.ts +++ b/packages/designer/src/builtin-simulator/viewport.ts @@ -4,7 +4,9 @@ import { AutoFit, IViewport } from '../simulator'; export default class Viewport implements IViewport { @obx.ref private rect?: DOMRect; + private _bounds?: DOMRect; + get bounds(): DOMRect { if (this._bounds) { return this._bounds; @@ -17,12 +19,13 @@ export default class Viewport implements IViewport { } get contentBounds(): DOMRect { - const bounds = this.bounds; - const scale = this.scale; + const { bounds } = this; + const { scale } = this; return new DOMRect(0, 0, bounds.width / scale, bounds.height / scale); } private viewportElement?: HTMLElement; + mount(viewportElement: HTMLElement | null) { if (!viewportElement || this.viewportElement === viewportElement) { return; @@ -67,7 +70,7 @@ export default class Viewport implements IViewport { } } - @obx.ref private _scale: number = 1; + @obx.ref private _scale = 1; /** * 缩放比例 @@ -87,6 +90,7 @@ export default class Viewport implements IViewport { } @obx.ref private _contentWidth: number | AutoFit = AutoFit; + @obx.ref private _contentHeight: number | AutoFit = AutoFit; @computed get contentHeight(): number | AutoFit { @@ -106,15 +110,19 @@ export default class Viewport implements IViewport { } @obx.ref private _scrollX = 0; + @obx.ref private _scrollY = 0; + get scrollX() { return this._scrollX; } + get scrollY() { return this._scrollY; } private _scrollTarget?: ScrollTarget; + /** * 滚动对象 */ @@ -123,6 +131,7 @@ export default class Viewport implements IViewport { } @obx private _scrolling = false; + get scrolling(): boolean { return this._scrolling; } diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts index f871ba5de..c14133262 100644 --- a/packages/designer/src/component-meta.ts +++ b/packages/designer/src/component-meta.ts @@ -13,7 +13,7 @@ import { FieldConfig, } from '@ali/lowcode-types'; import { computed } from '@ali/lowcode-editor-core'; -import { Node, ParentalNode, TransformStage } from './document'; +import { Node, ParentalNode } from './document'; import { Designer } from './designer'; import { intlNode } from './locale'; import { IconContainer } from './icons/container'; @@ -63,45 +63,62 @@ function buildFilter(rule?: string | string[] | RegExp | NestingFilter) { export class ComponentMeta { readonly isComponentMeta = true; + private _npm?: NpmInfo; + get npm() { return this._npm; } + private _componentName?: string; + get componentName(): string { return this._componentName!; } + private _isContainer?: boolean; + get isContainer(): boolean { return this._isContainer! || this.isRootComponent(); } + private _isModal?: boolean; + get isModal(): boolean { return this._isModal!; } + private _descriptor?: string; + get descriptor(): string | undefined { return this._descriptor; } + private _rootSelector?: string; + get rootSelector(): string | undefined { return this._rootSelector; } + private _transformedMetadata?: TransformedComponentMetadata; + get configure() { const config = this._transformedMetadata?.configure; return config?.combined || config?.props || []; } private _liveTextEditing?: LiveTextEditingConfig[]; + get liveTextEditing() { return this._liveTextEditing; } private parentWhitelist?: NestingFilter | null; + private childWhitelist?: NestingFilter | null; private _title?: TitleContent; + get title(): string | I18nData | ReactElement { // TODO: 标记下。这块需要康师傅加一下API,页面正常渲染。 // string | i18nData | ReactElement @@ -123,6 +140,7 @@ export class ComponentMeta { } private _acceptable?: boolean; + get acceptable(): boolean { return this._acceptable!; } @@ -145,7 +163,7 @@ export class ComponentMeta { // 额外转换逻辑 this._transformedMetadata = this.transformMetadata(metadata); - const title = this._transformedMetadata.title; + const { title } = this._transformedMetadata; if (title) { this._title = typeof title === 'string' @@ -182,8 +200,8 @@ export class ComponentMeta { const { component } = configure; if (component) { - this._isContainer = component.isContainer ? true : false; - this._isModal = component.isModal ? true : false; + this._isContainer = !!component.isContainer; + this._isModal = !!component.isModal; this._descriptor = component.descriptor; this._rootSelector = component.rootSelector; if (component.nestingRule) { @@ -208,11 +226,12 @@ export class ComponentMeta { return result as any; } - isRootComponent(includeBlock: boolean = true) { + isRootComponent(includeBlock = true) { return this.componentName === 'Page' || this.componentName === 'Component' || (includeBlock && this.componentName === 'Block'); } @computed get availableActions() { + // eslint-disable-next-line prefer-const let { disableBehaviors, actions } = this._transformedMetadata?.configure.component || {}; const disabled = ensureAList(disableBehaviors) || (this.isRootComponent(false) ? ['copy', 'remove'] : null); actions = builtinComponentActions.concat(this.designer.getGlobalComponentActions() || [], actions || []); @@ -313,6 +332,7 @@ registerMetadataTransducer((metadata) => { if (!component.nestingRule) { let m; // uri match xx.Group set subcontrolling: true, childWhiteList + // eslint-disable-next-line no-cond-assign if ((m = /^(.+)\.Group$/.exec(componentName))) { // component.subControlling = true; if (!component.nestingRule) { @@ -320,16 +340,16 @@ registerMetadataTransducer((metadata) => { childWhitelist: [`${m[1]}`], }; } - } - // uri match xx.Node set selfControlled: false, parentWhiteList - else if ((m = /^(.+)\.Node$/.exec(componentName))) { + // eslint-disable-next-line no-cond-assign + } else if ((m = /^(.+)\.Node$/.exec(componentName))) { + // uri match xx.Node set selfControlled: false, parentWhiteList // component.selfControlled = false; component.nestingRule = { parentWhitelist: [`${m[1]}`, componentName], }; - } - // uri match .Item .Node .Option set parentWhiteList - else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) { + // eslint-disable-next-line no-cond-assign + } else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) { + // uri match .Item .Node .Option set parentWhiteList component.nestingRule = { parentWhitelist: [`${m[1]}`], }; diff --git a/packages/designer/src/designer/active-tracker.ts b/packages/designer/src/designer/active-tracker.ts index 078969b65..7b9509251 100644 --- a/packages/designer/src/designer/active-tracker.ts +++ b/packages/designer/src/designer/active-tracker.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'events'; import { LocationDetail } from './location'; -import { Node, isNode } from '../document/node/node'; +import { Node, isNode } from '../document/node/node'; import { ComponentInstance } from '../simulator'; import { obx } from '@ali/lowcode-editor-core'; diff --git a/packages/designer/src/designer/builtin-hotkey.ts b/packages/designer/src/designer/builtin-hotkey.ts index 13bea87f4..bc8a698df 100644 --- a/packages/designer/src/designer/builtin-hotkey.ts +++ b/packages/designer/src/designer/builtin-hotkey.ts @@ -107,7 +107,7 @@ hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => { let selected = doc.selection.getTopNodes(true); selected = selected.filter((node) => { return node.canPerformAction('copy'); - }) + }); if (!selected || selected.length < 1) { return; } @@ -242,11 +242,10 @@ hotkey.bind(['option+left', 'option+right'], (e, action) => { parent.insertAfter(firstNode, silbing); } firstNode?.select(); - return; } }); -hotkey.bind(['option+up'], (e, action) => { +hotkey.bind(['option+up'], (e) => { const designer = focusing.focusDesigner; const doc = designer?.currentDocument; if (isFormEvent(e) || !doc) { @@ -275,7 +274,6 @@ hotkey.bind(['option+up'], (e, action) => { parent.insertBefore(firstNode, silbing); } firstNode?.select(); - return; } else { const place = parent.getSuitablePlace(firstNode, null); // upwards if (place) { @@ -285,7 +283,7 @@ hotkey.bind(['option+up'], (e, action) => { } }); -hotkey.bind(['option+down'], (e, action) => { +hotkey.bind(['option+down'], (e) => { const designer = focusing.focusDesigner; const doc = designer?.currentDocument; if (isFormEvent(e) || !doc) { @@ -315,7 +313,6 @@ hotkey.bind(['option+down'], (e, action) => { parent.insertAfter(firstNode, silbing); } firstNode?.select(); - return; } else { const place = parent.getSuitablePlace(firstNode, null); // upwards if (place) { diff --git a/packages/designer/src/designer/clipboard.ts b/packages/designer/src/designer/clipboard.ts index 921117f09..dcaa21c0f 100644 --- a/packages/designer/src/designer/clipboard.ts +++ b/packages/designer/src/designer/clipboard.ts @@ -1,5 +1,5 @@ function getDataFromPasteEvent(event: ClipboardEvent) { - const clipboardData = event.clipboardData; + const { clipboardData } = event; if (!clipboardData) { return null; } @@ -14,7 +14,7 @@ function getDataFromPasteEvent(event: ClipboardEvent) { return data; } else if (data.componentName) { return { - componentsTree: [ data ] + componentsTree: [data], }; } } catch (error) { @@ -34,12 +34,13 @@ function getDataFromPasteEvent(event: ClipboardEvent) { return { code: clipboardData.getData('text/plain'), maps: {}, - };*/ + }; */ } } class Clipboard { private copyPasters: HTMLTextAreaElement[] = []; + private waitFn?: (data: any, e: ClipboardEvent) => void; isCopyPasteEvent(e: Event) { diff --git a/packages/designer/src/designer/designer-view.tsx b/packages/designer/src/designer/designer-view.tsx index fa601a994..6ce25f606 100644 --- a/packages/designer/src/designer/designer-view.tsx +++ b/packages/designer/src/designer/designer-view.tsx @@ -24,7 +24,7 @@ export class DesignerView extends Component !!item).join('-') || parent?.componentMeta?.componentName || ''; + // eslint-disable-next-line no-unused-expressions this.editor?.emit('designer.drag', { time: (endTime - startTime).toFixed(2), selected: nodes @@ -139,6 +144,7 @@ export class Designer { if (!n) { return; } + // eslint-disable-next-line no-shadow const npm = n?.componentMeta?.npm; return ( [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || @@ -173,7 +179,7 @@ export class Designer { } this.postEvent('selection.change', this.currentSelection); if (this.currentSelection) { - const currentSelection = this.currentSelection; + const { currentSelection } = this; selectionDispose = currentSelection.onSelectionChange(() => { this.postEvent('selection.change', currentSelection); }); @@ -187,7 +193,7 @@ export class Designer { } this.postEvent('history.change', this.currentHistory); if (this.currentHistory) { - const currentHistory = this.currentHistory; + const { currentHistory } = this; historyDispose = currentHistory.onStateChange(() => { this.postEvent('history.change', currentHistory); }); @@ -217,6 +223,7 @@ export class Designer { get dropLocation() { return this._dropLocation; } + /** * 创建插入位置,考虑放到 dragon 中 */ @@ -246,6 +253,7 @@ export class Designer { } private oobxList: OffsetObserver[] = []; + createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null { const oobx = createOffsetObserver(nodeInstance); this.clearOobxList(); @@ -302,6 +310,7 @@ export class Designer { } private props?: DesignerProps; + setProps(nextProps: DesignerProps) { const props = this.props ? { ...this.props, ...nextProps } : nextProps; if (this.props) { @@ -380,6 +389,7 @@ export class Designer { } @obx.val private _componentMetasMap = new Map(); + private _lostComponentMetasMap = new Map(); private buildComponentMetasMap(metas: ComponentMetadata[]) { @@ -451,6 +461,7 @@ export class Designer { } private propsReducers = new Map(); + transformProps(props: CompositeObject | PropsList, node: Node, stage: TransformStage) { if (Array.isArray(props)) { // current not support, make this future @@ -464,7 +475,7 @@ export class Designer { return reducers.reduce((xprops, reducer) => { try { - return reducer(xprops, node) + return reducer(xprops, node); } catch (e) { // todo: add log console.warn(e); diff --git a/packages/designer/src/designer/detecting.ts b/packages/designer/src/designer/detecting.ts index 11492f937..273bad503 100644 --- a/packages/designer/src/designer/detecting.ts +++ b/packages/designer/src/designer/detecting.ts @@ -3,18 +3,22 @@ import { Node, DocumentModel } from '../document'; export class Detecting { @obx.ref private _enable = true; + get enable() { return this._enable; } + set enable(flag: boolean) { this._enable = flag; if (!flag) { this._current = null; } } + @obx.ref xRayMode = false; @obx.ref private _current: Node | null = null; + get current() { return this._current; } diff --git a/packages/designer/src/designer/drag-ghost/index.tsx b/packages/designer/src/designer/drag-ghost/index.tsx index d32ee526e..d54dd9bb0 100644 --- a/packages/designer/src/designer/drag-ghost/index.tsx +++ b/packages/designer/src/designer/drag-ghost/index.tsx @@ -9,9 +9,13 @@ type offBinding = () => any; @observer export default class DragGhost extends Component<{ designer: Designer }> { private dispose: offBinding[] = []; + @obx.ref private dragObject: DragObject | null = null; + @obx.ref private x = 0; + @obx.ref private y = 0; + private dragon = this.props.designer.dragon; constructor(props: any) { @@ -48,7 +52,7 @@ export default class DragGhost extends Component<{ designer: Designer }> { } renderGhostGroup() { - const dragObject = this.dragObject; + const { dragObject } = this; if (isDragNodeObject(dragObject)) { return dragObject.nodes.map(node => { const ghost = ( diff --git a/packages/designer/src/designer/dragon.ts b/packages/designer/src/designer/dragon.ts index 60ecef509..e8a696138 100644 --- a/packages/designer/src/designer/dragon.ts +++ b/packages/designer/src/designer/dragon.ts @@ -77,6 +77,7 @@ export interface ISensor { export type DragObject = DragNodeObject | DragNodeDataObject | DragAnyObject; export enum DragObjectType { + // eslint-disable-next-line no-shadow Node = 'node', NodeData = 'nodedata', } @@ -191,19 +192,20 @@ export class Dragon { * current actived sensor, 可用于感应区高亮 */ @obx.ref private _activeSensor: ISensor | undefined; + get activeSensor(): ISensor | undefined { return this._activeSensor; } @obx.ref private _dragging = false; + get dragging(): boolean { return this._dragging; } private emitter = new EventEmitter(); - constructor(readonly designer: Designer) { - } + constructor(readonly designer: Designer) {} /** * Quick listen a shell(container element) drag behavior @@ -238,7 +240,7 @@ export class Dragon { * @param boostEvent 拖拽初始时事件 */ boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent) { - const designer = this.designer; + const { designer } = this; const masterSensors = this.getMasterSensors(); const handleEvents = makeEventsHandler(boostEvent, masterSensors); const newBie = !isDragNodeObject(dragObject); diff --git a/packages/designer/src/designer/index.ts b/packages/designer/src/designer/index.ts index 8bcf028c9..79982c45b 100644 --- a/packages/designer/src/designer/index.ts +++ b/packages/designer/src/designer/index.ts @@ -1,4 +1,5 @@ import './builtin-hotkey'; + export * from './designer'; export * from './designer-view'; export * from './dragon'; diff --git a/packages/designer/src/designer/location.ts b/packages/designer/src/designer/location.ts index f26890357..eec210b35 100644 --- a/packages/designer/src/designer/location.ts +++ b/packages/designer/src/designer/location.ts @@ -127,9 +127,13 @@ export function getWindow(elem: Element | Document): Window { export class DropLocation { readonly target: ParentalNode; + readonly detail: LocationDetail; + readonly event: LocateEvent; + readonly source: string; + get document(): DocumentModel { return this.target.document; } diff --git a/packages/designer/src/designer/offset-observer.ts b/packages/designer/src/designer/offset-observer.ts index 4f72691af..7b4b8bebf 100644 --- a/packages/designer/src/designer/offset-observer.ts +++ b/packages/designer/src/designer/offset-observer.ts @@ -7,14 +7,23 @@ export class OffsetObserver { readonly id = uniqueId('oobx'); private lastOffsetLeft?: number; + private lastOffsetTop?: number; + private lastOffsetHeight?: number; + private lastOffsetWidth?: number; + @obx private _height = 0; + @obx private _width = 0; + @obx private _left = 0; + @obx private _top = 0; + @obx private _right = 0; + @obx private _bottom = 0; @computed get height() { @@ -42,6 +51,7 @@ export class OffsetObserver { } @obx hasOffset = false; + @computed get offsetLeft() { if (this.isRoot) { return this.viewport.scrollX * this.scale; @@ -51,6 +61,7 @@ export class OffsetObserver { } return this.lastOffsetLeft; } + @computed get offsetTop() { if (this.isRoot) { return this.viewport.scrollY * this.scale; @@ -60,12 +71,14 @@ export class OffsetObserver { } return this.lastOffsetTop; } + @computed get offsetHeight() { if (!this.viewport.scrolling || this.lastOffsetHeight == null) { this.lastOffsetHeight = this.isRoot ? this.viewport.height : this.height; } return this.lastOffsetHeight; } + @computed get offsetWidth() { if (!this.viewport.scrolling || this.lastOffsetWidth == null) { this.lastOffsetWidth = this.isRoot ? this.viewport.width : this.width; @@ -78,8 +91,11 @@ export class OffsetObserver { } private pid: number | undefined; + readonly viewport: IViewport; + private isRoot: boolean; + readonly node: Node; readonly compute: () => void; @@ -109,18 +125,17 @@ export class OffsetObserver { if (!rect) { this.hasOffset = false; - } else { - if (!this.viewport.scrolling || !this.hasOffset) { - this._height = rect.height; - this._width = rect.width; - this._left = rect.left; - this._top = rect.top; - this._right = rect.right; - this._bottom = rect.bottom; - this.hasOffset = true; - } + } else if (!this.viewport.scrolling || !this.hasOffset) { + this._height = rect.height; + this._width = rect.width; + this._left = rect.left; + this._top = rect.top; + this._right = rect.right; + this._bottom = rect.bottom; + this.hasOffset = true; } - this.pid = pid = (window as any).requestIdleCallback(compute); + this.pid = (window as any).requestIdleCallback(compute); + pid = this.pid; }; this.compute = compute; @@ -128,7 +143,8 @@ export class OffsetObserver { // try first compute(); // try second, ensure the dom mounted - this.pid = pid = (window as any).requestIdleCallback(compute); + this.pid = (window as any).requestIdleCallback(compute); + pid = this.pid; } purge() { diff --git a/packages/designer/src/designer/scroller.ts b/packages/designer/src/designer/scroller.ts index a7c93b91f..24070b306 100644 --- a/packages/designer/src/designer/scroller.ts +++ b/packages/designer/src/designer/scroller.ts @@ -4,9 +4,11 @@ export class ScrollTarget { get left() { return 'scrollX' in this.target ? this.target.scrollX : this.target.scrollLeft; } + get top() { return 'scrollY' in this.target ? this.target.scrollY : this.target.scrollTop; } + scrollTo(options: { left?: number; top?: number }) { this.target.scrollTo(options); } @@ -24,6 +26,7 @@ export class ScrollTarget { } private doe?: HTMLElement; + constructor(private target: Window | Element) { if (isWindow(target)) { this.doe = target.document.documentElement; @@ -50,6 +53,8 @@ export interface IScrollable { export class Scroller { private pid: number | undefined; + constructor(private scrollable: IScrollable) {} + get scrollTarget(): ScrollTarget | null { let target = this.scrollable.scrollTarget; if (!target) { @@ -62,19 +67,17 @@ export class Scroller { return target; } - constructor(private scrollable: IScrollable) {} - scrollTo(options: { left?: number; top?: number }) { this.cancel(); - const scrollTarget = this.scrollTarget; + const { scrollTarget } = this; if (!scrollTarget) { return; } let pid: number; - const left = scrollTarget.left; - const top = scrollTarget.top; + const { left } = scrollTarget; + const { top } = scrollTarget; const end = () => { this.cancel(); }; @@ -106,20 +109,22 @@ export class Scroller { scrollTarget.scrollTo(opt); if (time < 1) { - this.pid = pid = requestAnimationFrame(animate); + this.pid = requestAnimationFrame(animate); + pid = this.pid; } else { end(); } }; - this.pid = pid = requestAnimationFrame(animate); + this.pid = requestAnimationFrame(animate); + pid = this.pid; } scrolling(point: { globalX: number; globalY: number }) { this.cancel(); const { bounds, scale = 1 } = this.scrollable; - const scrollTarget = this.scrollTarget; + const { scrollTarget } = this; if (!scrollTarget || !bounds) { return; } diff --git a/packages/designer/src/designer/setting/setting-field.ts b/packages/designer/src/designer/setting/setting-field.ts index a70e83bb6..9ed826c06 100644 --- a/packages/designer/src/designer/setting/setting-field.ts +++ b/packages/designer/src/designer/setting/setting-field.ts @@ -19,29 +19,37 @@ function getSettingFieldCollectorKey(parent: SettingEntry, config: FieldConfig) export class SettingField extends SettingPropEntry implements SettingEntry { readonly isSettingField = true; + readonly isRequired: boolean; + readonly transducer: Transducer; + private _config: FieldConfig; + extraProps: FieldExtraProps; // ==== dynamic properties ==== private _title?: TitleContent; + get title() { // FIXME! intl return this._title || (typeof this.name === 'number' ? `项目 ${this.name}` : this.name); } + private _setter?: SetterType | DynamicSetter; + @computed get setter(): SetterType | null { if (!this._setter) { return null; } if (isDynamicSetter(this._setter)) { - return this._setter.call(this,this); + return this._setter.call(this, this); } return this._setter; } @obx.ref private _expanded = true; + get expanded(): boolean { return this._expanded; } @@ -62,7 +70,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry { ...extraProps, }; this.isRequired = config.isRequired || (setter as any)?.isRequired; - this._expanded = extraProps?.defaultCollapsed ? false : true; + this._expanded = !extraProps?.defaultCollapsed; // initial items if (items && items.length > 0) { diff --git a/packages/designer/src/designer/setting/setting-prop-entry.ts b/packages/designer/src/designer/setting/setting-prop-entry.ts index 9c989411d..c219a3091 100644 --- a/packages/designer/src/designer/setting/setting-prop-entry.ts +++ b/packages/designer/src/designer/setting/setting-prop-entry.ts @@ -1,4 +1,4 @@ -import { obx, computed, autorun } from '@ali/lowcode-editor-core'; +import { obx, computed } from '@ali/lowcode-editor-core'; import { IEditor, isJSExpression } from '@ali/lowcode-types'; import { uniqueId } from '@ali/lowcode-utils'; import { SettingEntry } from './setting-entry'; @@ -10,24 +10,36 @@ import { EventEmitter } from 'events'; export class SettingPropEntry implements SettingEntry { // === static properties === readonly editor: IEditor; + readonly isSameComponent: boolean; + readonly isMultiple: boolean; + readonly isSingle: boolean; + readonly nodes: Node[]; + readonly componentMeta: ComponentMeta | null; + readonly designer: Designer; + readonly top: SettingEntry; + readonly isGroup: boolean; + readonly type: 'field' | 'group'; + readonly id = uniqueId('entry'); readonly emitter = new EventEmitter(); // ==== dynamic properties ==== @obx.ref private _name: string | number; + get name() { return this._name; } + @computed get path() { const path = this.parent.path.slice(); if (this.type === 'field') { @@ -133,7 +145,7 @@ export class SettingPropEntry implements SettingEntry { * 获取当前属性值 */ @computed getValue(): any { - let val: any = undefined; + let val: any; if (this.type === 'field') { val = this.parent.getPropValue(this.name); } @@ -271,6 +283,7 @@ export class SettingPropEntry implements SettingEntry { isIgnore() { return false; } + /* getConfig(configName?: K): IPropConfig[K] | IPropConfig { if (configName) { @@ -287,6 +300,7 @@ export class SettingPropEntry implements SettingEntry { } return ''; } + setVariableValue(value: string) { const v = this.getValue(); this.setValue({ @@ -295,6 +309,7 @@ export class SettingPropEntry implements SettingEntry { mock: isJSExpression(v) ? v.mock : v, }); } + setUseVariable(flag: boolean) { if (this.isUseVariable() === flag) { return; @@ -310,12 +325,15 @@ export class SettingPropEntry implements SettingEntry { }); } } + isUseVariable() { return isJSExpression(this.getValue()); } + get useVariable() { return this.isUseVariable(); } + getMockOrValue() { const v = this.getValue(); if (isJSExpression(v)) { diff --git a/packages/designer/src/designer/setting/setting-top-entry.ts b/packages/designer/src/designer/setting/setting-top-entry.ts index 19c4c0fc9..507fc053c 100644 --- a/packages/designer/src/designer/setting/setting-top-entry.ts +++ b/packages/designer/src/designer/setting/setting-top-entry.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events'; import { CustomView, isCustomView, IEditor } from '@ali/lowcode-types'; import { computed } from '@ali/lowcode-editor-core'; import { SettingEntry } from './setting-entry'; -import { SettingField, isSettingField } from './setting-field'; +import { SettingField } from './setting-field'; import { SettingPropEntry } from './setting-prop-entry'; import { Node } from '../../document'; import { ComponentMeta } from '../../component-meta'; @@ -17,12 +17,19 @@ function generateSessionId(nodes: Node[]) { export class SettingTopEntry implements SettingEntry { private emitter = new EventEmitter(); + private _items: Array = []; + private _componentMeta: ComponentMeta | null = null; - private _isSame: boolean = true; + + private _isSame = true; + private _settingFieldMap: { [prop: string]: SettingField } = {}; + readonly path = []; + readonly top = this; + readonly parent = this; get componentMeta() { @@ -55,7 +62,9 @@ export class SettingTopEntry implements SettingEntry { } readonly id: string; + readonly first: Node; + readonly designer: Designer; constructor(readonly editor: IEditor, readonly nodes: Node[]) { @@ -75,7 +84,7 @@ export class SettingTopEntry implements SettingEntry { private setupComponentMeta() { // todo: enhance compile a temp configure.compiled - const first = this.first; + const { first } = this; const meta = first.componentMeta; const l = this.nodes.length; let theSame = true; @@ -100,7 +109,7 @@ export class SettingTopEntry implements SettingEntry { const settingFieldMap: { [prop: string]: SettingField } = {}; const settingFieldCollector = (name: string | number, field: SettingField) => { settingFieldMap[name] = field; - } + }; this._items = this.componentMeta.configure.map((item) => { if (isCustomView(item)) { return item; @@ -209,18 +218,23 @@ export class SettingTopEntry implements SettingEntry { getStatus() { } + setStatus() { } + getChildren() { // this.nodes.map() } + getDOMNode() { } + getId() { return this.id; } + getPage() { return this.first.document; } @@ -231,6 +245,7 @@ export class SettingTopEntry implements SettingEntry { get node() { return this.getNode(); } + getNode() { return this.nodes[0]; } diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 9ec44fd1d..fab7bb49e 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -8,10 +8,8 @@ import { isDragNodeDataObject, DragNodeObject, DragNodeDataObject, DropLocation import { Node, insertChildren, insertChild, isNode, RootNode, ParentalNode } from './node/node'; import { Selection } from './selection'; import { History } from './history'; -import { TransformStage } from './node'; +import { TransformStage, ModalNodesManager } from './node'; import { uniqueId } from '@ali/lowcode-utils'; -import { ModalNodesManager } from './node'; -import { foreachReverse } from '../utils/tree'; export type GetDataType = T extends undefined ? NodeType extends { @@ -35,28 +33,37 @@ export class DocumentModel { * 根节点 类型有:Page/Component/Block */ rootNode: RootNode | null; + /** * 文档编号 */ id: string = uniqueId('doc'); + /** * 选区控制 */ readonly selection: Selection = new Selection(this); + /** * 操作记录控制 */ readonly history: History; + /** * 模态节点管理 */ readonly modalNodesManager: ModalNodesManager; private _nodesMap = new Map(); + @obx.val private nodes = new Set(); + private seqId = 0; + private _simulator?: ISimulatorHost; + private emitter: EventEmitter; + private rootNodeVisitorMap: { [visitorName: string]: any } = {}; /** @@ -84,7 +91,9 @@ export class DocumentModel { } private _modalNode?: ParentalNode; + private _blank?: boolean; + get modalNode() { return this._modalNode; } @@ -94,6 +103,7 @@ export class DocumentModel { } private inited = false; + constructor(readonly project: Project, schema?: RootSchema) { /* // TODO @@ -129,9 +139,11 @@ export class DocumentModel { } @obx.val private willPurgeSpace: Node[] = []; + addWillPurge(node: Node) { this.willPurgeSpace.push(node); } + removeWillPurge(node: Node) { const i = this.willPurgeSpace.indexOf(node); if (i > -1) { @@ -151,8 +163,8 @@ export class DocumentModel { nextId() { let id; do { - id = 'node_' + (this.id.slice(-10) + (++this.seqId).toString(36)).toLocaleLowerCase(); - } while (this.nodesMap.get(id)) + id = `node_${ (this.id.slice(-10) + (++this.seqId).toString(36)).toLocaleLowerCase()}`; + } while (this.nodesMap.get(id)); return id; } @@ -281,6 +293,7 @@ export class DocumentModel { } @obx.ref private _dropLocation: DropLocation | null = null; + /** * 内部方法,请勿调用 */ @@ -362,7 +375,7 @@ export class DocumentModel { * 提供给模拟器的参数 */ @computed get simulatorProps(): object { - let simulatorProps = this.designer.simulatorProps; + let { simulatorProps } = this.designer; if (typeof simulatorProps === 'function') { simulatorProps = simulatorProps(this); } @@ -392,6 +405,7 @@ export class DocumentModel { } @obx.ref private _opened = false; + @obx.ref private _suspensed = false; /** @@ -532,8 +546,8 @@ export class DocumentModel { const data = { componentsMap: this.getComponentsMap(extraComps), componentsTree: [node], - }; - return data; + }; + return data; } getHistory(): History { @@ -582,20 +596,21 @@ export class DocumentModel { acceptRootNodeVisitor( - visitorName: string = 'default', - visitorFn: (node: RootNode) => any ) { - let visitorResult = {}; - if (!visitorName) { - /* tslint:disable no-console */ - console.warn('Invalid or empty RootNodeVisitor name.'); - } - try { - visitorResult = visitorFn.call(this, this.rootNode); - this.rootNodeVisitorMap[visitorName] = visitorResult; - } catch (e) { - console.error('RootNodeVisitor is not valid.'); - } - return visitorResult; + visitorName = 'default', + visitorFn: (node: RootNode) => any, + ) { + let visitorResult = {}; + if (!visitorName) { + /* tslint:disable no-console */ + console.warn('Invalid or empty RootNodeVisitor name.'); + } + try { + visitorResult = visitorFn.call(this, this.rootNode); + this.rootNodeVisitorMap[visitorName] = visitorResult; + } catch (e) { + console.error('RootNodeVisitor is not valid.'); + } + return visitorResult; } getRootNodeVisitor(name: string) { @@ -606,7 +621,7 @@ export class DocumentModel { const componentsMap: ComponentMap[] = []; // 组件去重 const map: any = {}; - for (let node of this._nodesMap.values()) { + for (const node of this._nodesMap.values()) { const { componentName } = node || {}; if (!map[componentName] && node?.componentMeta?.npm?.package) { map[componentName] = true; @@ -657,7 +672,7 @@ export class DocumentModel { /** * @deprecated */ - onRefresh(func: () => void) { + onRefresh(/* func: () => void */) { console.warn('onRefresh method is deprecated'); } } diff --git a/packages/designer/src/document/document-view.tsx b/packages/designer/src/document/document-view.tsx index 2565f34fc..e5018f486 100644 --- a/packages/designer/src/document/document-view.tsx +++ b/packages/designer/src/document/document-view.tsx @@ -9,9 +9,10 @@ export class DocumentView extends Component<{ document: DocumentModel }> { shouldComponentUpdate() { return false; } + render() { const { document } = this.props; - const simulatorProps = document.simulatorProps; + const { simulatorProps } = document; const Simulator = document.designer.simulatorComponent || BuiltinSimulatorHostView; return (
any, private redoer: (data: NodeSchema) => void, private timeGap: number = 1000) { @@ -147,7 +152,7 @@ export class History { * | modified | redoable | undoable | */ getState(): number { - const cursor = this.session.cursor; + const { cursor } = this.session; let state = 7; // undoable ? if (cursor <= 0) { @@ -190,6 +195,7 @@ export class History { class Session { private _data: any; + private activedTimer: any; get data() { diff --git a/packages/designer/src/document/node/exclusive-group.ts b/packages/designer/src/document/node/exclusive-group.ts index cd4b46030..2f5a98cb2 100644 --- a/packages/designer/src/document/node/exclusive-group.ts +++ b/packages/designer/src/document/node/exclusive-group.ts @@ -8,22 +8,29 @@ import { intl } from '../../locale'; // if-else-if assoc conditionGroup value, should be the same level, and siblings, need renderEngine support export class ExclusiveGroup { readonly isExclusiveGroup = true; + readonly id = uniqueId('exclusive'); + @obx.val readonly children: Node[] = []; @obx private visibleIndex = 0; + @computed get document() { return this.visibleNode.document; } + @computed get zLevel() { return this.visibleNode.zLevel; } + @computed get length() { return this.children.length; } + @computed get visibleNode(): Node { return this.children[this.visibleIndex]; } + @computed get firstNode(): Node { return this.children[0]!; } diff --git a/packages/designer/src/document/node/modal-nodes-manager.ts b/packages/designer/src/document/node/modal-nodes-manager.ts index 563637101..b5cb96963 100644 --- a/packages/designer/src/document/node/modal-nodes-manager.ts +++ b/packages/designer/src/document/node/modal-nodes-manager.ts @@ -21,8 +21,11 @@ export class ModalNodesManager { public willDestroy: any; private page: DocumentModel; + private modalNodes: Node[]; + private nodeRemoveEvents: any; + private emitter: EventEmitter; constructor(page: DocumentModel) { @@ -44,8 +47,8 @@ export class ModalNodesManager { public getVisibleModalNode() { const visibleNode = this.modalNodes ? this.modalNodes.find((node: Node) => { - return node.getVisible(); - }) + return node.getVisible(); + }) : null; return visibleNode; } @@ -109,7 +112,7 @@ export class ModalNodesManager { private addNodeEvent(node: Node) { this.nodeRemoveEvents[node.getId()] = - node.onVisibleChange((flag) => { + node.onVisibleChange(() => { this.emitter.emit('visibleChange'); }); } diff --git a/packages/designer/src/document/node/node-children.ts b/packages/designer/src/document/node/node-children.ts index 3f4bc4a27..1a3be57f0 100644 --- a/packages/designer/src/document/node/node-children.ts +++ b/packages/designer/src/document/node/node-children.ts @@ -8,13 +8,13 @@ import { foreachReverse } from '../../utils/tree'; export class NodeChildren { @obx.val private children: Node[]; + private emitter = new EventEmitter(); constructor(readonly owner: ParentalNode, data: NodeData | NodeData[]) { this.children = (Array.isArray(data) ? data : [data]).map(child => { return this.owner.document.createNode(child); }); - } internalInitParent() { @@ -94,6 +94,7 @@ export class NodeChildren { } private purged = false; + /** * 回收销毁 */ @@ -114,6 +115,7 @@ export class NodeChildren { } this.children.splice(i, 1); } + /** * 删除一个节点 */ @@ -128,18 +130,18 @@ export class NodeChildren { node.internalSetParent(null, useMutator); try { node.purge(useMutator); - } catch(err) { + } catch (err) { console.error(err); } } - const document = node.document; + const { document } = node; document.unlinkNode(node); document.selection.remove(node.id); document.destroyNode(node); this.emitter.emit('change'); const i = this.children.indexOf(node); if (useMutator) { - this.reportModified(node, this.owner, {type: 'remove', removeIndex: i, removeNode: node}); + this.reportModified(node, this.owner, { type: 'remove', removeIndex: i, removeNode: node }); } if (i < 0) { return false; @@ -152,7 +154,7 @@ export class NodeChildren { * 插入一个节点,返回新长度 */ insert(node: Node, at?: number | null, useMutator = true): void { - const children = this.children; + const { children } = this; let index = at == null || at === -1 ? children.length : at; const i = children.indexOf(node); @@ -195,7 +197,7 @@ export class NodeChildren { } } if (node.prevSibling && node.nextSibling) { - const conditionGroup = node.prevSibling.conditionGroup; + const { conditionGroup } = node.prevSibling; // insert at condition group if (conditionGroup && conditionGroup === node.nextSibling.conditionGroup) { node.setConditionGroup(conditionGroup); @@ -236,7 +238,7 @@ export class NodeChildren { */ [Symbol.iterator](): { next(): { value: Node } } { let index = 0; - const children = this.children; + const { children } = this; const length = children.length || 0; return { next() { @@ -271,6 +273,7 @@ export class NodeChildren { return fn(child, index); }); } + every(fn: (item: Node, index: number) => any): boolean { return this.children.every((child, index) => fn(child, index)); } diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index 230a7ba89..b2d2f4906 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -73,6 +73,7 @@ import { EventEmitter } from 'events'; */ export class Node { private emitter: EventEmitter; + /** * 是节点实例 */ @@ -94,28 +95,35 @@ export class Node { * * Slot 插槽节点,无 props,正常 children,有 slotArgs,有指令 */ readonly componentName: string; + /** * 属性抽象 */ props: Props; + protected _children?: NodeChildren; + /** * @deprecated */ private _addons: { [key: string]: { exportData: () => any; isProp: boolean; } } = {}; + @obx.ref private _parent: ParentalNode | null = null; + /** * 父级节点 */ get parent(): ParentalNode | null { return this._parent; } + /** * 当前节点子集 */ get children(): NodeChildren | null { return this._children || null; } + /** * 当前节点深度 */ @@ -165,18 +173,20 @@ export class Node { this.setupAutoruns(); } - this.settingEntry = this.document.designer.createSettingEntry([ this ]); + this.settingEntry = this.document.designer.createSettingEntry([this]); this.emitter = new EventEmitter(); } private initProps(props: any): any { return this.document.designer.transformProps(props, this, TransformStage.Init); } + private upgradeProps(props: any): any { return this.document.designer.transformProps(props, this, TransformStage.Upgrade); } private autoruns?: Array<() => void>; + private setupAutoruns() { const autoruns = this.componentMeta.getMetadata().experimental?.autoruns; if (!autoruns || autoruns.length < 1) { @@ -288,6 +298,7 @@ export class Node { } private _slotFor?: Prop | null = null; + internalSetSlotFor(slotFor: Prop | null | undefined) { this._slotFor = slotFor; } @@ -345,6 +356,7 @@ export class Node { } @obx.val _slots: Node[] = []; + @computed hasSlots() { return this._slots.length > 0; } @@ -354,6 +366,7 @@ export class Node { } @obx.ref private _conditionGroup: ExclusiveGroup | null = null; + get conditionGroup(): ExclusiveGroup | null { return this._conditionGroup; } @@ -418,7 +431,7 @@ export class Node { return false; } - wrapWith(schema: Schema) { + wrapWith(/* schema: Schema */) { // todo } @@ -507,7 +520,7 @@ export class Node { * 设置多个属性值,替换原有值 */ setProps(props?: PropsMap | PropsList | Props | null) { - if(props instanceof Props) { + if (props instanceof Props) { this.props = props; return; } @@ -531,7 +544,7 @@ export class Node { if (!this.parent) { return null; } - const index = this.index; + const { index } = this; if (index < 0) { return null; } @@ -545,7 +558,7 @@ export class Node { if (!this.parent) { return null; } - const index = this.index; + const { index } = this; if (index < 1) { return null; } @@ -680,6 +693,7 @@ export class Node { slotNode.internalSetParent(this as ParentalNode, true); this._slots.push(slotNode); } + /** * 当前node对应组件是否已注册可用 */ @@ -700,16 +714,18 @@ export class Node { } private purged = false; + /** * 是否已销毁 */ get isPurged() { return this.purged; } + /** * 销毁 */ - purge(useMutator = true) { + purge() { if (this.purged) { return; } @@ -731,9 +747,11 @@ export class Node { isEmpty(): boolean { return this.children ? this.children.isEmpty() : true; } + getChildren() { return this.children; } + getComponentName() { return this.componentName; } @@ -744,9 +762,11 @@ export class Node { insert(node: Node, ref?: Node, useMutator = true) { this.insertAfter(node, ref, useMutator); } + insertBefore(node: Node, ref?: Node, useMutator = true) { this.children?.insert(node, ref ? ref.index : null, useMutator); } + insertAfter(node: any, ref?: Node, useMutator = true) { if (!isNode(node)) { if (node.getComponentName) { @@ -759,21 +779,27 @@ export class Node { } this.children?.insert(node, ref ? ref.index + 1 : null, useMutator); } + getParent() { return this.parent; } + getId() { return this.id; } + getIndex() { return this.index; } + getNode() { return this; } + getRoot() { return this.document.rootNode; } + getProps() { return this.props; } @@ -802,6 +828,7 @@ export class Node { return this.status; } + /** * @deprecated */ @@ -814,6 +841,7 @@ export class Node { this.status[field] = flag; } } + /** * @deprecated */ @@ -824,6 +852,7 @@ export class Node { } return this.document.simulator?.findDOMNodes(instance)?.[0]; } + /** * @deprecated */ @@ -831,6 +860,7 @@ export class Node { console.warn('getPage is deprecated, use document instead'); return this.document; } + /** * @deprecated */ @@ -843,7 +873,7 @@ export class Node { const canDropIn = c.componentMeta?.prototype?.options?.canDropIn; if (typeof canDropIn === 'function') { return canDropIn(node); - } else if (typeof canDropIn === 'boolean'){ + } else if (typeof canDropIn === 'boolean') { return canDropIn; } return true; @@ -858,7 +888,7 @@ export class Node { if (this.isContainer()) { if (canDropIn === undefined || (typeof canDropIn === 'boolean' && canDropIn) || - (typeof canDropIn === 'function' && canDropIn(node))){ + (typeof canDropIn === 'function' && canDropIn(node))) { return { container: this, ref }; } } @@ -869,6 +899,7 @@ export class Node { return null; } + /** * @deprecated */ @@ -879,10 +910,11 @@ export class Node { } return this.getExtraProp(key)?.getValue(); } + /** * @deprecated */ - registerAddon(key: string, exportData: () => any, isProp: boolean = false) { + registerAddon(key: string, exportData: () => any, isProp = false) { // if (this._addons[key]) { // throw new Error(`node addon ${key} exist`); // } @@ -1034,7 +1066,7 @@ export function insertChildren( let index = at; let node: any; const results: Node[] = []; - // tslint:disable-next-line + // eslint-disable-next-line no-cond-assign while ((node = nodes.pop())) { node = insertChild(container, node, index, copy); results.push(node); diff --git a/packages/designer/src/document/node/props/prop-stash.ts b/packages/designer/src/document/node/props/prop-stash.ts index 1c62617ef..8e92956c7 100644 --- a/packages/designer/src/document/node/props/prop-stash.ts +++ b/packages/designer/src/document/node/props/prop-stash.ts @@ -6,6 +6,7 @@ import { Node } from '../node'; export type PendingItem = Prop[]; export class PropStash implements IPropParent { @obx.val private space: Set = new Set(); + @computed private get maps(): Map { const maps = new Map(); if (this.space.size > 0) { @@ -15,7 +16,9 @@ export class PropStash implements IPropParent { } return maps; } + private willPurge: () => void; + readonly owner: Node; constructor(readonly props: Props, write: (item: Prop) => void) { diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index 5acfcc757..0671bffa9 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -20,6 +20,7 @@ export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | ' export class Prop implements IPropParent { readonly isProp = true; + readonly owner: Node; private stash: PropStash | undefined; @@ -28,12 +29,14 @@ export class Prop implements IPropParent { * 键值 */ @obx key: string | number | undefined; + /** * 扩展值 */ @obx spread: boolean; readonly props: Props; + readonly options: any; constructor( @@ -77,6 +80,7 @@ export class Prop implements IPropParent { readonly id = uniqueId('prop$'); @obx.ref private _type: ValueTypes = 'unset'; + /** * 属性类型 */ @@ -162,6 +166,7 @@ export class Prop implements IPropParent { } private _code: string | null = null; + /** * 获得表达式值 */ @@ -272,9 +277,11 @@ export class Prop implements IPropParent { } private _slotNode?: SlotNode; + get slotNode() { return this._slotNode; } + setAsSlot(data: JSSlot) { this._type = 'slot'; const slotSchema: SlotSchema = { @@ -287,7 +294,7 @@ export class Prop implements IPropParent { if (this._slotNode) { this._slotNode.import(slotSchema); } else { - const owner = this.props.owner; + const { owner } = this.props; this._slotNode = owner.document.createNode(slotSchema); owner.addSlot(this._slotNode); this._slotNode.internalSetSlotFor(this); @@ -336,7 +343,9 @@ export class Prop implements IPropParent { } @obx.val private _items: Prop[] | null = null; + @obx.val private _maps: Map | null = null; + @computed private get items(): Prop[] | null { let _items: any; untracked(() => { @@ -371,6 +380,7 @@ export class Prop implements IPropParent { } return _items; } + @computed private get maps(): Map | null { if (!this.items) { return null; @@ -382,7 +392,7 @@ export class Prop implements IPropParent { * 获取某个属性 * @param stash 如果不存在,临时获取一个待写入 */ - get(path: string | number, stash: boolean = true): Prop | null { + get(path: string | number, stash = true): Prop | null { const type = this._type; if (type !== 'map' && type !== 'list' && type !== 'unset' && !stash) { return null; @@ -524,7 +534,7 @@ export class Prop implements IPropParent { } items[key] = prop; } else if (this.maps) { - const maps = this.maps; + const { maps } = this; const orig = maps.get(key); if (orig) { // replace @@ -559,6 +569,7 @@ export class Prop implements IPropParent { } private purged = false; + /** * 回收销毁 */ @@ -584,7 +595,7 @@ export class Prop implements IPropParent { */ [Symbol.iterator](): { next(): { value: Prop } } { let index = 0; - const items = this.items; + const { items } = this; const length = items?.length || 0; return { next() { @@ -606,7 +617,7 @@ export class Prop implements IPropParent { * 遍历 */ forEach(fn: (item: Prop, key: number | string | undefined) => void): void { - const items = this.items; + const { items } = this; if (!items) { return; } @@ -620,7 +631,7 @@ export class Prop implements IPropParent { * 遍历 */ map(fn: (item: Prop, key: number | string | undefined) => T): T[] | null { - const items = this.items; + const { items } = this; if (!items) { return null; } diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index e9c73ccd3..fa73e0190 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -23,7 +23,9 @@ export function getOriginalExtraKey(key: string): string { export class Props implements IPropParent { readonly id = uniqueId('props'); + @obx.val private items: Prop[] = []; + @computed private get maps(): Map { const maps = new Map(); if (this.items.length > 0) { @@ -151,41 +153,41 @@ export class Props implements IPropParent { query(path: string, stash = true): Prop | null { return this.get(path, stash); // todo: future support list search - let matchedLength = 0; - let firstMatched = null; - if (this.items) { - // target: a.b.c - // trys: a.b.c, a.b, a - let i = this.items.length; - while (i-- > 0) { - const expr = this.items[i]; - if (!expr.key) { - continue; - } - const name = String(expr.key); - if (name === path) { - // completely match - return expr; - } + // let matchedLength = 0; + // let firstMatched = null; + // if (this.items) { + // // target: a.b.c + // // trys: a.b.c, a.b, a + // let i = this.items.length; + // while (i-- > 0) { + // const expr = this.items[i]; + // if (!expr.key) { + // continue; + // } + // const name = String(expr.key); + // if (name === path) { + // // completely match + // return expr; + // } - // fisrt match - const l = name.length; - if (path.slice(0, l + 1) === `${name}.`) { - matchedLength = l; - firstMatched = expr; - } - } - } + // // fisrt match + // const l = name.length; + // if (path.slice(0, l + 1) === `${name}.`) { + // matchedLength = l; + // firstMatched = expr; + // } + // } + // } - let ret = null; - if (firstMatched) { - ret = firstMatched.get(path.slice(matchedLength + 1), true); - } - if (!ret && stash) { - return this.stash.get(path); - } + // let ret = null; + // if (firstMatched) { + // ret = firstMatched.get(path.slice(matchedLength + 1), true); + // } + // if (!ret && stash) { + // return this.stash.get(path); + // } - return ret; + // return ret; } /** @@ -257,7 +259,7 @@ export class Props implements IPropParent { */ [Symbol.iterator](): { next(): { value: Prop } } { let index = 0; - const items = this.items; + const { items } = this; const length = items.length || 0; return { next() { @@ -300,6 +302,7 @@ export class Props implements IPropParent { } private purged = false; + /** * 回收销毁 */ diff --git a/packages/designer/src/document/node/props/value-to-source.ts b/packages/designer/src/document/node/props/value-to-source.ts index 40cbecd61..77f5db856 100644 --- a/packages/designer/src/document/node/props/value-to-source.ts +++ b/packages/designer/src/document/node/props/value-to-source.ts @@ -4,6 +4,7 @@ function propertyNameRequiresQuotes(propertyName: string) { worksWithoutQuotes: false, }; + // eslint-disable-next-line no-new-func new Function('ctx', `ctx.worksWithoutQuotes = {${propertyName}: true}['${propertyName}']`)(); return !context.worksWithoutQuotes; @@ -57,15 +58,15 @@ export function valueToSource( if (value instanceof Map) { return value.size ? `${indentString.repeat(indentLevel)}new Map(${valueToSource([...value], { - circularReferenceToken, - doubleQuote, - includeFunctions, - includeUndefinedProperties, - indentLevel, - indentString, - lineEnding, - visitedObjects: new Set([value, ...visitedObjects]), - }).substr(indentLevel * indentString.length)})` + circularReferenceToken, + doubleQuote, + includeFunctions, + includeUndefinedProperties, + indentLevel, + indentString, + lineEnding, + visitedObjects: new Set([value, ...visitedObjects]), + }).substr(indentLevel * indentString.length)})` : `${indentString.repeat(indentLevel)}new Map()`; } @@ -76,15 +77,15 @@ export function valueToSource( if (value instanceof Set) { return value.size ? `${indentString.repeat(indentLevel)}new Set(${valueToSource([...value], { - circularReferenceToken, - doubleQuote, - includeFunctions, - includeUndefinedProperties, - indentLevel, - indentString, - lineEnding, - visitedObjects: new Set([value, ...visitedObjects]), - }).substr(indentLevel * indentString.length)})` + circularReferenceToken, + doubleQuote, + includeFunctions, + includeUndefinedProperties, + indentLevel, + indentString, + lineEnding, + visitedObjects: new Set([value, ...visitedObjects]), + }).substr(indentLevel * indentString.length)})` : `${indentString.repeat(indentLevel)}new Set()`; } @@ -94,8 +95,7 @@ export function valueToSource( } const itemsStayOnTheSameLine = value.every( - item => - typeof item === 'object' && + item => typeof item === 'object' && item && !(item instanceof Date) && !(item instanceof Map) && @@ -140,33 +140,33 @@ export function valueToSource( return itemsStayOnTheSameLine ? `${indentString.repeat(indentLevel)}[${value.join(', ')}]` : `${indentString.repeat(indentLevel)}[${lineEnding}${value.join( - `,${lineEnding}`, - )}${lineEnding}${indentString.repeat(indentLevel)}]`; + `,${lineEnding}`, + )}${lineEnding}${indentString.repeat(indentLevel)}]`; } value = Object.keys(value).reduce((entries, propertyName) => { - const propertyValue = value[propertyName], - propertyValueString = + const propertyValue = value[propertyName]; + const propertyValueString = typeof propertyValue !== 'undefined' || includeUndefinedProperties ? valueToSource(value[propertyName], { - circularReferenceToken, - doubleQuote, - includeFunctions, - includeUndefinedProperties, - indentLevel: indentLevel + 1, - indentString, - lineEnding, - visitedObjects: new Set([value, ...visitedObjects]), - }) + circularReferenceToken, + doubleQuote, + includeFunctions, + includeUndefinedProperties, + indentLevel: indentLevel + 1, + indentString, + lineEnding, + visitedObjects: new Set([value, ...visitedObjects]), + }) : null; if (propertyValueString) { const quotedPropertyName = propertyNameRequiresQuotes(propertyName) - ? quoteString(propertyName, { - doubleQuote, - }) - : propertyName, - trimmedPropertyValueString = propertyValueString.substr((indentLevel + 1) * indentString.length); + ? quoteString(propertyName, { + doubleQuote, + }) + : propertyName; + const trimmedPropertyValueString = propertyValueString.substr((indentLevel + 1) * indentString.length); if (typeof propertyValue === 'function' && trimmedPropertyValueString.startsWith(`${propertyName}()`)) { entries.push( @@ -184,8 +184,8 @@ export function valueToSource( return value.length ? `${indentString.repeat(indentLevel)}{${lineEnding}${value.join( - `,${lineEnding}`, - )}${lineEnding}${indentString.repeat(indentLevel)}}` + `,${lineEnding}`, + )}${lineEnding}${indentString.repeat(indentLevel)}}` : `${indentString.repeat(indentLevel)}{}`; case 'string': return `${indentString.repeat(indentLevel)}${quoteString(value, { @@ -226,7 +226,9 @@ export function getSource(value: any): string { if (value) { try { value.__source = source; - } catch (ex) {} + } catch (ex) { + console.error(ex); + } } return source; } diff --git a/packages/designer/src/document/selection.ts b/packages/designer/src/document/selection.ts index f1741b48b..c62f4be08 100644 --- a/packages/designer/src/document/selection.ts +++ b/packages/designer/src/document/selection.ts @@ -5,7 +5,11 @@ import { DocumentModel } from './document-model'; export class Selection { private emitter = new EventEmitter(); + @obx.val private _selected: string[] = []; + + constructor(readonly doc: DocumentModel) {} + /** * 选中的节点 id */ @@ -13,8 +17,6 @@ export class Selection { return this._selected; } - constructor(readonly doc: DocumentModel) {} - /** * 选中 */ diff --git a/packages/designer/src/icons/clone.tsx b/packages/designer/src/icons/clone.tsx index 844b3f5b0..baf6342cc 100644 --- a/packages/designer/src/icons/clone.tsx +++ b/packages/designer/src/icons/clone.tsx @@ -1,9 +1,9 @@ -import { SVGIcon, IconProps } from "@ali/lowcode-utils"; +import { SVGIcon, IconProps } from '@ali/lowcode-utils'; export function IconClone(props: IconProps) { return ( - + ); } diff --git a/packages/designer/src/icons/component.tsx b/packages/designer/src/icons/component.tsx index 78a3ba1f0..6804ca16b 100644 --- a/packages/designer/src/icons/component.tsx +++ b/packages/designer/src/icons/component.tsx @@ -1,4 +1,4 @@ -import { SVGIcon, IconProps } from "@ali/lowcode-utils"; +import { SVGIcon, IconProps } from '@ali/lowcode-utils'; export function IconComponent(props: IconProps) { return ( diff --git a/packages/designer/src/icons/container.tsx b/packages/designer/src/icons/container.tsx index 5c46b3f18..b4ccfcd73 100644 --- a/packages/designer/src/icons/container.tsx +++ b/packages/designer/src/icons/container.tsx @@ -1,4 +1,4 @@ -import { SVGIcon, IconProps } from "@ali/lowcode-utils"; +import { SVGIcon, IconProps } from '@ali/lowcode-utils'; export function IconContainer(props: IconProps) { return ( diff --git a/packages/designer/src/icons/hidden.tsx b/packages/designer/src/icons/hidden.tsx index 0f944cadf..081d64e08 100644 --- a/packages/designer/src/icons/hidden.tsx +++ b/packages/designer/src/icons/hidden.tsx @@ -1,4 +1,4 @@ -import { SVGIcon, IconProps } from "@ali/lowcode-utils"; +import { SVGIcon, IconProps } from '@ali/lowcode-utils'; export function IconHidden(props: IconProps) { return ( diff --git a/packages/designer/src/icons/page.tsx b/packages/designer/src/icons/page.tsx index a957239fe..6a8780099 100644 --- a/packages/designer/src/icons/page.tsx +++ b/packages/designer/src/icons/page.tsx @@ -1,4 +1,4 @@ -import { SVGIcon, IconProps } from "@ali/lowcode-utils"; +import { SVGIcon, IconProps } from '@ali/lowcode-utils'; export function IconPage(props: IconProps) { return ( diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index b4148ebff..fa2024525 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -6,6 +6,7 @@ import { ProjectSchema, RootSchema } from '@ali/lowcode-types'; export class Project { private emitter = new EventEmitter(); + @obx.val readonly documents: DocumentModel[] = []; private data: ProjectSchema = { version: '1.0.0', componentsMap: [], componentsTree: [] }; @@ -35,10 +36,10 @@ export class Project { /** * 替换当前document的schema,并触发渲染器的render - * @param schema + * @param schema */ - setSchema(schema?: ProjectSchema){ - let doc = this.documents.find((doc) => doc.actived); + setSchema(schema?: ProjectSchema) { + const doc = this.documents.find((doc) => doc.actived); doc && doc.import(schema?.componentsTree[0]); } @@ -90,16 +91,18 @@ export class Project { * 分字段设置储存数据,不记录操作记录 */ set( + // eslint-disable-next-line @typescript-eslint/no-unused-vars key: - | 'version' - | 'componentsTree' - | 'componentsMap' - | 'utils' - | 'constants' - | 'i18n' - | 'css' - | 'dataSource' - | string, + | 'version' + | 'componentsTree' + | 'componentsMap' + | 'utils' + | 'constants' + | 'i18n' + | 'css' + | 'dataSource' + | string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars value: any, ): void {} @@ -107,16 +110,17 @@ export class Project { * 分字段设置储存数据 */ get( + // eslint-disable-next-line @typescript-eslint/no-unused-vars key: - | 'version' - | 'componentsTree' - | 'componentsMap' - | 'utils' - | 'constants' - | 'i18n' - | 'css' - | 'dataSource' - | string, + | 'version' + | 'componentsTree' + | 'componentsMap' + | 'utils' + | 'constants' + | 'i18n' + | 'css' + | 'dataSource' + | string, ): any {} open(doc?: string | DocumentModel | RootSchema): DocumentModel { diff --git a/packages/designer/src/utils/tree.ts b/packages/designer/src/utils/tree.ts index 2cd8ff515..d850bb204 100644 --- a/packages/designer/src/utils/tree.ts +++ b/packages/designer/src/utils/tree.ts @@ -4,4 +4,4 @@ export function foreachReverse(arr: NodeChildren, fn: Function, context: any = { for (let i = arr.length - 1; i >= 0; i--) { fn.call(context, arr.get(i)); } -} \ No newline at end of file +} diff --git a/packages/editor-core/.eslintrc.js b/packages/editor-core/.eslintrc.js new file mode 100644 index 000000000..c4749b311 --- /dev/null +++ b/packages/editor-core/.eslintrc.js @@ -0,0 +1,14 @@ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'react/no-multi-comp': 1, + 'no-unused-expressions': 1, + 'implicit-arrow-linebreak': 1, + 'no-nested-ternary': 1, + 'no-mixed-operators': 1, + '@typescript-eslint/no-parameter-properties': 1, + '@typescript-eslint/ban-types': 1, + 'no-shadow': 1, + 'no-prototype-builtins': 1, + } +} \ No newline at end of file diff --git a/packages/editor-core/build.plugin.js b/packages/editor-core/build.plugin.js index 980daf4f1..d24fe23ae 100644 --- a/packages/editor-core/build.plugin.js +++ b/packages/editor-core/build.plugin.js @@ -1,12 +1,11 @@ const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); module.exports = ({ onGetWebpackConfig }) => { onGetWebpackConfig((config) => { config.resolve .plugin('tsconfigpaths') .use(TsconfigPathsPlugin, [{ - configFile: "./tsconfig.json" + configFile: './tsconfig.json', }]); }); }; diff --git a/packages/editor-core/src/di/ioc-context.ts b/packages/editor-core/src/di/ioc-context.ts index 9be8707e6..27b854cab 100644 --- a/packages/editor-core/src/di/ioc-context.ts +++ b/packages/editor-core/src/di/ioc-context.ts @@ -1,4 +1,5 @@ import { IocContext } from 'power-di'; + export * from 'power-di'; export const globalContext = IocContext.DefaultInstance; diff --git a/packages/editor-core/src/di/setter.ts b/packages/editor-core/src/di/setter.ts index 428b0a7ea..b4d771f01 100644 --- a/packages/editor-core/src/di/setter.ts +++ b/packages/editor-core/src/di/setter.ts @@ -52,7 +52,7 @@ export function registerSetter( function getInitialFromSetter(setter: any) { return setter && ( - setter.initial || setter.Initial + setter.initial || setter.Initial || (setter.type && (setter.type.initial || setter.type.Initial)) ) || null; // eslint-disable-line } diff --git a/packages/editor-core/src/editor.ts b/packages/editor-core/src/editor.ts index 6f8c22dc0..117b5c14d 100644 --- a/packages/editor-core/src/editor.ts +++ b/packages/editor-core/src/editor.ts @@ -11,7 +11,7 @@ import { import { IocContext, RegisterOptions } from './di'; import { globalLocale } from './intl'; import * as utils from './utils'; -import { tipHandler } from './widgets/tip/tip-handler'; +// import { tipHandler } from './widgets/tip/tip-handler'; EventEmitter.defaultMaxListeners = 100; @@ -22,7 +22,7 @@ export class Editor extends EventEmitter implements IEditor { * Ioc Container */ private context = new IocContext({ - notFoundHandler: (type: KeyType) => NOT_FOUND, + notFoundHandler: (/* type: KeyType */) => NOT_FOUND, }); get locale() { @@ -86,11 +86,13 @@ export class Editor extends EventEmitter implements IEditor { } config?: EditorConfig; + components?: PluginClassSet; + async init(config?: EditorConfig, components?: PluginClassSet): Promise { this.config = config || {}; this.components = components || {}; - const { shortCuts = [], hooks = [], lifeCycles } = this.config; + const { hooks = [], lifeCycles } = this.config; this.emit('editor.beforeInit'); const init = (lifeCycles && lifeCycles.init) || ((): void => {}); @@ -114,7 +116,7 @@ export class Editor extends EventEmitter implements IEditor { return; } try { - const { shortCuts = [], lifeCycles = {} } = this.config; + const { lifeCycles = {} } = this.config; // unRegistShortCuts(shortCuts); this.unregisterHooks(); @@ -152,12 +154,13 @@ export class Editor extends EventEmitter implements IEditor { }; private waits = new Map< - KeyType, - Array<{ - once?: boolean; - resolve: (data: any) => void; - }> + KeyType, + Array<{ + once?: boolean; + resolve:(data: any) => void; + }> >(); + private notifyGot(key: KeyType) { let waits = this.waits.get(key); if (!waits) { diff --git a/packages/editor-core/src/hotkey.ts b/packages/editor-core/src/hotkey.ts index f991aaeab..c4a25ff05 100644 --- a/packages/editor-core/src/hotkey.ts +++ b/packages/editor-core/src/hotkey.ts @@ -124,7 +124,7 @@ let REVERSE_MAP: CtrlKeyMap; * programatically */ for (let i = 1; i < 20; ++i) { - MAP[111 + i] = 'f' + i; + MAP[111 + i] = `f${ i}`; } /** @@ -346,22 +346,28 @@ function fireCallback(callback: HotkeyCallback, e: KeyboardEvent, combo?: string sequence, selected, }); - } catch(err) { + } catch (err) { console.error(err.message); } } export class Hotkey { private callBacks: HotkeyCallbacks = {}; + private directMap: HotkeyDirectMap = {}; + private sequenceLevels: SequenceLevels = {}; + private resetTimer = 0; + private ignoreNextKeyup: boolean | string = false; + private ignoreNextKeypress = false; + private nextExpectedAction: boolean | string = false; mount(window: Window) { - const document = window.document; + const { document } = window; const handleKeyEvent = this.handleKeyEvent.bind(this); document.addEventListener('keypress', handleKeyEvent, false); document.addEventListener('keydown', handleKeyEvent, false); @@ -582,7 +588,6 @@ export class Hotkey { combination = combination.replace(/\s+/g, ' '); const sequence: string[] = combination.split(' '); - let info: KeyInfo; // if this pattern is a sequence of keys then run through this method // to reprocess each pattern one key at a time @@ -591,7 +596,7 @@ export class Hotkey { return; } - info = getKeyInfo(combination, action); + const info: KeyInfo = getKeyInfo(combination, action); // make sure to initialize array if this is the first time // a callback is added for this key diff --git a/packages/editor-core/src/intl/ali-global-locale.ts b/packages/editor-core/src/intl/ali-global-locale.ts index 1f74d719b..a8ec7911a 100644 --- a/packages/editor-core/src/intl/ali-global-locale.ts +++ b/packages/editor-core/src/intl/ali-global-locale.ts @@ -1,5 +1,6 @@ import { EventEmitter } from 'events'; import { obx, computed } from '../utils/obx'; + const languageMap: { [key: string]: string } = { en: 'en-US', zh: 'zh-CN', @@ -30,7 +31,9 @@ const LowcodeConfigKey = 'ali-lowcode-config'; class AliGlobalLocale { private emitter = new EventEmitter(); + @obx.ref private _locale?: string; + @computed get locale() { if (this._locale != null) { return this._locale; @@ -67,7 +70,7 @@ class AliGlobalLocale { const it = navigator.browserLanguage.split('-'); locale = it[0]; if (it[1]) { - locale += '-' + it[1].toUpperCase(); + locale += `-${ it[1].toUpperCase()}`; } } diff --git a/packages/editor-core/src/intl/index.ts b/packages/editor-core/src/intl/index.ts index 88974b764..e4d6b4e37 100644 --- a/packages/editor-core/src/intl/index.ts +++ b/packages/editor-core/src/intl/index.ts @@ -34,7 +34,7 @@ function injectVars(msg: string, params: any, locale: string): string { return params[key]; } return $1; - });*/ + }); */ } export function intl(data: any, params?: object): ReactNode { @@ -92,11 +92,11 @@ class IntlElement extends Component<{ data: any; params?: object }> { export function createIntl( instance: string | object, ): { - intlNode(id: string, params?: object): ReactNode; - intl(id: string, params?: object): string; - getLocale(): string; - setLocale(locale: string): void; -} { + intlNode(id: string, params?: object): ReactNode; + intl(id: string, params?: object): string; + getLocale(): string; + setLocale(locale: string): void; + } { const data = computed(() => { const locale = globalLocale.getLocale(); if (typeof instance === 'string') { diff --git a/packages/editor-core/src/utils/app-preset.ts b/packages/editor-core/src/utils/app-preset.ts index f17a81458..72ae9321a 100644 --- a/packages/editor-core/src/utils/app-preset.ts +++ b/packages/editor-core/src/utils/app-preset.ts @@ -28,7 +28,7 @@ window.__newFunc = (funContext: string): ((...args: any[]) => any) => { }; // 关闭浏览器前提醒,只有产生过交互才会生效 -window.onbeforeunload = function(e: Event): string | void { +window.onbeforeunload = function (e: Event): string | void { const ev = e || window.event; // 本地调试不生效 if (location.href.indexOf('localhost') > 0) { diff --git a/packages/editor-core/src/utils/focus-tracker.ts b/packages/editor-core/src/utils/focus-tracker.ts index 6bfaa8581..61ecb3042 100644 --- a/packages/editor-core/src/utils/focus-tracker.ts +++ b/packages/editor-core/src/utils/focus-tracker.ts @@ -4,7 +4,7 @@ export class FocusTracker { if (this.checkModalDown(e)) { return; } - const first = this.first; + const { first } = this; if (first && !first.internalCheckInRange(e)) { this.internalSuspenseItem(first); first.internalTriggerBlur(); @@ -15,23 +15,30 @@ export class FocusTracker { win.document.removeEventListener('click', checkDown, true); }; } + private actives: Focusable[] = []; + get first() { return this.actives[0]; } + private modals: Array<{ checkDown: (e: MouseEvent) => boolean; checkOpen: () => boolean }> = []; + addModal(checkDown: (e: MouseEvent) => boolean, checkOpen: () => boolean) { this.modals.push({ checkDown, checkOpen, }); } + private checkModalOpen(): boolean { return this.modals.some(item => item.checkOpen()); } + private checkModalDown(e: MouseEvent): boolean { return this.modals.some(item => item.checkDown(e)); } + execSave() { // has Modal return; if (this.checkModalOpen()) { @@ -42,16 +49,19 @@ export class FocusTracker { this.first.internalTriggerSave(); } } + execEsc() { - const first = this.first; + const { first } = this; if (first) { this.internalSuspenseItem(first); first.internalTriggerEsc(); } } + create(config: FocusableConfig) { return new Focusable(this, config); } + internalActiveItem(item: Focusable) { const first = this.actives[0]; if (first === item) { @@ -69,6 +79,7 @@ export class FocusTracker { // trigger onActive item.internalTriggerActive(); } + internalSuspenseItem(item: Focusable) { const i = this.actives.indexOf(item); if (i > -1) { @@ -89,18 +100,23 @@ export interface FocusableConfig { export class Focusable { readonly isModal: boolean; + constructor(private tracker: FocusTracker, private config: FocusableConfig) { this.isModal = config.modal == null ? false : config.modal; } + active() { this.tracker.internalActiveItem(this); } + suspense() { this.tracker.internalSuspenseItem(this); } + purge() { this.tracker.internalSuspenseItem(this); } + internalCheckInRange(e: MouseEvent) { const { range } = this.config; if (!range) { @@ -111,11 +127,13 @@ export class Focusable { } return range.contains(e.target as HTMLElement); } + internalTriggerBlur() { if (this.config.onBlur) { this.config.onBlur(); } } + internalTriggerSave() { if (this.config.onSave) { this.config.onSave(); @@ -123,11 +141,13 @@ export class Focusable { } return false; } + internalTriggerEsc() { if (this.config.onEsc) { this.config.onEsc(); } } + internalTriggerActive() { if (this.config.onActive) { this.config.onActive(); diff --git a/packages/editor-core/src/utils/obx.ts b/packages/editor-core/src/utils/obx.ts index 3ab051f08..627a7e273 100644 --- a/packages/editor-core/src/utils/obx.ts +++ b/packages/editor-core/src/utils/obx.ts @@ -1,4 +1,5 @@ -export * from '@recore/obx'; import { observer } from '@recore/obx-react'; +export * from '@recore/obx'; + export { observer }; diff --git a/packages/editor-core/src/utils/request.ts b/packages/editor-core/src/utils/request.ts index 24cde7293..e7f4c41cf 100644 --- a/packages/editor-core/src/utils/request.ts +++ b/packages/editor-core/src/utils/request.ts @@ -1,4 +1,5 @@ import Debug from 'debug'; + const debug = Debug('request'); export function serialize(obj?: object): string { @@ -49,7 +50,7 @@ export function post(dataAPI: string, params?: object, headers?: object, otherPr export function request( dataAPI: string, - method: string = 'GET', + method = 'GET', data?: object | string, headers?: object, otherProps?: any, diff --git a/packages/editor-core/src/widgets/tip/tip-container.tsx b/packages/editor-core/src/widgets/tip/tip-container.tsx index 35ff58f14..81750bd6a 100644 --- a/packages/editor-core/src/widgets/tip/tip-container.tsx +++ b/packages/editor-core/src/widgets/tip/tip-container.tsx @@ -20,7 +20,7 @@ export class TipContainer extends Component { }; } - componentWillMount() { + UNSAFE_componentWillMount() { if (this.dispose) { this.dispose(); } diff --git a/packages/editor-core/src/widgets/tip/tip-handler.ts b/packages/editor-core/src/widgets/tip/tip-handler.ts index d528a52af..c0a806888 100644 --- a/packages/editor-core/src/widgets/tip/tip-handler.ts +++ b/packages/editor-core/src/widgets/tip/tip-handler.ts @@ -7,8 +7,11 @@ export interface TipOptions extends TipConfig { class TipHandler { tip: TipOptions | null = null; + private showDelay: number | null = null; + private hideDelay: number | null = null; + private emitter = new EventEmitter(); setTarget(target: HTMLElement) { @@ -107,7 +110,7 @@ function findTip(target: HTMLElement | null): TipOptions | null { while (child) { if (child.dataset && child.dataset.role === 'tip') { - const tipId = child.dataset.tipId; + const { tipId } = child.dataset; if (!tipId) { return null; } diff --git a/packages/editor-core/src/widgets/tip/tip-item.tsx b/packages/editor-core/src/widgets/tip/tip-item.tsx index 20ec87bea..58aed12b4 100644 --- a/packages/editor-core/src/widgets/tip/tip-item.tsx +++ b/packages/editor-core/src/widgets/tip/tip-item.tsx @@ -7,6 +7,7 @@ import { tipHandler } from './tip-handler'; export class TipItem extends Component { private dispose?: () => void; + constructor(props: any) { super(props); this.dispose = tipHandler.onChange(() => this.forceUpdate()); @@ -32,6 +33,7 @@ export class TipItem extends Component { } private timer: number | null = null; + clearTimer() { if (this.timer) { clearTimeout(this.timer); @@ -40,13 +42,14 @@ export class TipItem extends Component { } private shell: HTMLDivElement | null = null; - private originClassName: string = ''; + + private originClassName = ''; updateTip() { if (!this.shell) { return; } - const shell = this.shell; + const { shell } = this; const arrow = shell.querySelector('.lc-arrow') as HTMLElement; // reset @@ -55,7 +58,7 @@ export class TipItem extends Component { arrow.style.cssText = ''; this.clearTimer(); - const tip = tipHandler.tip; + const { tip } = tipHandler; if (!tip) { return; } diff --git a/packages/editor-core/src/widgets/title/index.tsx b/packages/editor-core/src/widgets/title/index.tsx index 1006935ed..5a8ac589b 100644 --- a/packages/editor-core/src/widgets/title/index.tsx +++ b/packages/editor-core/src/widgets/title/index.tsx @@ -11,6 +11,7 @@ export class Title extends Component<{ title: TitleContent; className?: string; super(props); this.handleClick = this.handleClick.bind(this); } + handleClick(e: React.MouseEvent) { const { title, onClick } = this.props as any; const url = title && (title.docUrl || title.url); @@ -22,7 +23,9 @@ export class Title extends Component<{ title: TitleContent; className?: string; // TODO: 操作交互冲突,目前 mixedSetter 仅有 2 个 setter 注册时用到了 onClick onClick && onClick(e); } + render() { + // eslint-disable-next-line prefer-const let { title, className } = this.props; if (title == null) { return null; @@ -53,7 +56,7 @@ export class Title extends Component<{ title: TitleContent; className?: string; diff --git a/packages/editor-preset-general/build.plugin.js b/packages/editor-preset-general/build.plugin.js index 1e9165eaf..ba3096f14 100644 --- a/packages/editor-preset-general/build.plugin.js +++ b/packages/editor-preset-general/build.plugin.js @@ -5,7 +5,7 @@ module.exports = ({ onGetWebpackConfig }) => { config.resolve .plugin('tsconfigpaths') .use(TsconfigPathsPlugin, [{ - configFile: "./tsconfig.json" + configFile: './tsconfig.json', }]); /* diff --git a/packages/editor-preset-general/src/index.ts b/packages/editor-preset-general/src/index.ts index 25de7f209..97f98db4b 100644 --- a/packages/editor-preset-general/src/index.ts +++ b/packages/editor-preset-general/src/index.ts @@ -1,11 +1,13 @@ import { render } from 'react-dom'; import { createElement } from 'react'; -import { Workbench, Skeleton, SettingsPrimaryPane, registerDefaults } from '@ali/lowcode-editor-skeleton'; -import { globalContext, Editor } from '@ali/lowcode-editor-core'; -import { Designer, LiveEditing, TransformStage, Node } from '@ali/lowcode-designer'; -import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane'; -import DesignerPlugin from '@ali/lowcode-plugin-designer'; import '@ali/lowcode-editor-setters'; +import DesignerPlugin from '@ali/lowcode-plugin-designer'; +import { Designer, LiveEditing } from '@ali/lowcode-designer'; +import { globalContext, Editor } from '@ali/lowcode-editor-core'; +import { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane'; +import { Workbench, Skeleton, SettingsPrimaryPane, registerDefaults } from '@ali/lowcode-editor-skeleton'; + +import { version } from '../package.json'; import { liveEditingRule, liveEditingSaveHander } from './live-editing'; export * from '@ali/lowcode-types'; @@ -22,7 +24,7 @@ editor.set(Skeleton, skeleton); editor.set('skeleton', skeleton); registerDefaults(); -export const designer = new Designer({ editor: editor }); +export const designer = new Designer({ editor }); editor.set(Designer, designer); editor.set('designer', designer); @@ -50,8 +52,6 @@ skeleton.add({ content: OutlineBackupPane, }); -const version = '0.9.0-beta'; - export default function GeneralWorkbench(props: any) { return createElement(Workbench, { skeleton, diff --git a/packages/editor-preset-general/src/live-editing.ts b/packages/editor-preset-general/src/live-editing.ts index de812d71e..0d6131e39 100644 --- a/packages/editor-preset-general/src/live-editing.ts +++ b/packages/editor-preset-general/src/live-editing.ts @@ -21,7 +21,7 @@ function getText(node: DocNode, prop: string) { export function liveEditingRule(target: EditingTarget) { // for vision components specific - const { node, rootElement, event } = target; + const { node, event } = target; const targetElement = event.target as HTMLElement; @@ -29,7 +29,7 @@ export function liveEditingRule(target: EditingTarget) { return null; } - const innerText = targetElement.innerText; + const { innerText } = targetElement; const propTarget = ['title', 'label', 'text', 'content', 'children'].find(prop => { return equalText(getText(node, prop), innerText); }); @@ -48,12 +48,12 @@ function equalText(v: any, innerText: string) { if (typeof v !== 'string') { return false; } - return v.trim() === innerText + return v.trim() === innerText; } export const liveEditingSaveHander: SaveHandler = { condition: (prop) => { - const v = prop.getValue(); + // const v = prop.getValue(); return prop.type === 'expression'; // || isI18nData(v); }, onSaveContent: (content, prop) => { @@ -72,8 +72,8 @@ export const liveEditingSaveHander: SaveHandler = { } else { prop.setValue(data); } - } -} + }, +}; // TODO: // 非文本编辑 // 国际化数据,改变当前 diff --git a/packages/editor-preset-vision/.eslintrc.js b/packages/editor-preset-vision/.eslintrc.js new file mode 100644 index 000000000..f3461d8f8 --- /dev/null +++ b/packages/editor-preset-vision/.eslintrc.js @@ -0,0 +1,20 @@ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'react/no-multi-comp': 1, + 'no-unused-expressions': 1, + 'implicit-arrow-linebreak': 1, + 'no-nested-ternary': 1, + 'no-mixed-operators': 1, + '@typescript-eslint/no-parameter-properties': 1, + '@typescript-eslint/ban-types': 1, + 'no-shadow': 1, + 'no-prototype-builtins': 1, + '@typescript-eslint/no-unused-vars': 1, + 'no-multi-assign': 1, + 'no-dupe-class-members': 1, + 'react/no-deprecated': 1, + 'no-useless-escape': 1, + 'brace-style': 1, + } +} \ No newline at end of file diff --git a/packages/editor-preset-vision/build.plugin.js b/packages/editor-preset-vision/build.plugin.js index 1e9165eaf..ba3096f14 100644 --- a/packages/editor-preset-vision/build.plugin.js +++ b/packages/editor-preset-vision/build.plugin.js @@ -5,7 +5,7 @@ module.exports = ({ onGetWebpackConfig }) => { config.resolve .plugin('tsconfigpaths') .use(TsconfigPathsPlugin, [{ - configFile: "./tsconfig.json" + configFile: './tsconfig.json', }]); /* diff --git a/packages/editor-preset-vision/src/base/base.ts b/packages/editor-preset-vision/src/base/base.ts index a19c1252a..3c3616a91 100644 --- a/packages/editor-preset-vision/src/base/base.ts +++ b/packages/editor-preset-vision/src/base/base.ts @@ -81,10 +81,13 @@ export function connectGeneralManagerList(managerList: any[], sourceManagerList: export class BaseManager implements INameable, IObservable { static EVENTS: IEventNameMap = {}; + static NAME = 'BaseManager'; private name: string; + private id: string; + private emitter: any; constructor(managerConfigs: IManagerConfigs = {}) { diff --git a/packages/editor-preset-vision/src/base/schemaManager.ts b/packages/editor-preset-vision/src/base/schemaManager.ts index 00ef16561..7a0a90867 100644 --- a/packages/editor-preset-vision/src/base/schemaManager.ts +++ b/packages/editor-preset-vision/src/base/schemaManager.ts @@ -11,7 +11,9 @@ import VisualManager from './visualManager'; export default class SchemaManager extends BaseManager implements IManagerController, ISchemaController { private schemaData: object = {}; + private visualManagerList: VisualManager[] = []; + private schemaManagerList: SchemaManager[] = []; getManager(): VisualManager { diff --git a/packages/editor-preset-vision/src/base/visualDesigner.ts b/packages/editor-preset-vision/src/base/visualDesigner.ts index a69018aba..a210b6737 100644 --- a/packages/editor-preset-vision/src/base/visualDesigner.ts +++ b/packages/editor-preset-vision/src/base/visualDesigner.ts @@ -22,16 +22,22 @@ interface IDesignerProps { export default class VisualDesigner extends Component implements IManagerController, IObservable, INameable { static NAME = 'VisualDesigner'; + static EVENTS: IEventNameMap = {}; + props: IDesignerProps = {}; + defaultProps: IDesignerProps = { name: 'defaultDesigner', visualManagers: [], }; private visualManagerList: VisualManager[] = []; + private name = ''; + private id = ''; + private emitter: IEmitter; constructor(props: IDesignerProps) { diff --git a/packages/editor-preset-vision/src/base/visualManager.ts b/packages/editor-preset-vision/src/base/visualManager.ts index e28b6b519..737120f20 100644 --- a/packages/editor-preset-vision/src/base/visualManager.ts +++ b/packages/editor-preset-vision/src/base/visualManager.ts @@ -11,6 +11,7 @@ import VisualDesigner from './visualDesigner'; export default class VisualManager extends BaseManager implements IManagerController, IDesignerController { private visualManagerList: VisualManager[] = []; + private visualDesignerList: VisualDesigner[] = []; getManager(): VisualManager { diff --git a/packages/editor-preset-vision/src/bundle/bundle.ts b/packages/editor-preset-vision/src/bundle/bundle.ts index 4e2cc3f6e..68fde2ea5 100644 --- a/packages/editor-preset-vision/src/bundle/bundle.ts +++ b/packages/editor-preset-vision/src/bundle/bundle.ts @@ -35,17 +35,25 @@ export interface ComponentViewBundle { export default class Bundle { static createPrototype = Prototype.create; + static addGlobalPropsReducer = Prototype.addGlobalPropsReducer; + static addGlobalPropsConfigure = Prototype.addGlobalPropsConfigure; + static addGlobalExtraActions = Prototype.addGlobalExtraActions; + static removeGlobalPropsConfigure = Prototype.removeGlobalPropsConfigure; + static overridePropsConfigure = Prototype.overridePropsConfigure; + static create(protos: ComponentProtoBundle[], views?: ComponentViewBundle[]) { return new Bundle(protos, views); } private viewsMap: { [componentName: string]: ComponentType } = {}; + private registry: { [componentName: string]: Prototype } = {}; + private prototypeList: Prototype[] = []; constructor(protos?: ComponentProtoBundle[], views?: ComponentViewBundle[]) { diff --git a/packages/editor-preset-vision/src/bundle/prototype.ts b/packages/editor-preset-vision/src/bundle/prototype.ts index d842563b1..b91554240 100644 --- a/packages/editor-preset-vision/src/bundle/prototype.ts +++ b/packages/editor-preset-vision/src/bundle/prototype.ts @@ -209,27 +209,37 @@ function isNewSpec(options: any): options is ComponentMetadata { class Prototype { static addGlobalPropsReducer = addGlobalPropsReducer; + static addGlobalPropsConfigure = addGlobalPropsConfigure; + static addGlobalExtraActions = addGlobalExtraActions; + static removeGlobalPropsConfigure = removeGlobalPropsConfigure; + static overridePropsConfigure = overridePropsConfigure; - static create(config: OldPrototypeConfig | ComponentMetadata | ComponentMeta, extraConfigs: any = null, lookup: boolean = false) { + + static create(config: OldPrototypeConfig | ComponentMetadata | ComponentMeta, extraConfigs: any = null, lookup = false) { return new Prototype(config, extraConfigs, lookup); } readonly isPrototype = true; + readonly meta: ComponentMeta; + readonly options: OldPrototypeConfig | ComponentMetadata; + get componentName() { return this.getId(); } + get packageName() { return this.meta.npm?.package; } + // 兼容原 vision 用法 view: ComponentType | undefined; - constructor(input: OldPrototypeConfig | ComponentMetadata | ComponentMeta, extraConfigs: any = null, lookup: boolean = false) { + constructor(input: OldPrototypeConfig | ComponentMetadata | ComponentMeta, extraConfigs: any = null, lookup = false) { if (lookup) { this.meta = designer.getComponentMeta(input.componentName); this.options = this.meta.getMetadata(); @@ -283,6 +293,7 @@ class Prototype { } private category?: string; + getCategory() { if (this.options.category != null) { return this.options.category; diff --git a/packages/editor-preset-vision/src/bundle/trunk.ts b/packages/editor-preset-vision/src/bundle/trunk.ts index e452a8b6e..5144275ad 100644 --- a/packages/editor-preset-vision/src/bundle/trunk.ts +++ b/packages/editor-preset-vision/src/bundle/trunk.ts @@ -8,8 +8,11 @@ import Prototype from './prototype'; export class Trunk { private trunk: any[] = []; + private emitter: EventEmitter = new EventEmitter(); + private metaBundle = new Bundle(); + private componentPrototypeMocker: any; isReady() { @@ -123,7 +126,7 @@ export class Trunk { console.warn('Trunk.setPackages is deprecated'); } - getSetter(type: string): any{ + getSetter(type: string): any { const setter = getSetter(type); if (setter?.component) { return setter.component; diff --git a/packages/editor-preset-vision/src/bundle/upgrade-metadata.ts b/packages/editor-preset-vision/src/bundle/upgrade-metadata.ts index c03b7f82e..b0931d6ca 100644 --- a/packages/editor-preset-vision/src/bundle/upgrade-metadata.ts +++ b/packages/editor-preset-vision/src/bundle/upgrade-metadata.ts @@ -308,7 +308,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec autorun: (field: Field) => { let fieldValue = untracked(() => field.getValue()); if (accessor) { - fieldValue = accessor.call(field, fieldValue) + fieldValue = accessor.call(field, fieldValue); } if (sync) { fieldValue = sync.call(field, fieldValue); @@ -318,7 +318,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec } else { field.setValue(fieldValue); } - } + }, }); } @@ -366,8 +366,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec let disabledValue: boolean; if (typeof disabled === 'function') { disabledValue = disabled.call(field, currentValue) === true; - } - else { + } else { disabledValue = disabled === true; } if (disabledValue) { @@ -377,7 +376,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec return ignore.call(field, currentValue) !== true; } return ignore !== true; - } + }, }); } @@ -437,8 +436,7 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec autorun: item.autorun, }); }, - }, - ) + }) : []; newConfig.items = objItems; @@ -475,8 +473,8 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec componentName: setter, condition: condition ? (field: Field) => { - return condition.call(field, field.getValue()); - } + return condition.call(field, field.getValue()); + } : null, }; }); @@ -520,11 +518,11 @@ type ConfigCollector = { addInitial: AddInitial; addFilter: AddFilter; addAutorun: AddAutorun; -} +}; function getInitialFromSetter(setter: any) { return setter && ( - setter.initial || setter.Initial + setter.initial || setter.Initial || (setter.type && (setter.type.initial || setter.type.Initial)) ) || null; // eslint-disable-line } @@ -719,8 +717,8 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { experimental.initialChildren = typeof initialChildren === 'function' ? (node: any) => { - return initialChildren.call(node, node.settingEntry); - } + return initialChildren.call(node, node.settingEntry); + } : initialChildren; } if (view) { @@ -796,9 +794,8 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { }, addAutorun: (item) => { autoruns.push(item); - } - } - ); + }, + }); experimental.initials = initials; experimental.filters = filters; experimental.autoruns = autoruns; diff --git a/packages/editor-preset-vision/src/components/index.tsx b/packages/editor-preset-vision/src/components/index.tsx index f32681dd0..77e24d70b 100644 --- a/packages/editor-preset-vision/src/components/index.tsx +++ b/packages/editor-preset-vision/src/components/index.tsx @@ -48,16 +48,19 @@ export class InstanceNodeSelector extends React.Component { node.select(); } }; + onMouseOver = (node: Node) => (_: any, flag = true) => { if (node && typeof node.hover === 'function') { node.hover(flag); } }; + onMouseOut = (node: Node) => (_: any, flag = false) => { if (node && typeof node.hover === 'function') { node.hover(flag); } }; + renderNodes = (node: Node) => { const nodes = this.state.parentNodes || []; const children = nodes.map((node, key) => { diff --git a/packages/editor-preset-vision/src/context.ts b/packages/editor-preset-vision/src/context.ts index 47a6917a4..d9171c07b 100644 --- a/packages/editor-preset-vision/src/context.ts +++ b/packages/editor-preset-vision/src/context.ts @@ -15,7 +15,9 @@ export type SetterProvider = (prop: any, componentPrototype: Prototype) => Compo export class VisualEngineContext { private managerMap: { [name: string]: VisualManager } = {}; + private moduleMap: { [name: string]: any } = {}; + private pluginsMap: { [name: string]: any } = {}; use(pluginName: string, plugin: any) { @@ -43,7 +45,9 @@ export class VisualEngineContext { } registerManager(managerMap?: { [name: string]: VisualManager }): this; + registerManager(name: string, manager: VisualManager): this; + registerManager(name?: any, manager?: VisualManager): this { if (name && typeof name === 'object') { this.managerMap = assign(this.managerMap, name); @@ -54,7 +58,9 @@ export class VisualEngineContext { } registerModule(moduleMap: { [name: string]: any }): this; + registerModule(name: string, module: any): this; + registerModule(name?: any, module?: any): this { if (typeof name === 'object') { this.moduleMap = Object.assign({}, this.moduleMap, name); diff --git a/packages/editor-preset-vision/src/drag-engine.ts b/packages/editor-preset-vision/src/drag-engine.ts index c1844c38a..303e56434 100644 --- a/packages/editor-preset-vision/src/drag-engine.ts +++ b/packages/editor-preset-vision/src/drag-engine.ts @@ -2,7 +2,7 @@ import { designer } from './editor'; import { DragObjectType, isNode, isDragNodeDataObject } from '@ali/lowcode-designer'; import { isPrototype } from './bundle/prototype'; -const dragon = designer.dragon; +const { dragon } = designer; const DragEngine = { from(shell: Element, boost: (e: MouseEvent) => any): any { return dragon.from(shell, (e) => { diff --git a/packages/editor-preset-vision/src/editor.ts b/packages/editor-preset-vision/src/editor.ts index a47c9eb76..96ecf7229 100644 --- a/packages/editor-preset-vision/src/editor.ts +++ b/packages/editor-preset-vision/src/editor.ts @@ -22,7 +22,7 @@ editor.set(Skeleton, skeleton); editor.set('skeleton', skeleton); registerDefaults(); -export const designer = new Designer({ editor: editor }); +export const designer = new Designer({ editor }); editor.set(Designer, designer); editor.set('designer', designer); @@ -142,7 +142,7 @@ designer.addPropsReducer((props: any, node: Node) => { return { ...props, lifeCycles: {}, - } + }; } return props; }, TransformStage.Render); @@ -223,8 +223,8 @@ designer.addPropsReducer((props: any, node: Node) => { // 设计器组件样式处理 function stylePropsReducer(props: any, node: any) { if (props && typeof props === 'object' && props.__style__) { - const cssId = '_style_pesudo_' + node.id.replace(/\$/g, '_'); - const cssClass = '_css_pesudo_' + node.id.replace(/\$/g, '_'); + const cssId = `_style_pesudo_${ node.id.replace(/\$/g, '_')}`; + const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`; const styleProp = props.__style__; appendStyleNode(props, styleProp, cssClass, cssId); } @@ -235,8 +235,8 @@ function stylePropsReducer(props: any, node: any) { appendStyleNode(props, styleProp, cssClass, cssId); } if (props && typeof props === 'object' && props.containerStyle) { - const cssId = '_style_pesudo_' + node.id; - const cssClass = '_css_pesudo_' + node.id.replace(/\$/g, '_'); + const cssId = `_style_pesudo_${ node.id}`; + const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`; const styleProp = props.containerStyle; appendStyleNode(props, styleProp, cssClass, cssId); } @@ -264,7 +264,7 @@ function appendStyleNode(props: any, styleProp: any, cssClass: string, cssId: st s.appendChild(doc.createTextNode(styleProp.replace(/(\d+)rpx/g, (a, b) => { return `${b / 2}px`; - }).replace(/:root/g, '.' + cssClass))); + }).replace(/:root/g, `.${ cssClass}`))); } } designer.addPropsReducer(stylePropsReducer, TransformStage.Render); diff --git a/packages/editor-preset-vision/src/env.ts b/packages/editor-preset-vision/src/env.ts index 8c7eae39b..4e267ee24 100644 --- a/packages/editor-preset-vision/src/env.ts +++ b/packages/editor-preset-vision/src/env.ts @@ -10,6 +10,7 @@ export class Env { @obx.val envs: ILiteralObject = {}; private emitter: EventEmitter; + private featureMap: ILiteralObject; constructor() { diff --git a/packages/editor-preset-vision/src/exchange.ts b/packages/editor-preset-vision/src/exchange.ts index 6e1f8c13e..350ccb5fe 100644 --- a/packages/editor-preset-vision/src/exchange.ts +++ b/packages/editor-preset-vision/src/exchange.ts @@ -20,5 +20,5 @@ export default { return () => { // this.emitter.removeListener('intoview', func); }; - } -} + }, +}; diff --git a/packages/editor-preset-vision/src/fields/field.tsx b/packages/editor-preset-vision/src/fields/field.tsx index 3fc13ea7e..ede347595 100644 --- a/packages/editor-preset-vision/src/fields/field.tsx +++ b/packages/editor-preset-vision/src/fields/field.tsx @@ -10,7 +10,7 @@ interface IHelpTip { } function splitWord(title: string): JSX.Element[] { - return (title || '').split('').map((w, i) => {w}); + return (title || '').split('').map((w, i) => {w}); } function getFieldTitle(title: string, tip: IHelpTip, compact?: boolean, propName?: string): JSX.Element { @@ -49,12 +49,12 @@ function getFieldTitle(title: string, tip: IHelpTip, compact?: boolean, propName return ( {titleContent || (typeof title === 'object' ? '' : title)} - {tipContent} + {tipContent} ); } @@ -92,6 +92,7 @@ export default class VEField extends Component { public static displayName = 'VEField'; public readonly props: IVEFieldProps; + public classNames: string[] = []; public state: IVEFieldState = { @@ -133,15 +134,15 @@ export default class VEField extends Component { } const headContent = headDIY ? this.renderHead() - :
{this.renderHead()}
; + :
{this.renderHead()}
; return ( -
+
{headContent} -
+
{this.renderBody()}
-
+
{this.renderFoot()}
diff --git a/packages/editor-preset-vision/src/fields/fields.tsx b/packages/editor-preset-vision/src/fields/fields.tsx index e106fe7f7..8f9d645c9 100644 --- a/packages/editor-preset-vision/src/fields/fields.tsx +++ b/packages/editor-preset-vision/src/fields/fields.tsx @@ -50,7 +50,7 @@ export class PlainField extends VEField { headDIY: true, }; - public static displayName: string = 'PlainField'; + public static displayName = 'PlainField'; public renderHead(): null { return null; @@ -59,6 +59,7 @@ export class PlainField extends VEField { export class InlineField extends VEField { public static displayName = 'InlineField'; + constructor(props: any) { super(props); this.classNames = ['engine-setting-field', 'engine-inline-field']; @@ -194,15 +195,14 @@ export class PopupField extends VEField { return (
- popups.popup({ - cancelOnBlur: true, - content: this.props.children, - position: 'left bottom', - showClose: true, - sizeFixed: true, - target: e.currentTarget, - }) + onClick={(e) => popups.popup({ + cancelOnBlur: true, + content: this.props.children, + position: 'left bottom', + showClose: true, + sizeFixed: true, + target: e.currentTarget, + }) } > {title} @@ -240,8 +240,11 @@ export class Stage extends Component { }; public stage: any; + public additionClassName: string; + public shell: Element | null = null; + private willDetach: () => any; public componentWillMount() { @@ -294,7 +297,7 @@ export class Stage extends Component { } public render() { - const stage = this.stage; + const { stage } = this; let content = null; let tabs = null; diff --git a/packages/editor-preset-vision/src/fields/inlinetip.tsx b/packages/editor-preset-vision/src/fields/inlinetip.tsx index f3344b1c3..6ba0ec658 100644 --- a/packages/editor-preset-vision/src/fields/inlinetip.tsx +++ b/packages/editor-preset-vision/src/fields/inlinetip.tsx @@ -19,7 +19,7 @@ export default class InlineTip extends Component { return (
diff --git a/packages/editor-preset-vision/src/fields/settingField.tsx b/packages/editor-preset-vision/src/fields/settingField.tsx index 4edd0c52d..3da6b8cff 100644 --- a/packages/editor-preset-vision/src/fields/settingField.tsx +++ b/packages/editor-preset-vision/src/fields/settingField.tsx @@ -7,8 +7,8 @@ import { EntryField, InlineField, PlainField, - PopupField -} from "./fields"; + PopupField, +} from './fields'; import { ComponentClass, Component, isValidElement, createElement } from 'react'; import { createSetterContent, getSetter } from '@ali/lowcode-editor-core'; @@ -37,7 +37,7 @@ const FIELD_TYPE_MAP: any = { inline: InlineField, plain: PlainField, popup: PopupField, - tab: AccordionField + tab: AccordionField, }; export class SettingField extends Component { @@ -67,7 +67,7 @@ export class SettingField extends Component { const { prop, selected, addonProps } = this.props; const display = this.props.forceDisplay || prop.getDisplay(); - if (display === "none") { + if (display === 'none') { return null; } @@ -82,7 +82,7 @@ export class SettingField extends Component { prop, setUseVariable: () => prop.setUseVariable(!prop.isUseVariable()), tip: prop.getTip(), - title: prop.getTitle() + title: prop.getTitle(), }; // 部分 Field 所需要的额外 fieldProps @@ -90,7 +90,7 @@ export class SettingField extends Component { const ctx = context; const plugin = ctx.getPlugin(VE_HOOKS.VE_SETTING_FIELD_PROVIDER); let Field; - if (typeof plugin === "function") { + if (typeof plugin === 'function') { Field = plugin(display, FIELD_TYPE_MAP, prop); } if (!Field) { @@ -98,7 +98,7 @@ export class SettingField extends Component { } this._prepareProps(display, extraProps); - if (display === "entry") { + if (display === 'entry') { return ; } @@ -110,7 +110,7 @@ export class SettingField extends Component { const fieldProps = { ...standardProps, ...extraProps }; if (prop.isUseVariable() && !this.variableSetter.isPopup) { - props.placeholder = "请输入表达式: ${var}"; + props.placeholder = '请输入表达式: ${var}'; props.key = `${prop.getId()}-variable`; setter = createElement(this.variableSetter, props); return {setter}; @@ -128,20 +128,20 @@ export class SettingField extends Component { setter = prop.getSetter(); if ( - typeof setter === "object" && - "componentName" in setter && + typeof setter === 'object' && + 'componentName' in setter && !(isValidElement(setter) || isReactClass(setter)) ) { const { componentName: setterType, props: setterProps } = setter as any; setter = createSetterContent(setterType, { ...addonProps, ...setterProps, - ...props + ...props, }); } else { setter = createSetterContent(setter, { ...addonProps, - ...props + ...props, }); } @@ -150,22 +150,22 @@ export class SettingField extends Component { private supportMultiSetter() { const { prop } = this.props; - const setter = prop && prop.getConfig && prop.getConfig("setter"); + const setter = prop && prop.getConfig && prop.getConfig('setter'); return prop.isSupportVariable() || Array.isArray(setter); } private _prepareProps(displayType: string, extraProps: IExtraProps): void { const { prop } = this.props; extraProps.propName = prop.isGroup() - ? "组合属性,无属性名称" + ? '组合属性,无属性名称' : prop.getName(); switch (displayType) { - case "title": + case 'title': break; - case "block": + case 'block': Object.assign(extraProps, { isGroup: prop.isGroup() }); break; - case "accordion": + case 'accordion': Object.assign(extraProps, { headDIY: true, isExpand: prop.isExpand(), @@ -173,10 +173,10 @@ export class SettingField extends Component { onExpandChange: () => prop.onExpandChange(() => this.forceUpdate()), toggleExpand: () => { prop.toggleExpand(); - } + }, }); break; - case "entry": + case 'entry': Object.assign(extraProps, { stageName: prop.getName() }); break; default: diff --git a/packages/editor-preset-vision/src/fields/variableSetter.tsx b/packages/editor-preset-vision/src/fields/variableSetter.tsx index 51643bd16..87842967e 100644 --- a/packages/editor-preset-vision/src/fields/variableSetter.tsx +++ b/packages/editor-preset-vision/src/fields/variableSetter.tsx @@ -22,13 +22,14 @@ class Input extends Component { } private domRef: HTMLTextAreaElement | null = null; + public adjustTextAreaHeight() { if (!this.domRef) { return; } this.domRef.style.height = '1px'; const calculatedHeight = this.domRef.scrollHeight; - this.domRef.style.height = calculatedHeight >= 200 ? '200px' : calculatedHeight + 'px'; + this.domRef.style.height = calculatedHeight >= 200 ? '200px' : `${calculatedHeight }px`; } public render() { @@ -50,7 +51,7 @@ class Input extends Component { onBlur={() => this.setState({ focused: false })} onFocus={() => this.setState({ focused: true })} onKeyUp={this.adjustTextAreaHeight.bind(this)} - > + />
); } @@ -73,7 +74,7 @@ export default class VariableSetter extends Component<{ } public render() { - const prop = this.props.prop; + const { prop } = this.props; return ( { private ref: HTMLElement | null = null; + private VariableSetter: any; constructor(props: IVEFieldProps) { @@ -34,9 +35,9 @@ export default class VariableSwitcher extends Component { return (
{ @@ -48,7 +49,8 @@ export default class VariableSwitcher extends Component { } else { prop.setUseVariable(!isUseVariable); } - }}> + }} + > 绑定变量
diff --git a/packages/editor-preset-vision/src/flags.ts b/packages/editor-preset-vision/src/flags.ts index 5e6174ff0..5c5aefec3 100644 --- a/packages/editor-preset-vision/src/flags.ts +++ b/packages/editor-preset-vision/src/flags.ts @@ -5,11 +5,14 @@ import { EventEmitter } from 'events'; const Shells = ['iphone6']; export class Flags { - public emitter: EventEmitter; + public flags: string[]; + public ready: boolean; + public lastFlags: string[]; + public lastShell: string; private lastSimulatorDevice: string; diff --git a/packages/editor-preset-vision/src/i18n-util/index.js b/packages/editor-preset-vision/src/i18n-util/index.js index 06bd973ac..f5d6d2cba 100644 --- a/packages/editor-preset-vision/src/i18n-util/index.js +++ b/packages/editor-preset-vision/src/i18n-util/index.js @@ -20,7 +20,7 @@ class DocItem { type: 'i18n', ...strings, }); - this.emitter = new EventEmitter; + this.emitter = new EventEmitter(); this.inited = unInitial !== true; } @@ -69,7 +69,7 @@ class DocItem { class I18nUtil { constructor() { - this.emitter = new EventEmitter; + this.emitter = new EventEmitter(); // original data source from remote this.i18nData = {}; // current i18n records on the left pane diff --git a/packages/editor-preset-vision/src/index.ts b/packages/editor-preset-vision/src/index.ts index 358ced1fe..30a3ddef1 100644 --- a/packages/editor-preset-vision/src/index.ts +++ b/packages/editor-preset-vision/src/index.ts @@ -8,7 +8,7 @@ import { hotkey as Hotkey, monitor } from '@ali/lowcode-editor-core'; import { createElement } from 'react'; import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const'; import Bus from './bus'; -import { skeleton } from './editor'; +import { skeleton, designer, editor } from './editor'; import { Workbench } from '@ali/lowcode-editor-skeleton'; import Panes from './panes'; import Exchange from './exchange'; @@ -24,7 +24,7 @@ import Env from './env'; import DragEngine from './drag-engine'; import Viewport from './viewport'; import Project from './project'; -import { designer, editor } from './editor'; + import Symbols from './symbols'; import './vision.less'; @@ -162,6 +162,6 @@ const version = '6.0.0(LowcodeEngine 0.9.3)'; console.log( `%c VisionEngine %c v${version} `, - "padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060;font-weight:bold;", - "padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e;font-weight:bold;" + 'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060;font-weight:bold;', + 'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e;font-weight:bold;', ); diff --git a/packages/editor-preset-vision/src/panes.ts b/packages/editor-preset-vision/src/panes.ts index 592e1f7c2..8c0776cab 100644 --- a/packages/editor-preset-vision/src/panes.ts +++ b/packages/editor-preset-vision/src/panes.ts @@ -25,12 +25,12 @@ export interface OldPaneConfig { place?: string; // align: left|right|top|center|bottom description?: string; // tip? tip?: - | string - | { - // as help tip - url?: string; - content?: string | JSX.Element; - }; // help + | string + | { + // as help tip + url?: string; + content?: string | JSX.Element; + }; // help init?: () => any; destroy?: () => any; diff --git a/packages/editor-preset-vision/src/prop.ts b/packages/editor-preset-vision/src/prop.ts index 8d9abdfbf..5ead648cf 100644 --- a/packages/editor-preset-vision/src/prop.ts +++ b/packages/editor-preset-vision/src/prop.ts @@ -54,7 +54,6 @@ export interface IVariableSettable { } export default class Prop implements IVariableSettable { - /** * Setters predefined as default options * can by selected by user for every prop @@ -65,20 +64,27 @@ export default class Prop implements IVariableSettable { public static INSET_SETTER = {}; public id: string; + public emitter: EventEmitter; public inited: boolean; + public i18nLink: any; + public loopLock: boolean; public props: any; + public parent: any; public config: IPropConfig; + public initial: any; + public initialData: any; public expanded: boolean; + public useVariable?: boolean; /** @@ -86,18 +92,24 @@ export default class Prop implements IVariableSettable { * prototype.js can config Transducer.toNative to generate value */ public value: any; + /** * value to be used in VisualDesigner more flexible * prototype.js can config Transducer.toHot to generate hotValue */ public hotValue: any; + /** * 启用变量之后,变量表达式字符串值 */ public variableValue: string; + public hotData: IMMap; + public defaultValue: any; + public transducer: any; + public inGroup: boolean; constructor(parent: any, config: IPropConfig, data?: any) { @@ -263,7 +275,7 @@ export default class Prop implements IVariableSettable { public getValue(disableCache?: boolean, options?: { disableAccessor?: boolean; }) { - const accessor = this.config.accessor; + const { accessor } = this.config; if (accessor && (!options || !options.disableAccessor)) { const value = accessor.call(this as any, this.value); if (!disableCache) { @@ -313,7 +325,7 @@ export default class Prop implements IVariableSettable { return; } - const sync = this.config.sync; + const { sync } = this.config; if (sync) { const value = sync.call(this as any, this.getValue(true)); if (value !== undefined) { @@ -356,7 +368,7 @@ export default class Prop implements IVariableSettable { } } - public setUseVariable(flag: boolean = false) { + public setUseVariable(flag = false) { if (this.useVariable === flag) { return; } const state = this.props.chainReach(this); @@ -412,7 +424,7 @@ export default class Prop implements IVariableSettable { this.i18nLink = I18nUtil.attach(this, this.value, ((val: any) => this.setValue(val, false, true)) as any); - const mutator = this.config.mutator; + const { mutator } = this.config; if (!extraOptions) { extraOptions = {}; @@ -484,7 +496,7 @@ export default class Prop implements IVariableSettable { this.resolveValue(); if (!options || !options.disableMutator) { - const mutator = this.config.mutator; + const { mutator } = this.config; if (mutator) { mutator.call(this as any, value); } @@ -511,7 +523,7 @@ export default class Prop implements IVariableSettable { return true; } - let hidden = this.config.hidden; + let { hidden } = this.config; if (typeof hidden === 'function') { hidden = hidden.call(this as any, this.getValue()); } @@ -519,7 +531,7 @@ export default class Prop implements IVariableSettable { } public isDisabled() { - let disabled = this.config.disabled; + let { disabled } = this.config; if (typeof disabled === 'function') { disabled = disabled.call(this as any, this.getValue()); } @@ -529,7 +541,7 @@ export default class Prop implements IVariableSettable { public isIgnore() { if (this.isDisabled()) { return true; } - let ignore = this.config.ignore; + let { ignore } = this.config; if (typeof ignore === 'function') { ignore = ignore.call(this as any, this.getValue()); } diff --git a/packages/editor-preset-vision/src/rootNodeVisitor.ts b/packages/editor-preset-vision/src/rootNodeVisitor.ts index 811aa5fd7..266ff7930 100644 --- a/packages/editor-preset-vision/src/rootNodeVisitor.ts +++ b/packages/editor-preset-vision/src/rootNodeVisitor.ts @@ -9,11 +9,15 @@ import { DocumentModel, Node, Root } from '@ali/lowcode-designer'; */ export default class RootNodeVisitor { public nodeIdMap: {[id: string]: Node} = {}; + public nodeFieldIdMap: {[fieldId: string]: Node} = {}; + public nodeList: Node[] = []; private page: DocumentModel; + private root: RootNode; + private cancelers: Function[] = []; constructor(page: DocumentModel, rootNode: RootNode) { diff --git a/packages/editor-preset-vision/src/vc-live-editing.ts b/packages/editor-preset-vision/src/vc-live-editing.ts index 3a03161a6..59cceb655 100644 --- a/packages/editor-preset-vision/src/vc-live-editing.ts +++ b/packages/editor-preset-vision/src/vc-live-editing.ts @@ -52,7 +52,7 @@ export function liveEditingRule(target: EditingTarget) { return null; } - const innerText = targetElement.innerText; + const { innerText } = targetElement; const propTarget = ['title', 'label', 'text', 'content'].find(prop => { return equalText(getText(node, prop), innerText); }); @@ -71,7 +71,7 @@ function equalText(v: any, innerText: string) { if (typeof v !== 'string') { return false; } - return v.trim() === innerText + return v.trim() === innerText; } export const liveEditingSaveHander: SaveHandler = { @@ -108,8 +108,8 @@ export const liveEditingSaveHander: SaveHandler = { } else { prop.setValue(data); } - } -} + }, +}; // TODO: // 非文本编辑 // 国际化数据,改变当前 diff --git a/packages/editor-preset-vision/src/viewport.ts b/packages/editor-preset-vision/src/viewport.ts index 9745e9ee9..da943bed1 100644 --- a/packages/editor-preset-vision/src/viewport.ts +++ b/packages/editor-preset-vision/src/viewport.ts @@ -1,9 +1,9 @@ import { EventEmitter } from 'events'; - -const domReady = require('domready'); import Flags from './flags'; import { designer } from './editor'; +const domReady = require('domready'); + function enterFullscreen() { const elem = document.documentElement; if (elem.requestFullscreen) { @@ -29,8 +29,11 @@ interface IStyleResourceConfig { class StyleResource { config: IStyleResourceConfig; + styleElement: HTMLStyleElement; + mounted: boolean; + inited: boolean; constructor(config: IStyleResourceConfig) { @@ -38,7 +41,7 @@ class StyleResource { } matchDevice(device: string) { - const media = this.config.media; + const { media } = this.config; if (!media || media === 'ALL' || media === '*') { return true; @@ -94,11 +97,17 @@ class StyleResource { export class Viewport { preview: boolean; + focused: boolean; + slateFixed: boolean; + emitter: EventEmitter; + device: string; + focusTarget: any; + cssResourceSet: StyleResource[]; constructor() { diff --git a/packages/editor-setters/src/color-setter/index.tsx b/packages/editor-setters/src/color-setter/index.tsx index 1ae6ada64..2415e8935 100644 --- a/packages/editor-setters/src/color-setter/index.tsx +++ b/packages/editor-setters/src/color-setter/index.tsx @@ -16,20 +16,24 @@ export interface PluginProps { export default class ColorPickerView extends PureComponent { static display = 'ColorPicker'; + static propTypes = { onChange: PropTypes.func, value: PropTypes.string, }; + static defaultProps = { onChange: () => {}, value: '', }; + constructor(props: Readonly<{ value: string; defaultValue: string }>) { super(props); this.state = { value: props.value || props.defaultValue, }; } + static getDerivedStateFromProps(props: { value: string }, state: { preValue: string }) { if (props.value != state.preValue) { return { @@ -39,10 +43,11 @@ export default class ColorPickerView extends PureComponent { } return null; } + onChangeComplete = (color: Color): void => { let value; if (color.rgb.a < 1) { - const rgb = color.rgb; + const { rgb } = color; const rgba = [rgb.r, rgb.g, rgb.b, rgb.a]; value = `rgba(${rgba.join(',')})`; } else { @@ -53,13 +58,15 @@ export default class ColorPickerView extends PureComponent { }); this.props.onChange && this.props.onChange(value); }; + onInputChange = (value: string): void => { - if (/^[0-9a-zA-Z]{6}$/.test(value)) value = '#' + value; + if (/^[0-9a-zA-Z]{6}$/.test(value)) value = `#${ value}`; this.setState({ value, }); this.props.onChange && this.props.onChange(value); }; + render(): React.ReactNode { const { value, onChange, ...restProps } = this.props; const boxStyle = { @@ -74,13 +81,13 @@ export default class ColorPickerView extends PureComponent { - + ); return ( diff --git a/packages/editor-setters/src/events-setter/index.tsx b/packages/editor-setters/src/events-setter/index.tsx index cf6b78a9f..b0d806ecb 100644 --- a/packages/editor-setters/src/events-setter/index.tsx +++ b/packages/editor-setters/src/events-setter/index.tsx @@ -1,9 +1,10 @@ -import { Component, isValidElement, ReactElement, ReactNode } from 'react'; -import { Radio, Menu, Table, Icon, Dialog } from '@alifd/next'; +import { Component } from 'react'; +import { Radio, Menu, Table, Icon } from '@alifd/next'; import nativeEvents from './native-events'; import './index.scss'; -const { SubMenu, Item, Group, Divider } = Menu; + +const { Item, Group } = Menu; const RadioGroup = Radio.Group; const EVENT_CONTENTS = { @@ -18,21 +19,19 @@ const DEFINITION_EVENT_TYPE = { LIFE_CYCLE_EVENT: 'lifeCycleEvent', }; -const SETTER_NAME = 'event-setter' +const SETTER_NAME = 'event-setter'; export default class EventsSetter extends Component<{ value: any[]; onChange: (eventList: any[]) => void; }> { state = { - showEventList: false, eventBtns: [], eventList: [], selectType: null, nativeEventList: [], lifeCycleEventList: [], eventDataList: (this.props?.value?.eventDataList ? this.props.value.eventDataList : this.props?.value) || [], - relatedEventName: '', }; // constructor (){ @@ -57,19 +56,17 @@ export default class EventsSetter extends Component<{ // return null; // } - private bindEventName:String; + private bindEventName: string; componentDidMount() { - console.log(this.state.eventDataList); - const {editor} = this.props.field; + const { editor } = this.props.field; this.initEventBtns(); this.initEventList(); - editor.on(`${SETTER_NAME}.bindEvent`,(relatedEventName,paramStr)=>{ - this.bindEvent(relatedEventName,paramStr); - }) - + editor.on(`${SETTER_NAME}.bindEvent`, (relatedEventName, paramStr) => { + this.bindEvent(relatedEventName, paramStr); + }); } /** @@ -88,6 +85,8 @@ export default class EventsSetter extends Component<{ if (item.type === DEFINITION_EVENT_TYPE.EVENTS) { isCustom = true; } + + return item; }); if (isRoot) { @@ -146,6 +145,8 @@ export default class EventsSetter extends Component<{ lifeCycleEventList: item.list, }); } + + return item; }); if (nativeEventList.length == 0) { @@ -156,7 +157,7 @@ export default class EventsSetter extends Component<{ } } - checkEventListStatus = (eventList: Array, eventType: String) => { + checkEventListStatus = (eventList: any[], eventType: string) => { const { eventDataList } = this.state; if ( eventType === DEFINITION_EVENT_TYPE.EVENTS || @@ -168,7 +169,11 @@ export default class EventsSetter extends Component<{ if (item.name === eventDataItem.name) { item.disabled = true; } + + return eventDataItem; }); + + return item; }); } else if (eventType === DEFINITION_EVENT_TYPE.NATIVE_EVENTS) { eventDataList.map(eventDataItem => { @@ -179,8 +184,12 @@ export default class EventsSetter extends Component<{ } else { item.disabled = false; } + return eventItem; }); + return item; }); + + return eventDataItem; }); } }; @@ -205,7 +214,7 @@ export default class EventsSetter extends Component<{
- this.onRelatedEventNameClick(record.relatedEventName)}> + this.onRelatedEventNameClick(record.relatedEventName)}> {record.relatedEventName || ''}
@@ -216,7 +225,7 @@ export default class EventsSetter extends Component<{ /** * 渲染事件操作项 */ - renderEventOperateCell = (eventName: String) => { + renderEventOperateCell = (eventName: string) => { return (
{ + updateEventListStatus = (eventName: string, unDisabled: boolean) => { const { eventList, nativeEventList, lifeCycleEventList } = this.state; eventList.map(item => { if (item.name === eventName) { item.disabled = !unDisabled; } + return item; }); lifeCycleEventList.map(item => { if (item.name === eventName) { item.disabled = !unDisabled; } + return item; }); nativeEventList.map(item => { @@ -253,7 +264,10 @@ export default class EventsSetter extends Component<{ if (itemData.name === eventName) { itemData.disabled = !unDisabled; } + return itemData; }); + + return item; }); }; @@ -264,8 +278,7 @@ export default class EventsSetter extends Component<{ }; - - onEventMenuClick = (eventName: String) => { + onEventMenuClick = (eventName: string) => { const { selectType, eventDataList } = this.state; eventDataList.push({ type: selectType, @@ -281,22 +294,22 @@ export default class EventsSetter extends Component<{ this.openDialog(eventName); }; - onRelatedEventNameClick = (eventName:String) => { - const {editor} = this.props.field; + onRelatedEventNameClick = (eventName: string) => { + const { editor } = this.props.field; editor.get('skeleton').getPanel('sourceEditor').show(); - setTimeout(()=>{ - editor.emit('sourceEditor.focusByFunction',{ - functionName:eventName - }) - },300) + setTimeout(() => { + editor.emit('sourceEditor.focusByFunction', { + functionName: eventName, + }); + }, 300); // editor.emit('sourceEditor.focusByFunction',{ // functionName:eventName // }) - } + }; closeEventMenu = () => { if (this.state.selectType !== null) { @@ -306,7 +319,7 @@ export default class EventsSetter extends Component<{ } }; - openDeleteEventDialog = (eventName: String) => { + openDeleteEventDialog = (eventName: string) => { this.deleteEvent(eventName); // Dialog.confirm({ // title: '删除事件', @@ -315,54 +328,59 @@ export default class EventsSetter extends Component<{ // }); }; - deleteEvent = (eventName: String) => { - const { eventDataList,eventList} = this.state; + deleteEvent = (eventName: string) => { + const { eventDataList, eventList } = this.state; eventDataList.map((item, index) => { if (item.name === eventName) { eventDataList.splice(index, 1); } + + return item; }); this.setState({ eventDataList, }); - this.props.onChange({eventDataList,eventList}); + this.props.onChange({ eventDataList, eventList }); this.updateEventListStatus(eventName, true); }; - openDialog = (bindEventName: String) => { - const {editor} = this.props.field; - const {eventDataList} = this.state; + openDialog = (bindEventName: string) => { + const { editor } = this.props.field; + const { eventDataList } = this.state; let paramStr; - eventDataList.map((item)=>{ - if (item.name == bindEventName){ + eventDataList.map((item) => { + if (item.name == bindEventName) { paramStr = item.paramStr; } - }) + return item; + }); this.bindEventName = bindEventName; - editor.emit('eventBindDialog.openDialog',bindEventName,SETTER_NAME,paramStr); + editor.emit('eventBindDialog.openDialog', bindEventName, SETTER_NAME, paramStr); }; - bindEvent = (relatedEventName: String,paramStr:String) => { - const {eventDataList,eventList} = this.state; + bindEvent = (relatedEventName: string, paramStr: string) => { + const { eventDataList, eventList } = this.state; eventDataList.map(item => { if (item.name === this.bindEventName) { item.relatedEventName = relatedEventName; - if (paramStr){ - item.paramStr = paramStr + if (paramStr) { + item.paramStr = paramStr; } } + + return item; }); this.setState({ - eventDataList - }) + eventDataList, + }); - this.props.onChange({eventDataList,eventList}); + this.props.onChange({ eventDataList, eventList }); - //this.closeDialog(); + // this.closeDialog(); }; render() { @@ -374,15 +392,14 @@ export default class EventsSetter extends Component<{ selectType, eventDataList, } = this.state; - const {editor} = this.props.field; - let showEventList = + const showEventList = lifeCycleEventList.length > 0 ? lifeCycleEventList : eventList; return (
{ - eventBtns.length>1 ?点击选择事件类型:点击绑定事件 + eventBtns.length > 1 ? 点击选择事件类型 : 点击绑定事件 }
@@ -400,7 +417,7 @@ export default class EventsSetter extends Component<{ className="event-menu" onItemClick={this.onEventMenuClick} > - {showEventList.map((item, index) => ( + {showEventList.map((item) => ( {nativeEventList.map((item, index) => ( - {item.eventList.map(item => ( - - {item.name} + {item.eventList.map(groupItem => ( + + {groupItem.name} ))} diff --git a/packages/editor-setters/src/expression-setter/index.tsx b/packages/editor-setters/src/expression-setter/index.tsx index 94b9a824e..d1df5ea1f 100644 --- a/packages/editor-setters/src/expression-setter/index.tsx +++ b/packages/editor-setters/src/expression-setter/index.tsx @@ -1,6 +1,6 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { Select, Balloon, Icon } from '@alife/next'; +import { Select, Balloon } from '@alife/next'; import * as acorn from 'acorn'; import { isJSExpression, generateI18n } from './locale/utils'; @@ -12,15 +12,15 @@ const { Option, AutoComplete } = Select; const { Tooltip } = Balloon; const helpMap = { this: '容器上下文对象', - 'state': '容器的state', - 'props': '容器的props', - 'context': '容器的context', - 'schema': '页面上下文对象', - 'component': '组件上下文对象', - 'constants': '应用常量对象', - 'utils': '应用工具对象', - 'dataSourceMap': '容器数据源Map', - 'field': '表单Field对象' + state: '容器的state', + props: '容器的props', + context: '容器的context', + schema: '页面上下文对象', + component: '组件上下文对象', + constants: '应用常量对象', + utils: '应用工具对象', + dataSourceMap: '容器数据源Map', + field: '表单Field对象', }; export default class ExpressionView extends PureComponent { @@ -68,13 +68,12 @@ export default class ExpressionView extends PureComponent { return val; } - constructor(props: Readonly<{}>) { + constructor(props: any) { super(props); this.expression = React.createRef(); this.i18n = generateI18n(props.locale, props.messages); this.state = { value: ExpressionView.getInitValue(props.value), - context: props.context || {}, dataSource: props.dataSource || [], }; } @@ -126,31 +125,15 @@ export default class ExpressionView extends PureComponent { * @param {String} * @return {Array} */ - getDataSource(tempStr: string): any[] { - const {editor} = this.props.field; + getDataSource(): any[] { + const { editor } = this.props.field; const schema = editor.get('designer').project.getSchema(); const stateMap = schema.componentsTree[0].state; - let dataSource = []; + const dataSource = []; - for (let key in stateMap){ + for (const key in stateMap) { dataSource.push(`this.state.${key}`); } - // if (/[^\w\.]$/.test(tempStr)) { - // return []; - // } else if (tempStr === null || tempStr === '') { - // return this.getContextKeys([]); - // } else if (/\w\.$/.test(tempStr)) { - // const currentField = this.getCurrentFiled(tempStr); - // if (!currentField) return null; - // let tempKeys = this.getObjectKeys(currentField.str); - // tempKeys = this.getContextKeys(tempKeys); - // if (!tempKeys) return null; - // return tempKeys; - // } else if (/\.$/.test(tempStr)) { - // return []; - // } else { - // return null; - // } return dataSource; } @@ -283,12 +266,11 @@ export default class ExpressionView extends PureComponent { innerBefore={{'{{'}} innerAfter={{'}}'}} popupClassName="expression-setter-item-inner" - itemRender={({ value }) => { - console.log('111:'+value); + itemRender={({ itemValue }) => { return ( - ); }} @@ -312,6 +294,7 @@ export default class ExpressionView extends PureComponent { const isMoveKey = !!(event.type == 'keyup' && ~[37, 38, 39, 91].indexOf(event.keyCode)); const isMouseup = event.type == 'mouseup'; if (isMoveKey || isMouseup) { + // eslint-disable-next-line react/no-access-state-in-setstate const dataSource = this.getDataSource(this.state.value) || []; this.setState({ dataSource, diff --git a/packages/editor-setters/src/expression-setter/locale/snippets.ts b/packages/editor-setters/src/expression-setter/locale/snippets.ts index 7c8484c4f..2bcb0dd00 100644 --- a/packages/editor-setters/src/expression-setter/locale/snippets.ts +++ b/packages/editor-setters/src/expression-setter/locale/snippets.ts @@ -4,21 +4,21 @@ export default [ kind: 'Class', insertText: 'constants', detail: '应用全局常量', - documentation: '应用范围定义的通用常量' + documentation: '应用范围定义的通用常量', }, { label: 'utils', kind: 'Class', insertText: 'utils', detail: '应用全局公共函数', - documentation: '应用范围扩展的公共函数' + documentation: '应用范围扩展的公共函数', }, { label: 'state', kind: 'Enum', insertText: 'state', detail: '当前所在容器组件内部状态', - documentation: 'React Class内部状态state' + documentation: 'React Class内部状态state', }, { label: 'setState', @@ -26,7 +26,7 @@ export default [ insertText: 'setState({\n\t$0\n})', insertTextRules: 'InsertAsSnippet', detail: '设置当前所在容器组件的state数据', - documentation: '原生React方法,会自动更新组件视图' + documentation: '原生React方法,会自动更新组件视图', }, { label: 'reloadDataSource', @@ -34,45 +34,45 @@ export default [ insertText: 'reloadDataSource(${1:${2:namespace}, ${3:false}, ${4:callback}})', insertTextRules: 'InsertAsSnippet', detail: '刷新当前所在的容器组件', - documentation: '触发当前所在的容器组件,重新发送异步请求,并用最新数据更新视图' + documentation: '触发当前所在的容器组件,重新发送异步请求,并用最新数据更新视图', }, { label: 'location', kind: 'Class', insertText: 'location', - detail: '路由解析对象' + detail: '路由解析对象', }, { label: 'location.query', kind: 'Value', insertText: 'location.query.${1:xxxx}', insertTextRules: 'InsertAsSnippet', - detail: '从路由解析对象中获取参数信息' + detail: '从路由解析对象中获取参数信息', }, { label: 'history', kind: 'Class', insertText: 'history', - detail: '路由历史对象' + detail: '路由历史对象', }, { label: 'React', kind: 'Keyword', insertText: 'React', - detail: 'React对象' + detail: 'React对象', }, { label: 'ReactDOM', kind: 'Keyword', insertText: 'ReactDOM', - detail: 'ReactDom对象' + detail: 'ReactDom对象', }, { label: 'ReactDOM.findDOMNode', kind: 'Function', insertText: 'ReactDOM.findDOMNode(${1:this.refs.xxxx})', insertTextRules: 'InsertAsSnippet', - detail: 'ReactDom查找真实dom node' + detail: 'ReactDom查找真实dom node', }, { label: 'Dialog.alert', @@ -84,10 +84,10 @@ export default [ '\tonOk: () => {', '\t\t$3', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: 'alert弹框 By Fusion' + detail: 'alert弹框 By Fusion', }, { label: 'Dialog.confirm', @@ -102,52 +102,52 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '确认弹出框 By Fusion' + detail: '确认弹出框 By Fusion', }, { label: 'Message.success', kind: 'Method', insertText: 'Message.success(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '成功反馈提示 By Fusion' + detail: '成功反馈提示 By Fusion', }, { label: 'Message.error', kind: 'Method', insertText: 'Message.error(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '错误反馈提示 By Fusion' + detail: '错误反馈提示 By Fusion', }, { label: 'Message.help', kind: 'Method', insertText: 'Message.help(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '帮助反馈提示 By Fusion' + detail: '帮助反馈提示 By Fusion', }, { label: 'Message.loading', kind: 'Method', insertText: 'Message.loading(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: 'loading反馈提示 By Fusion' + detail: 'loading反馈提示 By Fusion', }, { label: 'Message.notice', kind: 'Method', insertText: 'Message.notice(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '注意反馈提示 By Fusion' + detail: '注意反馈提示 By Fusion', }, { label: 'Message.waining', kind: 'Method', insertText: 'Message.waining(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '警告反馈提示 By Fusion' + detail: '警告反馈提示 By Fusion', }, { label: 'Modal.confirm', @@ -162,10 +162,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '确认弹出框 By Antd' + detail: '确认弹出框 By Antd', }, { label: 'Modal.info', @@ -180,10 +180,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '信息弹出框 By Antd' + detail: '信息弹出框 By Antd', }, { label: 'Modal.success', @@ -198,10 +198,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '成功弹出框 By Antd' + detail: '成功弹出框 By Antd', }, { label: 'Modal.error', @@ -216,10 +216,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '错误弹出框 By Antd' + detail: '错误弹出框 By Antd', }, { label: 'Modal.warning', @@ -234,9 +234,9 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '警告弹出框 By Antd' - } -]; \ No newline at end of file + detail: '警告弹出框 By Antd', + }, +]; diff --git a/packages/editor-setters/src/expression-setter/locale/utils.ts b/packages/editor-setters/src/expression-setter/locale/utils.ts index a02a8dd06..d401ad511 100644 --- a/packages/editor-setters/src/expression-setter/locale/utils.ts +++ b/packages/editor-setters/src/expression-setter/locale/utils.ts @@ -1,11 +1,11 @@ import IntlMessageFormat from 'intl-messageformat'; export const isJSExpression = (obj = '') => { - if(obj && typeof obj === 'object' && obj.type === 'JSExpression') { + if (obj && typeof obj === 'object' && obj.type === 'JSExpression') { return true; } return false; -} +}; /** * 用于构造国际化字符串处理函数 @@ -18,4 +18,4 @@ export const generateI18n = (locale = 'zh-CN', messages = {}) => { const formater = new IntlMessageFormat(messages[key], locale); return formater.format(values); }; -} +}; diff --git a/packages/editor-setters/src/expression-setter/locale/zh-CN.ts b/packages/editor-setters/src/expression-setter/locale/zh-CN.ts index 951596009..f4f2e0630 100644 --- a/packages/editor-setters/src/expression-setter/locale/zh-CN.ts +++ b/packages/editor-setters/src/expression-setter/locale/zh-CN.ts @@ -33,4 +33,4 @@ export default { object: '对象ObjectButton', reactNode: '节点类型ReactNode', typeError: 'Minix组件属性Types配置错误,存在不支持类型[{type}],请检查组件属性配置', -}; \ No newline at end of file +}; diff --git a/packages/editor-setters/src/function-setter/index.tsx b/packages/editor-setters/src/function-setter/index.tsx index 397e131c5..003c45e68 100644 --- a/packages/editor-setters/src/function-setter/index.tsx +++ b/packages/editor-setters/src/function-setter/index.tsx @@ -1,12 +1,11 @@ import React, { PureComponent } from 'react'; // import PropTypes from 'prop-types'; -import { Button, Icon,Dialog } from '@alifd/next'; +import { Button, Icon, Dialog } from '@alifd/next'; import MonacoEditor from 'react-monaco-editor'; -import { js_beautify, css_beautify } from 'js-beautify'; +import { js_beautify } from 'js-beautify'; import './index.scss'; -import { timingSafeEqual } from 'crypto'; -const SETTER_NAME = 'function-setter' +const SETTER_NAME = 'function-setter'; const defaultEditorOption = { width: '100%', @@ -14,7 +13,7 @@ const defaultEditorOption = { options: { readOnly: false, automaticLayout: true, - folding: true, //默认开启折叠代码功能 + folding: true, // 默认开启折叠代码功能 lineNumbers: 'on', wordWrap: 'off', formatOnPaste: true, @@ -40,103 +39,100 @@ interface FunctionSetterProps { defaultValue: string; placeholder: string; hasClear: boolean; - onChange: (icon: string | object) => undefined; + onChange: (icon: string) => undefined; icons: string[]; } -export default class FunctionSetter extends PureComponent { +export default class FunctionSetter extends PureComponent { static defaultProps = { value: undefined, type: 'string', defaultValue: '', hasClear: true, placeholder: '请点击选择 Icon', - onChange: (icon: string | object) => undefined, + onChange: () => undefined, }; private emitEventName = ''; state = { - firstLoad: true, - isShowDialog:false, - functionCode:null + isShowDialog: false, }; componentDidMount() { const { editor } = this.props.field; this.emitEventName = `${SETTER_NAME}-${this.props.field.id}`; - editor.on(`${this.emitEventName}.bindEvent`, this.bindEvent) + editor.on(`${this.emitEventName}.bindEvent`, this.bindEvent); } bindEvent = (eventName) => { this.bindEventCallback(eventName); - } + }; componentWillUnmount() { const { editor } = this.props.field; - editor.off(`${this.emitEventName}.bindEvent`, this.bindEvent) + editor.off(`${this.emitEventName}.bindEvent`, this.bindEvent); } bindFunction = () => { - const { field, value } = this.props; + const { field } = this.props; field.editor.emit('eventBindDialog.openDialog', field.name, this.emitEventName); - } + }; openDialog = () => { - const {value={}} = this.props; + const { value = {} } = this.props; this.setState({ - isShowDialog:true - }) + isShowDialog: true, + }); this.functionCode = value.value; - - } + }; closeDialog = () => { this.setState({ - isShowDialog:false - }) - } + isShowDialog: false, + }); + }; removeFunctionBind = () => { - const { field ,removeProp} = this.props; + const { removeProp } = this.props; removeProp(); - } + }; - parseFunctionName = (functionString: String) => { + parseFunctionName = (functionString: string) => { // 因为函数格式是固定的,所以可以按照字符换去匹配获取函数名 - let funNameStr = functionString.split('this.')[1]; + const funNameStr = functionString.split('this.')[1]; - if (funNameStr){ - let endIndex = funNameStr.indexOf('('); + if (funNameStr) { + const endIndex = funNameStr.indexOf('('); return funNameStr.substr(0, endIndex); - }else{ - return '' + } else { + return ''; } - } + }; /** * 渲染按钮(初始状态) */ renderButton = () => { - return - } + return ; + }; updateCode = (newCode) => { this.functionCode = newCode; - } + }; onDialogOk = () => { - const {onChange} = this.props; + const { onChange } = this.props; onChange({ type: 'JSFunction', - value: this.functionCode + value: this.functionCode, }); this.closeDialog(); - } + }; focusFunctionName = (functionName) => { const { editor } = this.props.field; @@ -145,73 +141,90 @@ export default class FunctionSetter extends PureComponent { editor.emit('sourceEditor.focusByFunction', { - functionName - }) - }, 300) - } + functionName, + }); + }, 300); + }; /** * 渲染绑定函数 */ renderBindFunction = () => { const { value } = this.props; - + // 解析函数名 - let functionName = this.parseFunctionName(value.value); - return
- - this.focusFunctionName(functionName)}>{functionName} - - -
- } + const functionName = this.parseFunctionName(value.value); + return ( +
+ + this.focusFunctionName(functionName)}>{functionName} + + +
+ ); + }; /** * 渲染编辑函数按钮(可直接编辑函数内容) */ renderEditFunctionButton = () => { - return
- + return ( +
+
- } + ); + }; - bindEventCallback = (eventName: String) => { + bindEventCallback = (eventName: string) => { const { onChange } = this.props; onChange({ type: 'JSFunction', value: `function(){ this.${eventName}() }`, }); - } + }; render() { const { value } = this.props; - const {isShowDialog} = this.state; + const { isShowDialog } = this.state; let functionName = ''; - if (value && value.value){ + if (value && value.value) { functionName = this.parseFunctionName(value.value); } - return
- { - value ? (functionName?this.renderBindFunction():this.renderEditFunctionButton()) : this.renderButton() + + let renderFunction; + if (value) { + if (functionName) { + renderFunction = this.renderBindFunction; + } else { + renderFunction = this.renderEditFunctionButton; + } + } else { + renderFunction = this.renderButton; + } + + return ( +
+ { + renderFunction() } - { - value && value.value && {this.closeDialog()}}> -
- { this.closeDialog(); }}> +
+ this.updateCode(newCode)} /> -
+
} - - -
; +
+ ); } } diff --git a/packages/editor-setters/src/icon-setter/index.tsx b/packages/editor-setters/src/icon-setter/index.tsx index 0762b9ff3..f61c32271 100644 --- a/packages/editor-setters/src/icon-setter/index.tsx +++ b/packages/editor-setters/src/icon-setter/index.tsx @@ -68,18 +68,18 @@ interface IconSetterProps { defaultValue: string; placeholder: string; hasClear: boolean; - onChange: (icon: string | object) => undefined; + onChange: (icon: string) => undefined; icons: string[]; } -export default class IconSetter extends PureComponent { +export default class IconSetter extends PureComponent { static defaultProps = { value: undefined, type: 'string', defaultValue: '', hasClear: true, - icons: icons, + icons, placeholder: '请点击选择 Icon', - onChange: (icon: string | object) => undefined, + onChange: () => undefined, }; state = { @@ -109,7 +109,7 @@ export default class IconSetter extends PureComponent { }; render() { - const { icons, value, defaultValue, onChange, placeholder, hasClear, type } = this.props; + const { value, defaultValue, onChange, placeholder, hasClear } = this.props; const { firstLoad } = this.state; const _value = typeof value === 'object' ? value?.props?.type : value; if (firstLoad && defaultValue && typeof value === 'undefined') { @@ -149,7 +149,7 @@ export default class IconSetter extends PureComponent { { @@ -67,9 +68,11 @@ class StringDateSetter extends Component { ); } } + +// eslint-disable-next-line react/no-multi-comp class StringTimePicker extends Component { render() { - const { onChange, editor } = this.props; + const { onChange } = this.props; return ( { @@ -80,7 +83,7 @@ class StringTimePicker extends Component { } } -const VariableSetter ={ +const VariableSetter = { component: ExpressionSetter, condition: (field: any) => { const v = field.getValue(); @@ -99,7 +102,7 @@ const FunctionBindSetter = { const v = field.getValue(); return v == isJSFunction(v); }, -} +}; const builtinSetters: any = { StringSetter, @@ -120,7 +123,7 @@ const builtinSetters: any = { JsonSetter, StyleSetter, IconSetter, - FunctionSetter:FunctionBindSetter + FunctionSetter: FunctionBindSetter, }; registerSetter(builtinSetters); diff --git a/packages/editor-setters/src/json-setter/index.tsx b/packages/editor-setters/src/json-setter/index.tsx index 7ae9b80d0..ae08d750d 100644 --- a/packages/editor-setters/src/json-setter/index.tsx +++ b/packages/editor-setters/src/json-setter/index.tsx @@ -14,11 +14,12 @@ import Snippets from './locale/snippets'; import zhCN from './locale/zh-CN'; import './index.scss'; -let registerApiAndSnippetStatus = false; //判断注册api机制 +let registerApiAndSnippetStatus = false; // 判断注册api机制 window.bt = js_beautify; class MonacoEditorView extends PureComponent { static displayName = 'MonacoEditor'; + render() { const { type, ...restProps } = this.props; const Node = type == 'button' ? MonacoEditorButtonView : MonacoEditorDefaultView; @@ -28,30 +29,33 @@ class MonacoEditorView extends PureComponent { localeConfig('MonacoEditor', MonacoEditorView); -//monaco编辑器存在3种主题:vs、vs-dark、hc-black +// monaco编辑器存在3种主题:vs、vs-dark、hc-black +// eslint-disable-next-line react/no-multi-comp class MonacoEditorDefaultView extends PureComponent { static displayName = 'MonacoEditorDefault'; + static propTypes = { locale: PropTypes.string, messages: PropTypes.object, language: PropTypes.string, }; + static defaultProps = { locale: 'zh-CN', messages: zhCN, width: '100%', height: '300px', language: 'json', - autoFocus: false, //自动获得焦点 - autoSubmit: true, //自动提交 - placeholder: '', //默认占位内容 + autoFocus: false, // 自动获得焦点 + autoSubmit: true, // 自动提交 + placeholder: '', // 默认占位内容 btnText: '提交', btnSize: 'small', - rules: [], //校验规则 + rules: [], // 校验规则 options: { readOnly: false, automaticLayout: true, - folding: true, //默认开启折叠代码功能 + folding: true, // 默认开启折叠代码功能 lineNumbers: 'on', wordWrap: 'off', formatOnPaste: true, @@ -70,16 +74,26 @@ class MonacoEditorDefaultView extends PureComponent { }, }, }; + strValue: string; + i18n: any; + editorRef: React.RefObject; + options: any; + fullScreenOptions: any; + position: any; + editor: any; + editorNode: unknown; + editorParentNode: any; - constructor(props: Readonly<{}>) { + + constructor(props: Readonly) { super(props); this.strValue = ''; this.i18n = generateI18n(props.locale, props.messages); @@ -104,17 +118,18 @@ class MonacoEditorDefaultView extends PureComponent { } componentDidUpdate() { - //如果是全屏操作,获得焦点,光标保留在原来位置; + // 如果是全屏操作,获得焦点,光标保留在原来位置; if (this.position) { this.editor.focus(); this.editor.setPosition(this.position); delete this.position; } } + componentDidMount() { - this.editorNode = this.editorRef.current; //记录当前dom节点; - this.editorParentNode = this.editorNode.parentNode; //记录父节点; - //自动获得焦点, 格式化需要时间 + this.editorNode = this.editorRef.current; // 记录当前dom节点; + this.editorParentNode = this.editorNode.parentNode; // 记录父节点; + // 自动获得焦点, 格式化需要时间 if (this.props.autoFocus) { setTimeout(() => { this.editor.setPosition({ @@ -124,7 +139,7 @@ class MonacoEditorDefaultView extends PureComponent { this.editor.focus(); }, 100); } - //快捷键编码 + // 快捷键编码 const CtrlCmd = 2048; const KEY_S = 49; const Shift = 1024; @@ -133,28 +148,28 @@ class MonacoEditorDefaultView extends PureComponent { const Escape = 9; this.editor.addCommand(CtrlCmd | KEY_S, () => { - this.onSubmit(); //保存快捷键 + this.onSubmit(); // 保存快捷键 }); this.editor.addCommand(CtrlCmd | Shift | KEY_F, () => { - this.fullScreen(); //全屏快捷键 + this.fullScreen(); // 全屏快捷键 }); this.editor.addCommand(CtrlCmd | KEY_B, () => { - this.format(); //美化快捷键 + this.format(); // 美化快捷键 }); this.editor.addCommand(Escape, () => { this.props.onEscape && this.props.onEscape(); }); - //注册api + // 注册api this.editor.submit = this.onSubmit; this.editor.format = this.format; this.editor.fullScreen = this.fullScreen; this.editor.toJson = this.toJson; this.editor.toObject = this.toObject; this.editor.toFunction = this.toFunction; - //针对object情况,改写setValue和getValue api + // 针对object情况,改写setValue和getValue api if (this.props.language === 'object') { - const getValue = this.editor.getValue; - const setValue = this.editor.setValue; + const { getValue } = this.editor; + const { setValue } = this.editor; this.editor.getValue = () => { return getValue.call(this.editor).substring(this.valuePrefix.length); }; @@ -180,18 +195,18 @@ class MonacoEditorDefaultView extends PureComponent { } = this.props; const { isFullScreen } = this.state; - this.valuePrefix = ''; //值前缀 + this.valuePrefix = ''; // 值前缀 if (language === 'object') this.valuePrefix = 'export default '; if (!this.isFullScreenAction) { - //将值转换成目标值 + // 将值转换成目标值 const nowValue = this.valueHandler(value || placeholder, language); const curValue = this.valueHandler(this.strValue, language); if (nowValue !== curValue) this.strValue = nowValue; - if (language === 'object') this.strValue = this.strValue || placeholder || '{\n\t\n}'; //设置初始化值 + if (language === 'object') this.strValue = this.strValue || placeholder || '{\n\t\n}'; // 设置初始化值 if (language === 'json' && this.strValue === '{}') this.strValue = '{\n\t\n}'; } this.isFullScreenAction = false; - //真实高亮语言 + // 真实高亮语言 let tarLanguage = language; if (language === 'object' || language === 'function') { tarLanguage = 'javascript'; @@ -242,11 +257,11 @@ class MonacoEditorDefaultView extends PureComponent { ); } - //值变化 + // 值变化 onChange(curValue) { if (curValue === this.valuePrefix + this.strValue) return; const { onAfterChange, language, autoSubmit, onChange } = this.props; - this.strValue = curValue; //记录当前格式 + this.strValue = curValue; // 记录当前格式 if (this.ct) clearTimeout(this.ct); this.ct = setTimeout(() => { this.position = this.editor.getPosition(); @@ -256,7 +271,7 @@ class MonacoEditorDefaultView extends PureComponent { }, 300); } - //提交动作 + // 提交动作 onSubmit() { const { onSubmit, onChange, language } = this.props; const curValue = this.editor.getValue(); @@ -265,7 +280,7 @@ class MonacoEditorDefaultView extends PureComponent { onSubmit && onSubmit(ret.value, ret.error, this.editor); } - //值类型转换处理 + // 值类型转换处理 valueHandler(value, language) { let tarValue = value || ''; if (language === 'json') { @@ -274,8 +289,12 @@ class MonacoEditorDefaultView extends PureComponent { } else if (value && typeof value === 'string') { try { const ret = this.toJson(value); - if (!ret.error) tarValue = JSON.stringify(ret.value, null, 2); - } catch (err) {} + if (!ret.error) { + tarValue = JSON.stringify(ret.value, null, 2); + } + } catch (err) { + // empty + } } } else if (language === 'function') { if (typeof value === 'function') { @@ -285,25 +304,29 @@ class MonacoEditorDefaultView extends PureComponent { tarValue = js_beautify(tarValue, { indent_size: 2, indent_empty_lines: true }); } } else if (language === 'object') { - //先转成对象,在进行序列化和格式化; + // 先转成对象,在进行序列化和格式化; value = value || {}; if (value && typeof value === 'object') { try { tarValue = serialize(value, { unsafe: true }); tarValue = js_beautify(tarValue, { indent_size: 2, indent_empty_lines: true }); - } catch (err) {} + } catch (err) { + // empty + } } else if (typeof value === 'string') { try { const ret = this.resultHandler(value, 'object'); tarValue = ret.error ? ret.value : serialize(ret.value, { unsafe: true }); tarValue = js_beautify(tarValue, { indent_size: 2, indent_empty_lines: true }); - } catch (err) {} + } catch (err) { + // empty + } } } return tarValue; } - //结果处理 + // 结果处理 resultHandler(value, language) { let ret = { value }; if (language === 'json') { @@ -316,10 +339,10 @@ class MonacoEditorDefaultView extends PureComponent { return ret; } - //设置全屏时的动作 + // 设置全屏时的动作 fullScreen() { if (!this.editorRef) return; - //还原到原来位置; + // 还原到原来位置; this.position = this.editor.getPosition(); if (this.state.isFullScreen) { if (this.editorParentNode) { @@ -332,8 +355,9 @@ class MonacoEditorDefaultView extends PureComponent { } else { document.body.appendChild(this.editorNode); } + // eslint-disable-next-line react/no-access-state-in-setstate const nextFs = !this.state.isFullScreen; - this.isFullScreenAction = true; //记录是全屏幕操作 + this.isFullScreenAction = true; // 记录是全屏幕操作 this.setState( { isFullScreen: nextFs, @@ -344,9 +368,8 @@ class MonacoEditorDefaultView extends PureComponent { ); } - //美化代码 + // 美化代码 format() { - const { language } = this.props; if (!this.editor) return; if (/^\$_obj?\{.*?\}$/m.test(this.editor.getValue())) return; if (this.props.language === 'json' || this.props.language === 'object' || this.props.language === 'function') { @@ -360,11 +383,13 @@ class MonacoEditorDefaultView extends PureComponent { } } - //校验是否是json + // 校验是否是json toJson(value) { try { + // eslint-disable-next-line no-new-func const obj = new Function(`'use strict'; return ${value.replace(/[\r\n\t]/g, '')}`)(); if (typeof obj === 'object' && obj) { + // eslint-disable-next-line no-new-func const tarValue = new Function(`'use strict'; return ${value}`)(); return { value: JSON.parse(JSON.stringify(tarValue)) }; } @@ -374,9 +399,10 @@ class MonacoEditorDefaultView extends PureComponent { } } - //校验是否为object对象 + // 校验是否为object对象 toObject(value) { try { + // eslint-disable-next-line no-new-func const obj = new Function(`'use strict';return ${value}`)(); if (obj && typeof obj === 'object') { if (jsonuri.isCircular(obj)) return { error: this.i18n('circularRef'), value }; @@ -389,9 +415,10 @@ class MonacoEditorDefaultView extends PureComponent { } } - //校验是否为function + // 校验是否为function toFunction(value) { try { + // eslint-disable-next-line no-new-func const fun = new Function(`'use strict';return ${value}`)(); if (fun && typeof fun === 'function') { return { value: fun }; @@ -403,11 +430,11 @@ class MonacoEditorDefaultView extends PureComponent { } } - //注册api和代码片段 + // 注册api和代码片段 registerApiAndSnippet(monaco) { if (registerApiAndSnippetStatus) return; registerApiAndSnippetStatus = true; - //注册this.提示的方法; + // 注册this.提示的方法; const thisSuggestions = []; Snippets.map((item) => { if (!item.label || !item.kind || !item.insertText) return; @@ -416,9 +443,9 @@ class MonacoEditorDefaultView extends PureComponent { kind: monaco.languages.CompletionItemKind[item.kind], insertText: item.insertText, }); - if (item.insertTextRules) - tarItem.insertTextRules = monaco.languages.CompletionItemInsertTextRule[item.insertTextRules]; + if (item.insertTextRules) tarItem.insertTextRules = monaco.languages.CompletionItemInsertTextRule[item.insertTextRules]; thisSuggestions.push(tarItem); + return item; }); monaco.languages.registerCompletionItemProvider('javascript', { provideCompletionItems: (model, position) => { @@ -430,7 +457,7 @@ class MonacoEditorDefaultView extends PureComponent { }); const match = textUntilPosition.match(/(^this\.)|(\sthis\.)/); const suggestions = match ? thisSuggestions : []; - return { suggestions: suggestions }; + return { suggestions }; }, triggerCharacters: ['.'], }); @@ -439,7 +466,7 @@ class MonacoEditorDefaultView extends PureComponent { const prefix = 'data:text/javascript;charset=utf-8,'; const baseUrl = 'https://g.alicdn.com/iceluna/iceluna-vendor/0.0.1/'; window.MonacoEnvironment = { - getWorkerUrl: function(label: string) { + getWorkerUrl(label: string) { if (label === 'json') { return `${prefix}${encodeURIComponent(` importScripts('${baseUrl}json.worker.js');`)}`; @@ -461,33 +488,42 @@ window.MonacoEnvironment = { }, }; +// eslint-disable-next-line react/no-multi-comp export default class MonacoEditorButtonView extends PureComponent { static displayName = 'JsonSetter'; + static propTypes = { locale: PropTypes.string, messages: PropTypes.object, }; + static defaultProps = { locale: 'zh-CN', messages: zhCN, }; + i18n: any; + objectButtonRef: React.RefObject; - constructor(props: Readonly<{}>) { + + constructor(props: Readonly) { super(props); this.i18n = generateI18n(props.locale, props.messages); this.objectButtonRef = React.createRef(); // 兼容代码,待去除 window.__ctx.appHelper.constants = window.__ctx.appHelper.constants || {}; } + afterHandler(value: { nrs_temp_field: any }) { if (!value) return; return value.nrs_temp_field; } + beforeHandler(value: any) { if (!value) return; return { nrs_temp_field: value }; } + message(type: string, title: any, dom: Element | null) { Message.show({ type, @@ -499,6 +535,7 @@ export default class MonacoEditorButtonView extends PureComponent { }, }); } + componentDidMount() { const { registerApi } = this.props; const objectButtonThis = this.objectButtonRef; @@ -510,6 +547,7 @@ export default class MonacoEditorButtonView extends PureComponent { setValues: objectButtonThis.setValues, }); } + render() { const self = this; const { locale, messages, value, onChange, field, languages, ...restProps } = this.props; @@ -518,8 +556,8 @@ export default class MonacoEditorButtonView extends PureComponent { tarRestProps.autoSubmit = true; tarRestProps.autoFocus = true; const tarOnSubmit = tarRestProps.onSubmit; - //确保monaco快捷键保存,能出发最外层的保存 - tarRestProps.onSubmit = (value, error) => { + // 确保monaco快捷键保存,能出发最外层的保存 + tarRestProps.onSubmit = (editorValue, error) => { const msgDom = document.querySelector('.object-button-overlay .next-dialog-body'); if (error) return this.message('error', this.i18n('formatError'), msgDom); this.objectButtonRef && @@ -539,12 +577,12 @@ export default class MonacoEditorButtonView extends PureComponent { tarObjProps.value = value || ''; tarObjProps.onChange = onChange; const tarRule = []; - //判断,如果是json,function, object等类型,自动追加校验规则; + // 判断,如果是json,function, object等类型,自动追加校验规则; if (tarRestProps.language && ['json', 'function', 'object'].includes(tarRestProps.language)) { if (['json', 'object'].includes(tarRestProps.language)) { tarRule.push({ - validator: function(value: any, callback: (arg0: undefined) => void) { - if (typeof value !== 'object') { + validator(validatorValue: any, callback: (arg0: undefined) => void) { + if (typeof validatorValue !== 'object') { callback(self.i18n('formatError')); } else { callback(); @@ -553,8 +591,8 @@ export default class MonacoEditorButtonView extends PureComponent { }); } else { tarRule.push({ - validator: function(value: any, callback: (arg0: undefined) => void) { - if (typeof value !== 'function') { + validator(validatorValue: any, callback: (arg0: undefined) => void) { + if (typeof validatorValue !== 'function') { callback(self.i18n('formatError')); } else { callback(); diff --git a/packages/editor-setters/src/json-setter/locale/snippets.ts b/packages/editor-setters/src/json-setter/locale/snippets.ts index 7c8484c4f..2bcb0dd00 100644 --- a/packages/editor-setters/src/json-setter/locale/snippets.ts +++ b/packages/editor-setters/src/json-setter/locale/snippets.ts @@ -4,21 +4,21 @@ export default [ kind: 'Class', insertText: 'constants', detail: '应用全局常量', - documentation: '应用范围定义的通用常量' + documentation: '应用范围定义的通用常量', }, { label: 'utils', kind: 'Class', insertText: 'utils', detail: '应用全局公共函数', - documentation: '应用范围扩展的公共函数' + documentation: '应用范围扩展的公共函数', }, { label: 'state', kind: 'Enum', insertText: 'state', detail: '当前所在容器组件内部状态', - documentation: 'React Class内部状态state' + documentation: 'React Class内部状态state', }, { label: 'setState', @@ -26,7 +26,7 @@ export default [ insertText: 'setState({\n\t$0\n})', insertTextRules: 'InsertAsSnippet', detail: '设置当前所在容器组件的state数据', - documentation: '原生React方法,会自动更新组件视图' + documentation: '原生React方法,会自动更新组件视图', }, { label: 'reloadDataSource', @@ -34,45 +34,45 @@ export default [ insertText: 'reloadDataSource(${1:${2:namespace}, ${3:false}, ${4:callback}})', insertTextRules: 'InsertAsSnippet', detail: '刷新当前所在的容器组件', - documentation: '触发当前所在的容器组件,重新发送异步请求,并用最新数据更新视图' + documentation: '触发当前所在的容器组件,重新发送异步请求,并用最新数据更新视图', }, { label: 'location', kind: 'Class', insertText: 'location', - detail: '路由解析对象' + detail: '路由解析对象', }, { label: 'location.query', kind: 'Value', insertText: 'location.query.${1:xxxx}', insertTextRules: 'InsertAsSnippet', - detail: '从路由解析对象中获取参数信息' + detail: '从路由解析对象中获取参数信息', }, { label: 'history', kind: 'Class', insertText: 'history', - detail: '路由历史对象' + detail: '路由历史对象', }, { label: 'React', kind: 'Keyword', insertText: 'React', - detail: 'React对象' + detail: 'React对象', }, { label: 'ReactDOM', kind: 'Keyword', insertText: 'ReactDOM', - detail: 'ReactDom对象' + detail: 'ReactDom对象', }, { label: 'ReactDOM.findDOMNode', kind: 'Function', insertText: 'ReactDOM.findDOMNode(${1:this.refs.xxxx})', insertTextRules: 'InsertAsSnippet', - detail: 'ReactDom查找真实dom node' + detail: 'ReactDom查找真实dom node', }, { label: 'Dialog.alert', @@ -84,10 +84,10 @@ export default [ '\tonOk: () => {', '\t\t$3', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: 'alert弹框 By Fusion' + detail: 'alert弹框 By Fusion', }, { label: 'Dialog.confirm', @@ -102,52 +102,52 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '确认弹出框 By Fusion' + detail: '确认弹出框 By Fusion', }, { label: 'Message.success', kind: 'Method', insertText: 'Message.success(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '成功反馈提示 By Fusion' + detail: '成功反馈提示 By Fusion', }, { label: 'Message.error', kind: 'Method', insertText: 'Message.error(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '错误反馈提示 By Fusion' + detail: '错误反馈提示 By Fusion', }, { label: 'Message.help', kind: 'Method', insertText: 'Message.help(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '帮助反馈提示 By Fusion' + detail: '帮助反馈提示 By Fusion', }, { label: 'Message.loading', kind: 'Method', insertText: 'Message.loading(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: 'loading反馈提示 By Fusion' + detail: 'loading反馈提示 By Fusion', }, { label: 'Message.notice', kind: 'Method', insertText: 'Message.notice(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '注意反馈提示 By Fusion' + detail: '注意反馈提示 By Fusion', }, { label: 'Message.waining', kind: 'Method', insertText: 'Message.waining(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '警告反馈提示 By Fusion' + detail: '警告反馈提示 By Fusion', }, { label: 'Modal.confirm', @@ -162,10 +162,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '确认弹出框 By Antd' + detail: '确认弹出框 By Antd', }, { label: 'Modal.info', @@ -180,10 +180,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '信息弹出框 By Antd' + detail: '信息弹出框 By Antd', }, { label: 'Modal.success', @@ -198,10 +198,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '成功弹出框 By Antd' + detail: '成功弹出框 By Antd', }, { label: 'Modal.error', @@ -216,10 +216,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '错误弹出框 By Antd' + detail: '错误弹出框 By Antd', }, { label: 'Modal.warning', @@ -234,9 +234,9 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '警告弹出框 By Antd' - } -]; \ No newline at end of file + detail: '警告弹出框 By Antd', + }, +]; diff --git a/packages/editor-setters/src/json-setter/locale/utils.ts b/packages/editor-setters/src/json-setter/locale/utils.ts index a02a8dd06..d401ad511 100644 --- a/packages/editor-setters/src/json-setter/locale/utils.ts +++ b/packages/editor-setters/src/json-setter/locale/utils.ts @@ -1,11 +1,11 @@ import IntlMessageFormat from 'intl-messageformat'; export const isJSExpression = (obj = '') => { - if(obj && typeof obj === 'object' && obj.type === 'JSExpression') { + if (obj && typeof obj === 'object' && obj.type === 'JSExpression') { return true; } return false; -} +}; /** * 用于构造国际化字符串处理函数 @@ -18,4 +18,4 @@ export const generateI18n = (locale = 'zh-CN', messages = {}) => { const formater = new IntlMessageFormat(messages[key], locale); return formater.format(values); }; -} +}; diff --git a/packages/editor-setters/src/json-setter/locale/zh-CN.ts b/packages/editor-setters/src/json-setter/locale/zh-CN.ts index 951596009..f4f2e0630 100644 --- a/packages/editor-setters/src/json-setter/locale/zh-CN.ts +++ b/packages/editor-setters/src/json-setter/locale/zh-CN.ts @@ -33,4 +33,4 @@ export default { object: '对象ObjectButton', reactNode: '节点类型ReactNode', typeError: 'Minix组件属性Types配置错误,存在不支持类型[{type}],请检查组件属性配置', -}; \ No newline at end of file +}; diff --git a/packages/editor-setters/src/locale/snippets.ts b/packages/editor-setters/src/locale/snippets.ts index 7c8484c4f..2bcb0dd00 100644 --- a/packages/editor-setters/src/locale/snippets.ts +++ b/packages/editor-setters/src/locale/snippets.ts @@ -4,21 +4,21 @@ export default [ kind: 'Class', insertText: 'constants', detail: '应用全局常量', - documentation: '应用范围定义的通用常量' + documentation: '应用范围定义的通用常量', }, { label: 'utils', kind: 'Class', insertText: 'utils', detail: '应用全局公共函数', - documentation: '应用范围扩展的公共函数' + documentation: '应用范围扩展的公共函数', }, { label: 'state', kind: 'Enum', insertText: 'state', detail: '当前所在容器组件内部状态', - documentation: 'React Class内部状态state' + documentation: 'React Class内部状态state', }, { label: 'setState', @@ -26,7 +26,7 @@ export default [ insertText: 'setState({\n\t$0\n})', insertTextRules: 'InsertAsSnippet', detail: '设置当前所在容器组件的state数据', - documentation: '原生React方法,会自动更新组件视图' + documentation: '原生React方法,会自动更新组件视图', }, { label: 'reloadDataSource', @@ -34,45 +34,45 @@ export default [ insertText: 'reloadDataSource(${1:${2:namespace}, ${3:false}, ${4:callback}})', insertTextRules: 'InsertAsSnippet', detail: '刷新当前所在的容器组件', - documentation: '触发当前所在的容器组件,重新发送异步请求,并用最新数据更新视图' + documentation: '触发当前所在的容器组件,重新发送异步请求,并用最新数据更新视图', }, { label: 'location', kind: 'Class', insertText: 'location', - detail: '路由解析对象' + detail: '路由解析对象', }, { label: 'location.query', kind: 'Value', insertText: 'location.query.${1:xxxx}', insertTextRules: 'InsertAsSnippet', - detail: '从路由解析对象中获取参数信息' + detail: '从路由解析对象中获取参数信息', }, { label: 'history', kind: 'Class', insertText: 'history', - detail: '路由历史对象' + detail: '路由历史对象', }, { label: 'React', kind: 'Keyword', insertText: 'React', - detail: 'React对象' + detail: 'React对象', }, { label: 'ReactDOM', kind: 'Keyword', insertText: 'ReactDOM', - detail: 'ReactDom对象' + detail: 'ReactDom对象', }, { label: 'ReactDOM.findDOMNode', kind: 'Function', insertText: 'ReactDOM.findDOMNode(${1:this.refs.xxxx})', insertTextRules: 'InsertAsSnippet', - detail: 'ReactDom查找真实dom node' + detail: 'ReactDom查找真实dom node', }, { label: 'Dialog.alert', @@ -84,10 +84,10 @@ export default [ '\tonOk: () => {', '\t\t$3', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: 'alert弹框 By Fusion' + detail: 'alert弹框 By Fusion', }, { label: 'Dialog.confirm', @@ -102,52 +102,52 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '确认弹出框 By Fusion' + detail: '确认弹出框 By Fusion', }, { label: 'Message.success', kind: 'Method', insertText: 'Message.success(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '成功反馈提示 By Fusion' + detail: '成功反馈提示 By Fusion', }, { label: 'Message.error', kind: 'Method', insertText: 'Message.error(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '错误反馈提示 By Fusion' + detail: '错误反馈提示 By Fusion', }, { label: 'Message.help', kind: 'Method', insertText: 'Message.help(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '帮助反馈提示 By Fusion' + detail: '帮助反馈提示 By Fusion', }, { label: 'Message.loading', kind: 'Method', insertText: 'Message.loading(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: 'loading反馈提示 By Fusion' + detail: 'loading反馈提示 By Fusion', }, { label: 'Message.notice', kind: 'Method', insertText: 'Message.notice(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '注意反馈提示 By Fusion' + detail: '注意反馈提示 By Fusion', }, { label: 'Message.waining', kind: 'Method', insertText: 'Message.waining(${1:content})', insertTextRules: 'InsertAsSnippet', - detail: '警告反馈提示 By Fusion' + detail: '警告反馈提示 By Fusion', }, { label: 'Modal.confirm', @@ -162,10 +162,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '确认弹出框 By Antd' + detail: '确认弹出框 By Antd', }, { label: 'Modal.info', @@ -180,10 +180,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '信息弹出框 By Antd' + detail: '信息弹出框 By Antd', }, { label: 'Modal.success', @@ -198,10 +198,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '成功弹出框 By Antd' + detail: '成功弹出框 By Antd', }, { label: 'Modal.error', @@ -216,10 +216,10 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '错误弹出框 By Antd' + detail: '错误弹出框 By Antd', }, { label: 'Modal.warning', @@ -234,9 +234,9 @@ export default [ '\tonCancel: () => {', '\t\t$4', '\t}', - '})' + '})', ].join('\n'), insertTextRules: 'InsertAsSnippet', - detail: '警告弹出框 By Antd' - } -]; \ No newline at end of file + detail: '警告弹出框 By Antd', + }, +]; diff --git a/packages/editor-setters/src/locale/utils.ts b/packages/editor-setters/src/locale/utils.ts index a02a8dd06..d401ad511 100644 --- a/packages/editor-setters/src/locale/utils.ts +++ b/packages/editor-setters/src/locale/utils.ts @@ -1,11 +1,11 @@ import IntlMessageFormat from 'intl-messageformat'; export const isJSExpression = (obj = '') => { - if(obj && typeof obj === 'object' && obj.type === 'JSExpression') { + if (obj && typeof obj === 'object' && obj.type === 'JSExpression') { return true; } return false; -} +}; /** * 用于构造国际化字符串处理函数 @@ -18,4 +18,4 @@ export const generateI18n = (locale = 'zh-CN', messages = {}) => { const formater = new IntlMessageFormat(messages[key], locale); return formater.format(values); }; -} +}; diff --git a/packages/editor-setters/src/locale/zh-CN.ts b/packages/editor-setters/src/locale/zh-CN.ts index 951596009..f4f2e0630 100644 --- a/packages/editor-setters/src/locale/zh-CN.ts +++ b/packages/editor-setters/src/locale/zh-CN.ts @@ -33,4 +33,4 @@ export default { object: '对象ObjectButton', reactNode: '节点类型ReactNode', typeError: 'Minix组件属性Types配置错误,存在不支持类型[{type}],请检查组件属性配置', -}; \ No newline at end of file +}; diff --git a/packages/editor-setters/src/mixed-setter/index.tsx b/packages/editor-setters/src/mixed-setter/index.tsx index 7d288cc93..f99c1bec3 100644 --- a/packages/editor-setters/src/mixed-setter/index.tsx +++ b/packages/editor-setters/src/mixed-setter/index.tsx @@ -10,6 +10,7 @@ import './index.scss'; export default class Mixed extends PureComponent { static displayName = 'Mixed'; + static propTypes = { locale: PropTypes.string, messages: PropTypes.object, @@ -22,6 +23,7 @@ export default class Mixed extends PureComponent { selectProps: PropTypes.object, radioGroupProps: PropTypes.object, }; + static defaultProps = { locale: 'zh-CN', messages: zhCN, @@ -32,22 +34,25 @@ export default class Mixed extends PureComponent { }, ], }; + typeMap: any; - i18n: (key: any, values?: {}) => string | void | Array; - constructor(props: Readonly<{}>) { + + i18n: (key: any, values) => string | void | Array; + + constructor(props: Readonly) { super(props); const type = props.defaultType; // judgeTypeHandler(props, {}); this.i18n = generateI18n(props.locale, props.messages); this.state = { - preType: type, type, }; } + changeType(type: string) { if (typeof type === 'object' || type === this.state.type) return; const { onChange } = this.props; - let newValue = undefined; - const setterProps = this.typeMap[type]['props']; + let newValue; + const setterProps = this.typeMap[type].props; if (setterProps) { if (setterProps.value !== undefined) { newValue = setterProps.value; @@ -56,11 +61,12 @@ export default class Mixed extends PureComponent { } } if (type === 'BoolSetter' && newValue === undefined) { - newValue = false; //给切换到switch默认值为false + newValue = false; // 给切换到switch默认值为false } this.setState({ type }); onChange && onChange(newValue); } + render() { const { style = {}, className, locale, messages, types = [], defaultType, ...restProps } = this.props; this.typeMap = {}; @@ -78,7 +84,7 @@ export default class Mixed extends PureComponent { realTypes.push(name); }); let moreBtnNode = null; - //如果只有2种,且有变量表达式,则直接展示变量按钮 + // 如果只有2种,且有变量表达式,则直接展示变量按钮 if (realTypes.length > 1) { const isTwoType = !!(realTypes.length === 2 && ~realTypes.indexOf('ExpressionSetter')); const btnProps = { @@ -108,10 +114,11 @@ export default class Mixed extends PureComponent { if (isTwoType) { moreBtnNode = triggerNode; } else { + // eslint-disable-next-line @typescript-eslint/ban-types const MenuItems: {} | null | undefined = []; realTypes.map((type) => { if (this.typeMap[type]) { - MenuItems.push({this.typeMap[type]['label']}); + MenuItems.push({this.typeMap[type].label}); } else { console.error( this.i18n('typeError', { @@ -119,6 +126,7 @@ export default class Mixed extends PureComponent { }), ); } + return type; }); const MenuNode = ( { - if(obj && typeof obj === 'object' && obj.type === 'JSExpression') { + if (obj && typeof obj === 'object' && obj.type === 'JSExpression') { return true; } return false; -} +}; /** * 用于构造国际化字符串处理函数 @@ -18,4 +18,4 @@ export const generateI18n = (locale = 'zh-CN', messages = {}) => { const formater = new IntlMessageFormat(messages[key], locale); return formater.format(values); }; -} +}; diff --git a/packages/editor-setters/src/mixed-setter/locale/zh-CN.ts b/packages/editor-setters/src/mixed-setter/locale/zh-CN.ts index 951596009..f4f2e0630 100644 --- a/packages/editor-setters/src/mixed-setter/locale/zh-CN.ts +++ b/packages/editor-setters/src/mixed-setter/locale/zh-CN.ts @@ -33,4 +33,4 @@ export default { object: '对象ObjectButton', reactNode: '节点类型ReactNode', typeError: 'Minix组件属性Types配置错误,存在不支持类型[{type}],请检查组件属性配置', -}; \ No newline at end of file +}; diff --git a/packages/editor-setters/src/style-setter/index.tsx b/packages/editor-setters/src/style-setter/index.tsx index b6a75679c..004fe2bf1 100644 --- a/packages/editor-setters/src/style-setter/index.tsx +++ b/packages/editor-setters/src/style-setter/index.tsx @@ -5,25 +5,26 @@ import LowStyleSetter from '@ali/lc-style-setter'; import { globalLocale } from '@ali/lowcode-editor-core'; export default class StyleSetter extends Component { - static displayName = 'StyleSetter'; + static propTypes = { value: PropTypes.object, onChange: PropTypes.func, placeholder: PropTypes.string, - locale: PropTypes.string + locale: PropTypes.string, }; + static defaultProps = { value: {}, onChange: () => { }, placeholder: '', - locale: globalLocale.getLocale() || 'en-US' + locale: globalLocale.getLocale() || 'en-US', }; onChange = (val: any) => { const { onChange } = this.props; onChange(val.native); - } + }; render() { const { value } = this.props; @@ -33,5 +34,4 @@ export default class StyleSetter extends Component {
); } - } diff --git a/packages/editor-skeleton/.eslintrc.js b/packages/editor-skeleton/.eslintrc.js new file mode 100644 index 000000000..0430ed0a5 --- /dev/null +++ b/packages/editor-skeleton/.eslintrc.js @@ -0,0 +1,16 @@ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'react/no-multi-comp': 1, + 'no-unused-expressions': 1, + 'implicit-arrow-linebreak': 1, + 'no-nested-ternary': 1, + 'no-mixed-operators': 1, + '@typescript-eslint/no-parameter-properties': 1, + '@typescript-eslint/ban-types': 1, + 'no-shadow': 1, + 'no-prototype-builtins': 1, + 'no-confusing-arrow': 1, + 'no-case-declarations': 1, + } +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/area.ts b/packages/editor-skeleton/src/area.ts index d0f4b0f4f..6ff3b6e07 100644 --- a/packages/editor-skeleton/src/area.ts +++ b/packages/editor-skeleton/src/area.ts @@ -5,7 +5,7 @@ import { IWidget } from './widget/widget'; import { IWidgetBaseConfig } from './types'; export default class Area { - @obx private _visible: boolean = true; + @obx private _visible = true; @computed get visible() { if (this.exclusive) { @@ -22,7 +22,8 @@ export default class Area; - constructor(readonly skeleton: Skeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent: boolean = false) { + + constructor(readonly skeleton: Skeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent = false) { this.container = skeleton.createContainer(name, handle, exclusive, () => this.visible, defaultSetCurrent); } @@ -43,11 +44,12 @@ export default class Area { onSort(sortedIds: Array) { const { itemsMap } = this.state; - const { onChange ,itemSetter,field} = this.props; - const items = sortedIds.map((id, index) => { + const { onChange, itemSetter, field } = this.props; + const items = sortedIds.map((id) => { const item = itemsMap.get(id)!; // item.setKey(index); return item; @@ -85,7 +85,8 @@ export class ListSetter extends Component { }); // 对itemsMap重新生成并刷新当前setter数据 - let newItems = [],newItemsMap = {} + const newItems = []; + // const newItemsMap = {}; itemsMap.clear(); for (let i = 0; i < items.length; i++) { const newItem = field.createField({ @@ -101,12 +102,13 @@ export class ListSetter extends Component { onChange(values); this.setState({ - items:newItems, - itemsMap + items: newItems, + itemsMap, }); } private scrollToLast = false; + onAdd() { const { items, itemsMap } = this.state; const { itemSetter } = this.props; @@ -127,7 +129,7 @@ export class ListSetter extends Component { } onRemove(field: SettingField) { - const {onChange, itemSetter} = this.props; + const { onChange } = this.props; const { items, itemsMap } = this.state; let i = items.indexOf(field); const values = items.map((item) => { @@ -145,7 +147,7 @@ export class ListSetter extends Component { } itemsMap.delete(field.id); field.remove(); - onChange(values) + onChange(values); this.setState({ items: items.slice() }); } @@ -171,7 +173,7 @@ export class ListSetter extends Component { } const { items } = this.state; - const scrollToLast = this.scrollToLast; + const { scrollToLast } = this; this.scrollToLast = false; const lastIndex = items.length - 1; @@ -197,12 +199,12 @@ export class ListSetter extends Component { return (
- {/*
+ {/*
-
*/} +
*/} {columns &&
{columns}
} {content}