Browse Source

fix: modify eslint configuration and add pre-commit

master
yangpei 4 years ago
parent
commit
3a6b912785
  1. 19
      .eslintrc.js
  2. 14
      package.json
  3. 5
      packages/cli/src/cmd/dev/index.js
  4. 9
      packages/cli/src/cmd/dev/mergePkg.js
  5. 5
      packages/cli/src/utils/server/index.js
  6. 7
      packages/compile-code/src/addPlugins.ts
  7. 68
      packages/compile-code/src/compileForOnline.ts
  8. 12
      packages/compile-code/src/compileForProject.ts
  9. 8
      packages/compile-code/src/extractNodeFns.ts
  10. 5
      packages/compile-code/src/index.ts
  11. 13
      packages/compile-code/src/simplifyDSL.ts
  12. 9
      packages/compile-code/src/template/runOnline.ts
  13. 1
      packages/core/package.json
  14. 5
      packages/core/src/api/index.ts
  15. 41
      packages/core/src/common/shortcuts.ts
  16. 12
      packages/core/src/components/codeEditor/index.tsx
  17. 166
      packages/core/src/components/codeEditor/theme-monokai.ts
  18. 22
      packages/core/src/components/codeRun/index.tsx
  19. 39
      packages/core/src/components/codeRun/inputPanel/index.tsx
  20. 8
      packages/core/src/components/colorPicker/index.tsx
  21. 74
      packages/core/src/components/console/index.tsx
  22. 150
      packages/core/src/components/schemaForm/index.tsx
  23. 6
      packages/core/src/hooks/useClickAway.ts
  24. 23
      packages/core/src/mods/flowChart/codeEditorModel/index.tsx
  25. 9
      packages/core/src/mods/flowChart/codeRunModal/index.tsx
  26. 52
      packages/core/src/mods/flowChart/contextMenu/index.tsx
  27. 6
      packages/core/src/mods/flowChart/contextMenu/menuConfig/blank.tsx
  28. 5
      packages/core/src/mods/flowChart/contextMenu/menuConfig/index.ts
  29. 16
      packages/core/src/mods/flowChart/contextMenu/menuConfig/node.tsx
  30. 39
      packages/core/src/mods/flowChart/createFlowChart.ts
  31. 24
      packages/core/src/mods/flowChart/index.tsx
  32. 16
      packages/core/src/mods/flowChart/registerServerStorage.ts
  33. 12
      packages/core/src/mods/header/connectStatus/editModal.tsx
  34. 27
      packages/core/src/mods/header/export/exportModal.tsx
  35. 11
      packages/core/src/mods/header/export/index.tsx
  36. 279
      packages/core/src/mods/header/guide/guideModal.tsx
  37. 12
      packages/core/src/mods/header/guide/index.tsx
  38. 11
      packages/core/src/mods/header/importDSL/index.tsx
  39. 7
      packages/core/src/mods/settingBar/components/checkbox/index.tsx
  40. 7
      packages/core/src/mods/settingBar/components/input/index.tsx
  41. 143
      packages/core/src/mods/settingBar/components/json/index.tsx
  42. 198
      packages/core/src/mods/settingBar/components/json/json.ts
  43. 12
      packages/core/src/mods/settingBar/index.tsx
  44. 12
      packages/core/src/mods/settingBar/mods/basic/index.tsx
  45. 14
      packages/core/src/mods/settingBar/mods/testCase/index.tsx
  46. 11
      packages/core/src/mods/sideBar/index.tsx
  47. 14
      packages/core/src/mods/toolBar/widgets/bgColor.tsx
  48. 18
      packages/core/src/mods/toolBar/widgets/borderColor.tsx
  49. 6
      packages/core/src/mods/toolBar/widgets/common/makeDropdownWidget.tsx
  50. 4
      packages/core/src/mods/toolBar/widgets/fontSize.tsx
  51. 15
      packages/core/src/mods/toolBar/widgets/horizontalAlign.tsx
  52. 9
      packages/core/src/mods/toolBar/widgets/lineStyle.tsx
  53. 90
      packages/core/src/mods/toolBar/widgets/nodeAlign.tsx
  54. 18
      packages/core/src/mods/toolBar/widgets/textColor.tsx
  55. 4
      packages/core/src/mods/toolBar/widgets/underline.tsx
  56. 9
      packages/core/src/mods/toolBar/widgets/verticalAlign.tsx
  57. 5
      packages/core/src/typings.d.ts
  58. 25
      packages/core/src/utils/analyzeDeps.ts
  59. 20
      packages/core/src/utils/flowChartUtils.ts
  60. 2
      packages/core/src/utils/index.ts
  61. 19
      packages/json-schema-editor/src/components/add-node.tsx
  62. 5
      packages/json-schema-editor/src/components/schema-form.tsx
  63. 12
      packages/json-schema-editor/src/components/schema-head.tsx
  64. 22
      packages/json-schema-editor/src/components/schema-item.tsx
  65. 8
      packages/json-schema-editor/src/model/schema.ts
  66. 38
      packages/json-schema-editor/src/reducer/schema.ts
  67. 14
      packages/json-schema-editor/src/utils/schema.ts

19
.eslintrc.js

@ -1,22 +1,23 @@
module.exports = { module.exports = {
extends: [ extends: [
'standard', "prettier/@typescript-eslint",
'plugin:import/typescript', "plugin:react/recommended",
'prettier', "plugin:@typescript-eslint/recommended",
'prettier/react', "plugin:prettier/recommended"
], ],
plugins: ['prettier', 'react'],
env: { env: {
node: true, node: true,
browser: true, browser: true,
}, },
settings: {
react: {
version: "detect"
}
},
rules: { rules: {
'import/no-extraneous-dependencies': ['error', { peerDependencies: true }], 'import/no-extraneous-dependencies': ['error', { peerDependencies: true }],
'prettier/prettier': 'error', 'prettier/prettier': 'error',
// 修复 tsx 文件引用 tsx 文件报错的问题 'react/jsx-filename-extension': ['warn', { extensions: ['.ts', '.tsx'] }], // 修复 tsx 文件引用 tsx 文件报错的问题
'react/jsx-filename-extension': ['warn', { extensions: ['.ts', '.tsx'] }],
"semi": [2, "always"],//语句强制分号结尾
"semi-spacing": [0, { "before": false, "after": true }],//分号前后空格
"no-var": 0,//禁用var,用let和const代替 "no-var": 0,//禁用var,用let和const代替
"no-use-before-define": 2,//未定义前不能使用 "no-use-before-define": 2,//未定义前不能使用
"no-unused-expressions": 2,//禁止无用的表达式 "no-unused-expressions": 2,//禁止无用的表达式

14
package.json

@ -10,6 +10,9 @@
"example": "concurrently \"lerna run watch --parallel\" \"cross-env APP_ROOT=example umi dev\" \"imove -d\"", "example": "concurrently \"lerna run watch --parallel\" \"cross-env APP_ROOT=example umi dev\" \"imove -d\"",
"postinstall": "lerna init && lerna bootstrap && npm link packages/cli" "postinstall": "lerna init && lerna bootstrap && npm link packages/cli"
}, },
"pre-commit": [
"lint"
],
"jest": { "jest": {
"projects": [ "projects": [
"config/cli.jest.config.js", "config/cli.jest.config.js",
@ -24,27 +27,28 @@
"@rollup/plugin-node-resolve": "^7.1.3", "@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-strip": "^1.3.2", "@rollup/plugin-strip": "^1.3.2",
"@types/jest": "^26.0.16", "@types/jest": "^26.0.16",
"@typescript-eslint/eslint-plugin": "^2.29.0", "@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^2.29.0", "@typescript-eslint/parser": "^2.29.0",
"concurrently": "^5.3.0", "concurrently": "^5.3.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "^6.2.0", "cypress": "^6.2.0",
"dumi": "^1.0.38", "dumi": "^1.0.38",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1", "eslint-config-prettier": "^7.2.0",
"eslint-plugin-prettier": "^3.3.0", "eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-standard": "^5.0.0", "eslint-plugin-react": "^7.22.0",
"lerna": "^3.22.1", "lerna": "^3.22.1",
"less": "^3.12.2", "less": "^3.12.2",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"ora": "^4.1.1", "ora": "^4.1.1",
"postcss": "^8.2.1", "postcss": "^8.2.1",
"pre-commit": "^1.2.2",
"prettier": "^2.2.1",
"rollup": "^2.6.1", "rollup": "^2.6.1",
"rollup-plugin-postcss": "^4.0.0", "rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-size-snapshot": "^0.11.0", "rollup-plugin-size-snapshot": "^0.11.0",
"rollup-plugin-typescript": "^1.0.1", "rollup-plugin-typescript": "^1.0.1",
"snazzy": "^9.0.0", "snazzy": "^9.0.0",
"standard": "^16.0.3",
"ts-jest": "^26.4.4", "ts-jest": "^26.4.4",
"typescript": "^4.1.3", "typescript": "^4.1.3",
"umi": "^3.3.3", "umi": "^3.3.3",

5
packages/cli/src/cmd/dev/index.js

@ -10,11 +10,10 @@ const CACHE_PATH = path.join(process.cwd(), './.cache');
const CACHE_DSL_FILE = path.join(CACHE_PATH, 'imove.dsl.json'); const CACHE_DSL_FILE = path.join(CACHE_PATH, 'imove.dsl.json');
class Dev extends Base { class Dev extends Base {
async writeOutputIntoFiles(curPath, output) { async writeOutputIntoFiles(curPath, output) {
for(let key in output) { for (const key in output) {
const newPath = path.join(curPath, key); const newPath = path.join(curPath, key);
if(path.extname(newPath)) { if (path.extname(newPath)) {
await fs.writeFile(newPath, output[key]); await fs.writeFile(newPath, output[key]);
} else { } else {
await fs.ensureDir(newPath); await fs.ensureDir(newPath);

9
packages/cli/src/cmd/dev/mergePkg.js

@ -14,9 +14,14 @@ const extractDep = (dsl) => {
const { dependencies } = cell.data || {}; const { dependencies } = cell.data || {};
try { try {
const json = JSON.parse(dependencies); const json = JSON.parse(dependencies);
Object.keys(json).forEach((key) => (mergedDependencies[key] = json[key])); Object.keys(json).forEach(
(key) => (mergedDependencies[key] = json[key]),
);
} catch (error) { } catch (error) {
console.log('extract dependencies failed, the error is:', error.message); console.log(
'extract dependencies failed, the error is:',
error.message,
);
} }
}); });
return mergedDependencies; return mergedDependencies;

5
packages/cli/src/utils/server/index.js

@ -7,7 +7,10 @@ const createServer = (port = 3500) => {
app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.urlencoded({ extended: false }));
app.use((req, res, next) => { app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept',
);
next(); next();
}); });
app.listen(port, () => { app.listen(port, () => {

7
packages/compile-code/src/addPlugins.ts

@ -1,11 +1,12 @@
const INSERT_IMPORT_PLUGINS_COMMENT = '// import plugins here'; const INSERT_IMPORT_PLUGINS_COMMENT = '// import plugins here';
const INSERT_USE_PLUGINS_COMMENT = '// use plugins here'; const INSERT_USE_PLUGINS_COMMENT = '// use plugins here';
const addPlugins = (originalCode: string = '', plugins: string[] = []): string => { const addPlugins = (originalCode = '', plugins: string[] = []): string => {
const modifiedContent: string = originalCode const modifiedContent: string = originalCode
.replace(new RegExp(INSERT_IMPORT_PLUGINS_COMMENT), () => { .replace(new RegExp(INSERT_IMPORT_PLUGINS_COMMENT), () => {
return plugins.map((plugin, index) => `import plugin${index} from '${plugin}';`).join('\n'); return plugins
.map((plugin, index) => `import plugin${index} from '${plugin}';`)
.join('\n');
}) })
.replace(new RegExp(INSERT_USE_PLUGINS_COMMENT), () => { .replace(new RegExp(INSERT_USE_PLUGINS_COMMENT), () => {
return plugins.map((_, index) => `logic.use(plugin${index});`).join('\n'); return plugins.map((_, index) => `logic.use(plugin${index});`).join('\n');

68
packages/compile-code/src/compileForOnline.ts

@ -1,4 +1,4 @@
import {Cell} from '@antv/x6'; import { Cell } from '@antv/x6';
import makeCode from './template/runOnline'; import makeCode from './template/runOnline';
import simplifyDSL from './simplifyDSL'; import simplifyDSL from './simplifyDSL';
@ -23,46 +23,46 @@ interface DSL {
const INSERT_DSL_COMMENT = '// define dsl here'; const INSERT_DSL_COMMENT = '// define dsl here';
const INSERT_NODE_FNS_COMMENT = '// define nodeFns here'; const INSERT_NODE_FNS_COMMENT = '// define nodeFns here';
const importRegex = /import\s+([\s\S]*?)\s+from\s+(?:('[@\.\/\-\w]+')|("[@\.\/\-\w]+"))\s*;?/mg; const importRegex = /import\s+([\s\S]*?)\s+from\s+(?:('[@\.\/\-\w]+')|("[@\.\/\-\w]+"))\s*;?/gm;
const virtualSourceNode = { const virtualSourceNode = {
id: 'virtual-imove-start', id: 'virtual-imove-start',
shape: 'imove-start', shape: 'imove-start',
data: { data: {
trigger: 'virtual-imove-start', trigger: 'virtual-imove-start',
configData: {}, configData: {},
code: 'export default async function(ctx) {\n \n}' code: 'export default async function(ctx) {\n \n}',
} },
}; };
const findStartNode = (dsl: DSL): Cell.Properties => { const findStartNode = (dsl: DSL): Cell.Properties => {
const nodes = dsl.cells.filter((cell) => cell.shape !== 'edge');
const edges = dsl.cells.filter((cell) => cell.shape === 'edge');
const nodes = dsl.cells.filter(cell => cell.shape !== 'edge'); if (nodes.length === 0) {
const edges = dsl.cells.filter(cell => cell.shape === 'edge');
if(nodes.length === 0) {
throw new Error('Compile failed, no node is selected'); throw new Error('Compile failed, no node is selected');
} }
let foundEdge = null; let foundEdge = null;
let startNode = nodes[0]; let startNode = nodes[0];
while(foundEdge = edges.find(edge => edge.target.cell === startNode.id)) { while (
(foundEdge = edges.find((edge) => edge.target.cell === startNode.id))
) {
const newSourceId = foundEdge.source.cell; const newSourceId = foundEdge.source.cell;
startNode = nodes.find(node => node.id === newSourceId) as Cell.Properties; startNode = nodes.find(
(node) => node.id === newSourceId,
) as Cell.Properties;
} }
if(startNode.shape !== 'imove-start') { if (startNode.shape !== 'imove-start') {
dsl.cells.push( dsl.cells.push(virtualSourceNode, {
virtualSourceNode, shape: 'edge',
{
shape: "edge",
source: { source: {
cell: 'virtual-imove-start', cell: 'virtual-imove-start',
}, },
target: { target: {
cell: startNode.id cell: startNode.id,
} },
} });
);
startNode = virtualSourceNode; startNode = virtualSourceNode;
} }
@ -70,13 +70,12 @@ const findStartNode = (dsl: DSL): Cell.Properties => {
}; };
const getNextNode = (curNode: Cell.Properties, dsl: DSL) => { const getNextNode = (curNode: Cell.Properties, dsl: DSL) => {
const nodes = dsl.cells.filter((cell) => cell.shape !== 'edge');
const edges = dsl.cells.filter((cell) => cell.shape === 'edge');
const nodes = dsl.cells.filter(cell => cell.shape !== 'edge'); const foundEdge = edges.find((edge) => edge.source.cell === curNode.id);
const edges = dsl.cells.filter(cell => cell.shape === 'edge'); if (foundEdge) {
return nodes.find((node) => node.id === foundEdge.target.cell);
const foundEdge = edges.find(edge => edge.source.cell === curNode.id);
if(foundEdge) {
return nodes.find(node => node.id === foundEdge.target.cell);
} }
}; };
@ -86,11 +85,18 @@ const compileSimplifiedDSL = (dsl: DSL): string => {
}; };
const compileNodeFn = (node: Cell.Properties): string => { const compileNodeFn = (node: Cell.Properties): string => {
const {data: {label, code}} = node; const {
const newCode = code.replace(importRegex, (match: string, p1: string, p2: string, p3: string) => { data: { label, code },
} = node;
const newCode = code
.replace(
importRegex,
(match: string, p1: string, p2: string, p3: string) => {
const pkgName = (p2 || p3).replace(/('|")/g, ''); const pkgName = (p2 || p3).replace(/('|")/g, '');
return `const ${p1} = (await import('https://jspm.dev/${pkgName}')).default;`; return `const ${p1} = (await import('https://jspm.dev/${pkgName}')).default;`;
}).replace(/export\s+default/, 'return'); },
)
.replace(/export\s+default/, 'return');
return `await (async function() { return `await (async function() {
${newCode} ${newCode}
@ -98,9 +104,9 @@ const compileNodeFn = (node: Cell.Properties): string => {
}; };
const compileNodeFnsMap = (dsl: DSL): string => { const compileNodeFnsMap = (dsl: DSL): string => {
const nodes = dsl.cells.filter(cell => cell.shape !== 'edge'); const nodes = dsl.cells.filter((cell) => cell.shape !== 'edge');
const kvs = nodes.map(node => { const kvs = nodes.map((node) => {
const {id} = node; const { id } = node;
return `'${id}': ${compileNodeFn(node)}`; return `'${id}': ${compileNodeFn(node)}`;
}); });

12
packages/compile-code/src/compileForProject.ts

@ -1,4 +1,4 @@
import {Cell} from '@antv/x6'; import { Cell } from '@antv/x6';
import addPlugins from './addPlugins'; import addPlugins from './addPlugins';
import simplifyDSL from './simplifyDSL'; import simplifyDSL from './simplifyDSL';
import extractNodeFns from './extractNodeFns'; import extractNodeFns from './extractNodeFns';
@ -11,8 +11,8 @@ interface DSL {
} }
interface IOutput { interface IOutput {
'nodeFns': { nodeFns: {
[fileName: string]: string [fileName: string]: string;
}; };
'context.js': string; 'context.js': string;
'dsl.json': string; 'dsl.json': string;
@ -22,11 +22,11 @@ interface IOutput {
const compile = (dsl: DSL, plugins = []): IOutput => { const compile = (dsl: DSL, plugins = []): IOutput => {
const output: IOutput = { const output: IOutput = {
'nodeFns': extractNodeFns(dsl), nodeFns: extractNodeFns(dsl),
"context.js": contextTpl, 'context.js': contextTpl,
'dsl.json': JSON.stringify(simplifyDSL(dsl), null, 2), 'dsl.json': JSON.stringify(simplifyDSL(dsl), null, 2),
'index.js': addPlugins(indexTpl, plugins), 'index.js': addPlugins(indexTpl, plugins),
'logic.js': logicTpl 'logic.js': logicTpl,
}; };
return output; return output;
}; };

8
packages/compile-code/src/extractNodeFns.ts

@ -1,4 +1,4 @@
import {Cell} from '@antv/x6'; import { Cell } from '@antv/x6';
interface DSL { interface DSL {
cells: Cell.Properties[]; cells: Cell.Properties[];
@ -34,8 +34,8 @@ const genNodeFns = (dsl: DSL): INodesFns => {
data: { label, code }, data: { label, code },
} of nodes) { } of nodes) {
const fileName: string = id + '.js'; const fileName: string = id + '.js';
const descData: string = `// ${shape}: ${label}\n`; const descData = `// ${shape}: ${label}\n`;
const saveData: string = `${descData}\n${code}`; const saveData = `${descData}\n${code}`;
nodeFns[fileName] = saveData; nodeFns[fileName] = saveData;
} }
return nodeFns; return nodeFns;
@ -43,7 +43,7 @@ const genNodeFns = (dsl: DSL): INodesFns => {
const extract = (dsl: DSL): INodesFns => { const extract = (dsl: DSL): INodesFns => {
const nodeFns = genNodeFns(dsl); const nodeFns = genNodeFns(dsl);
const nodeIds = Object.keys(nodeFns).map(fileName => fileName.slice(0, -3)); const nodeIds = Object.keys(nodeFns).map((fileName) => fileName.slice(0, -3));
const entryFileContent = genEntryFile(nodeIds); const entryFileContent = genEntryFile(nodeIds);
nodeFns['index.js'] = entryFileContent; nodeFns['index.js'] = entryFileContent;
return nodeFns; return nodeFns;

5
packages/compile-code/src/index.ts

@ -1,7 +1,4 @@
import compileForOnline from './compileForOnline'; import compileForOnline from './compileForOnline';
import compileForProject from './compileForProject'; import compileForProject from './compileForProject';
export { export { compileForOnline, compileForProject };
compileForOnline,
compileForProject
};

13
packages/compile-code/src/simplifyDSL.ts

@ -1,10 +1,13 @@
import {Cell} from '@antv/x6'; import { Cell } from '@antv/x6';
interface DSL { interface DSL {
cells: Cell.Properties[]; cells: Cell.Properties[];
} }
const extractObj = (obj: Cell.Properties = {}, keys: string[] = []): Cell.Properties => { const extractObj = (
obj: Cell.Properties = {},
keys: string[] = [],
): Cell.Properties => {
const ret: Cell.Properties = {}; const ret: Cell.Properties = {};
keys.forEach((key) => { keys.forEach((key) => {
if (obj[key]) { if (obj[key]) {
@ -22,7 +25,11 @@ const simplifyDSL = (dsl: DSL): Cell.Properties => {
return extractObj(cell, ['id', 'shape', 'source', 'target']); return extractObj(cell, ['id', 'shape', 'source', 'target']);
} else { } else {
const newCell = extractObj(cell, ['id', 'shape', 'data']); const newCell = extractObj(cell, ['id', 'shape', 'data']);
newCell.data = extractObj(cell.data, ['trigger', 'configData', 'ports']); newCell.data = extractObj(cell.data, [
'trigger',
'configData',
'ports',
]);
return newCell; return newCell;
} }
}), }),

9
packages/compile-code/src/template/runOnline.ts

@ -9,14 +9,15 @@ const makeCode = (mockNode: any, mockInput: any) => `
// Logic // Logic
${logic ${logic
.split('\n') .split('\n')
.filter(line => !line.match(/import nodeFns/) && !line.match(/import Context/)) .filter(
(line) => !line.match(/import nodeFns/) && !line.match(/import Context/),
)
.join('\n') .join('\n')
.replace(/export\s+default/, '') .replace(/export\s+default/, '')
.replace( .replace(
`import EventEmitter from 'eventemitter3';`, `import EventEmitter from 'eventemitter3';`,
`const EventEmitter = (await import('https://jspm.dev/eventemitter3')).default;` `const EventEmitter = (await import('https://jspm.dev/eventemitter3')).default;`,
) )}
}
// DSL // DSL
// define dsl here // define dsl here

1
packages/core/package.json

@ -42,7 +42,6 @@
"@monaco-editor/react": "^3.7.4", "@monaco-editor/react": "^3.7.4",
"antd": "^4.5.3", "antd": "^4.5.3",
"axios": "^0.21.0", "axios": "^0.21.0",
"form-render": "^0.9.5",
"fr-generator": "^1.0.0", "fr-generator": "^1.0.0",
"jszip": "^3.5.0", "jszip": "^3.5.0",
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",

5
packages/core/src/api/index.ts

@ -86,7 +86,10 @@ export const queryGraph = (projectId: string) => {
}); });
}; };
export const modifyGraph = (projectId: string, actions: IModifyGraphAction[]) => { export const modifyGraph = (
projectId: string,
actions: IModifyGraphAction[],
) => {
return request({ return request({
url: '/api/modifyGraph', url: '/api/modifyGraph',
params: { params: {

41
packages/core/src/common/shortcuts.ts

@ -77,10 +77,18 @@ const shortcuts: { [key: string]: Shortcut } = {
const onEdgeDel = (edge: Edge) => { const onEdgeDel = (edge: Edge) => {
const srcNode = edge.getSourceNode() as Node; const srcNode = edge.getSourceNode() as Node;
const isSrcNodeInDelCells = !!toDelCells.find((c) => c === srcNode); const isSrcNodeInDelCells = !!toDelCells.find((c) => c === srcNode);
if (srcNode && srcNode.shape === 'imove-branch' && !isSrcNodeInDelCells) { if (
srcNode &&
srcNode.shape === 'imove-branch' &&
!isSrcNodeInDelCells
) {
const portId = edge.getSourcePortId(); const portId = edge.getSourcePortId();
if (portId === 'right' || portId === 'bottom') { if (portId === 'right' || portId === 'bottom') {
const edgeLabel = safeGet(edge.getLabelAt(0), 'attrs.label.text', ''); const edgeLabel = safeGet(
edge.getLabelAt(0),
'attrs.label.text',
'',
);
srcNode.setPortProp(portId, 'attrs/text/text', edgeLabel); srcNode.setPortProp(portId, 'attrs/text/text', edgeLabel);
} }
} }
@ -109,9 +117,12 @@ const shortcuts: { [key: string]: Shortcut } = {
handler(flowChart: Graph) { handler(flowChart: Graph) {
const cells = flowChart.getSelectedCells(); const cells = flowChart.getSelectedCells();
if (cells.length > 0) { if (cells.length > 0) {
const isAlreadyBold = safeGet(cells, '0.attrs.label.fontWeight', 'normal') === 'bold'; const isAlreadyBold =
safeGet(cells, '0.attrs.label.fontWeight', 'normal') === 'bold';
cells.forEach((cell) => { cells.forEach((cell) => {
cell.setAttrs({ label: { fontWeight: isAlreadyBold ? 'normal' : 'bold' } }); cell.setAttrs({
label: { fontWeight: isAlreadyBold ? 'normal' : 'bold' },
});
}); });
flowChart.trigger('toolBar:forceUpdate'); flowChart.trigger('toolBar:forceUpdate');
} }
@ -123,9 +134,12 @@ const shortcuts: { [key: string]: Shortcut } = {
handler(flowChart: Graph) { handler(flowChart: Graph) {
const cells = flowChart.getSelectedCells(); const cells = flowChart.getSelectedCells();
if (cells.length > 0) { if (cells.length > 0) {
const isAlreadyItalic = safeGet(cells, '0.attrs.label.fontStyle', 'normal') === 'italic'; const isAlreadyItalic =
safeGet(cells, '0.attrs.label.fontStyle', 'normal') === 'italic';
cells.forEach((cell) => { cells.forEach((cell) => {
cell.setAttrs({ label: { fontStyle: isAlreadyItalic ? 'normal' : 'italic' } }); cell.setAttrs({
label: { fontStyle: isAlreadyItalic ? 'normal' : 'italic' },
});
}); });
flowChart.trigger('toolBar:forceUpdate'); flowChart.trigger('toolBar:forceUpdate');
} }
@ -138,9 +152,14 @@ const shortcuts: { [key: string]: Shortcut } = {
const cells = flowChart.getSelectedCells(); const cells = flowChart.getSelectedCells();
if (cells.length > 0) { if (cells.length > 0) {
const isAlreadyUnderline = const isAlreadyUnderline =
safeGet(cells, '0.attrs.label.textDecoration', 'none') === 'underline'; safeGet(cells, '0.attrs.label.textDecoration', 'none') ===
'underline';
cells.forEach((cell) => { cells.forEach((cell) => {
cell.setAttrs({ label: { textDecoration: isAlreadyUnderline ? 'none' : 'underline' } }); cell.setAttrs({
label: {
textDecoration: isAlreadyUnderline ? 'none' : 'underline',
},
});
}); });
flowChart.trigger('toolBar:forceUpdate'); flowChart.trigger('toolBar:forceUpdate');
} }
@ -151,14 +170,14 @@ const shortcuts: { [key: string]: Shortcut } = {
keys: 'meta + ]', keys: 'meta + ]',
handler(flowChart: Graph) { handler(flowChart: Graph) {
getSelectedNodes(flowChart).forEach((node) => node.toFront()); getSelectedNodes(flowChart).forEach((node) => node.toFront());
} },
}, },
bringToBack: { bringToBack: {
keys: 'meta + [', keys: 'meta + [',
handler(flowChart: Graph) { handler(flowChart: Graph) {
getSelectedNodes(flowChart).forEach((node) => node.toBack()); getSelectedNodes(flowChart).forEach((node) => node.toBack());
} },
} },
}; };
export default shortcuts; export default shortcuts;

12
packages/core/src/components/codeEditor/index.tsx

@ -1,14 +1,10 @@
import React, { import React, { useMemo, useState, useEffect } from 'react';
useMemo,
useState,
useEffect
} from 'react';
import { import {
monaco, monaco,
EditorDidMount, EditorDidMount,
ControlledEditor, ControlledEditor,
ControlledEditorProps ControlledEditorProps,
} from '@monaco-editor/react'; } from '@monaco-editor/react';
import monokaiTheme from './theme-monokai'; import monokaiTheme from './theme-monokai';
@ -22,7 +18,7 @@ monaco.init().then((monaco) => {
}); });
const CODE_EDITOR_OPTIONS = { const CODE_EDITOR_OPTIONS = {
fontSize: 14 fontSize: 14,
}; };
interface IProps extends ControlledEditorProps { interface IProps extends ControlledEditorProps {
@ -30,7 +26,7 @@ interface IProps extends ControlledEditorProps {
} }
export const CodeEditor: React.FC<IProps> = (props) => { export const CodeEditor: React.FC<IProps> = (props) => {
const {options, editorDidMount, onSave, ...rest} = props; const { options, editorDidMount, onSave, ...rest } = props;
const [editorInst, setEditorInst] = useState<any>(); const [editorInst, setEditorInst] = useState<any>();
const editorOptions = useMemo(() => { const editorOptions = useMemo(() => {

166
packages/core/src/components/codeEditor/theme-monokai.ts

@ -1,144 +1,144 @@
export default { export default {
"base": "vs-dark", base: 'vs-dark',
"inherit": true, inherit: true,
"rules": [ rules: [
{ {
"background": "272822", background: '272822',
"token": "" token: '',
}, },
{ {
"foreground": "75715e", foreground: '75715e',
"token": "comment" token: 'comment',
}, },
{ {
"foreground": "e6db74", foreground: 'e6db74',
"token": "string" token: 'string',
}, },
{ {
"foreground": "ae81ff", foreground: 'ae81ff',
"token": "constant.numeric" token: 'constant.numeric',
}, },
{ {
"foreground": "ae81ff", foreground: 'ae81ff',
"token": "constant.language" token: 'constant.language',
}, },
{ {
"foreground": "ae81ff", foreground: 'ae81ff',
"token": "constant.character" token: 'constant.character',
}, },
{ {
"foreground": "ae81ff", foreground: 'ae81ff',
"token": "constant.other" token: 'constant.other',
}, },
{ {
"foreground": "f92672", foreground: 'f92672',
"token": "keyword" token: 'keyword',
}, },
{ {
"foreground": "f92672", foreground: 'f92672',
"token": "storage" token: 'storage',
}, },
{ {
"foreground": "66d9ef", foreground: '66d9ef',
"fontStyle": "italic", fontStyle: 'italic',
"token": "storage.type" token: 'storage.type',
}, },
{ {
"foreground": "a6e22e", foreground: 'a6e22e',
"fontStyle": "underline", fontStyle: 'underline',
"token": "entity.name.class" token: 'entity.name.class',
}, },
{ {
"foreground": "a6e22e", foreground: 'a6e22e',
"fontStyle": "italic underline", fontStyle: 'italic underline',
"token": "entity.other.inherited-class" token: 'entity.other.inherited-class',
}, },
{ {
"foreground": "a6e22e", foreground: 'a6e22e',
"token": "entity.name.function" token: 'entity.name.function',
}, },
{ {
"foreground": "fd971f", foreground: 'fd971f',
"fontStyle": "italic", fontStyle: 'italic',
"token": "variable.parameter" token: 'variable.parameter',
}, },
{ {
"foreground": "f92672", foreground: 'f92672',
"token": "entity.name.tag" token: 'entity.name.tag',
}, },
{ {
"foreground": "a6e22e", foreground: 'a6e22e',
"token": "entity.other.attribute-name" token: 'entity.other.attribute-name',
}, },
{ {
"foreground": "66d9ef", foreground: '66d9ef',
"token": "support.function" token: 'support.function',
}, },
{ {
"foreground": "66d9ef", foreground: '66d9ef',
"token": "support.constant" token: 'support.constant',
}, },
{ {
"foreground": "66d9ef", foreground: '66d9ef',
"fontStyle": "italic", fontStyle: 'italic',
"token": "support.type" token: 'support.type',
}, },
{ {
"foreground": "66d9ef", foreground: '66d9ef',
"fontStyle": "italic", fontStyle: 'italic',
"token": "support.class" token: 'support.class',
}, },
{ {
"foreground": "f8f8f0", foreground: 'f8f8f0',
"background": "f92672", background: 'f92672',
"token": "invalid" token: 'invalid',
}, },
{ {
"foreground": "f8f8f0", foreground: 'f8f8f0',
"background": "ae81ff", background: 'ae81ff',
"token": "invalid.deprecated" token: 'invalid.deprecated',
}, },
{ {
"foreground": "cfcfc2", foreground: 'cfcfc2',
"token": "meta.structure.dictionary.json string.quoted.double.json" token: 'meta.structure.dictionary.json string.quoted.double.json',
}, },
{ {
"foreground": "75715e", foreground: '75715e',
"token": "meta.diff" token: 'meta.diff',
}, },
{ {
"foreground": "75715e", foreground: '75715e',
"token": "meta.diff.header" token: 'meta.diff.header',
}, },
{ {
"foreground": "f92672", foreground: 'f92672',
"token": "markup.deleted" token: 'markup.deleted',
}, },
{ {
"foreground": "a6e22e", foreground: 'a6e22e',
"token": "markup.inserted" token: 'markup.inserted',
}, },
{ {
"foreground": "e6db74", foreground: 'e6db74',
"token": "markup.changed" token: 'markup.changed',
}, },
{ {
"foreground": "ae81ffa0", foreground: 'ae81ffa0',
"token": "constant.numeric.line-number.find-in-files - match" token: 'constant.numeric.line-number.find-in-files - match',
}, },
{ {
"foreground": "e6db74", foreground: 'e6db74',
"token": "entity.name.filename.find-in-files" token: 'entity.name.filename.find-in-files',
} },
], ],
"colors": { colors: {
"editor.foreground": "#F8F8F2", 'editor.foreground': '#F8F8F2',
"editor.background": "#272822", 'editor.background': '#272822',
"editor.selectionBackground": "#49483E", 'editor.selectionBackground': '#49483E',
"editor.lineHighlightBackground": "#3E3D32", 'editor.lineHighlightBackground': '#3E3D32',
"editorCursor.foreground": "#F8F8F0", 'editorCursor.foreground': '#F8F8F0',
"editorWhitespace.foreground": "#3B3A32", 'editorWhitespace.foreground': '#3B3A32',
"editorIndentGuide.activeBackground": "#9D550FB0", 'editorIndentGuide.activeBackground': '#9D550FB0',
"editor.selectionHighlightBorder": "#222218" 'editor.selectionHighlightBorder': '#222218',
} },
} };

22
packages/core/src/components/codeRun/index.tsx

@ -1,8 +1,4 @@
import React, { import React, { useState, useEffect, useCallback } from 'react';
useState,
useEffect,
useCallback,
} from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -26,7 +22,7 @@ const defaultInput = {
pipe: {}, pipe: {},
context: {}, context: {},
payload: {}, payload: {},
config: {} config: {},
}; };
interface ICardProps { interface ICardProps {
@ -39,9 +35,7 @@ const Card: React.FC<ICardProps> = (props) => {
return ( return (
<div className={styles.card}> <div className={styles.card}>
<div className={styles.cardTitleText}>{title}</div> <div className={styles.cardTitleText}>{title}</div>
<div className={styles.cardBody}> <div className={styles.cardBody}>{props.children}</div>
{props.children}
</div>
</div> </div>
); );
}; };
@ -51,8 +45,7 @@ interface ICodeRunProps {
} }
const CodeRun: React.FC<ICodeRunProps> = (props) => { const CodeRun: React.FC<ICodeRunProps> = (props) => {
const { flowChart } = props;
const {flowChart} = props;
const [isRunning, setIsRunning] = useState(false); const [isRunning, setIsRunning] = useState(false);
const [input, setInput] = useState(defaultInput); const [input, setInput] = useState(defaultInput);
const [output, setOutput] = useState({}); const [output, setOutput] = useState({});
@ -98,10 +91,7 @@ const CodeRun: React.FC<ICodeRunProps> = (props) => {
)} )}
</div> </div>
<Card title={'输入'}> <Card title={'输入'}>
<InputPanel <InputPanel data={input} onChange={onChangeInput} />
data={input}
onChange={onChangeInput}
/>
</Card> </Card>
</Pane> </Pane>
<Pane className={styles.pane}> <Pane className={styles.pane}>
@ -124,6 +114,6 @@ const CodeRun: React.FC<ICodeRunProps> = (props) => {
</SplitPane> </SplitPane>
</div> </div>
); );
} };
export default CodeRun; export default CodeRun;

39
packages/core/src/components/codeRun/inputPanel/index.tsx

@ -1,7 +1,4 @@
import React, { import React, { useRef, useEffect } from 'react';
useRef,
useEffect,
} from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -33,7 +30,7 @@ const VisualFormItem: React.FC<IVisualFormItemProps> = (props) => {
<Form.List name={type}> <Form.List name={type}>
{(fields, { add, remove }) => ( {(fields, { add, remove }) => (
<React.Fragment> <React.Fragment>
{fields.map(field => ( {fields.map((field) => (
<Space key={field.key} align={'baseline'}> <Space key={field.key} align={'baseline'}>
<Form.Item <Form.Item
{...field} {...field}
@ -73,7 +70,7 @@ const VisualFormItem: React.FC<IVisualFormItemProps> = (props) => {
interface IVisualPanelProps { interface IVisualPanelProps {
data: any; data: any;
onChange: (value: object) => void onChange: (value: object) => void;
} }
const VisualPanel: React.FC<IVisualPanelProps> = (props) => { const VisualPanel: React.FC<IVisualPanelProps> = (props) => {
@ -84,7 +81,10 @@ const VisualPanel: React.FC<IVisualPanelProps> = (props) => {
const filedsValue: { [key: string]: any } = {}; const filedsValue: { [key: string]: any } = {};
for (const { type } of inputItems) { for (const { type } of inputItems) {
const mockValue = data[type] || {}; const mockValue = data[type] || {};
filedsValue[type] = Object.keys(mockValue).map(key => ({ key, value: mockValue[key] })); filedsValue[type] = Object.keys(mockValue).map((key) => ({
key,
value: mockValue[key],
}));
} }
form.setFieldsValue(filedsValue); form.setFieldsValue(filedsValue);
}, [data]); }, [data]);
@ -94,28 +94,23 @@ const VisualPanel: React.FC<IVisualPanelProps> = (props) => {
const filedsValue = form.getFieldsValue(); const filedsValue = form.getFieldsValue();
for (const { type } of inputItems) { for (const { type } of inputItems) {
const mockValue = filedsValue[type] || []; const mockValue = filedsValue[type] || [];
input[type] = mockValue.reduce((prev: any, cur: { key: string, value: any }) => { input[type] = mockValue.reduce(
(prev: any, cur: { key: string; value: any }) => {
const { key, value } = cur; const { key, value } = cur;
prev[key] = value; prev[key] = value;
return prev; return prev;
}, {}); },
{},
);
} }
onChange(input); onChange(input);
}; };
return ( return (
<div className={styles.visualInput}> <div className={styles.visualInput}>
<Form <Form form={form} autoComplete={'on'} onChange={onFormChange}>
form={form}
autoComplete={'on'}
onChange={onFormChange}
>
{inputItems.map(({ type, desc }, index) => ( {inputItems.map(({ type, desc }, index) => (
<VisualFormItem <VisualFormItem key={index} type={type} desc={desc} />
key={index}
type={type}
desc={desc}
/>
))} ))}
</Form> </Form>
</div> </div>
@ -148,7 +143,11 @@ const InputPanel: React.FC<IInputPanelProps> = (props) => {
return ( return (
<Tabs type={'card'} defaultActiveKey={'visualTab'}> <Tabs type={'card'} defaultActiveKey={'visualTab'}>
<Tabs.TabPane className={styles.tabPane} tab={'可视化模式'} key={'visualTab'}> <Tabs.TabPane
className={styles.tabPane}
tab={'可视化模式'}
key={'visualTab'}
>
<VisualPanel data={data} onChange={onVisualChange} /> <VisualPanel data={data} onChange={onVisualChange} />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane className={styles.tabPane} tab={'源码模式'} key={'jsonTab'}> <Tabs.TabPane className={styles.tabPane} tab={'源码模式'} key={'jsonTab'}>

8
packages/core/src/components/colorPicker/index.tsx

@ -18,7 +18,13 @@ const ColorPicker: React.FC<IProps> = (props) => {
const onChange = (color: ColorResult): void => setCurColor(color.hex); const onChange = (color: ColorResult): void => setCurColor(color.hex);
const onChangeComplete = (color: ColorResult): void => changeCb(color.hex); const onChangeComplete = (color: ColorResult): void => changeCb(color.hex);
return <SketchPicker color={curColor} onChange={onChange} onChangeComplete={onChangeComplete} />; return (
<SketchPicker
color={curColor}
onChange={onChange}
onChangeComplete={onChangeComplete}
/>
);
}; };
export default ColorPicker; export default ColorPicker;

74
packages/core/src/components/console/index.tsx

@ -3,7 +3,7 @@ import React, {
useState, useState,
useEffect, useEffect,
useCallback, useCallback,
ChangeEvent ChangeEvent,
} from 'react'; } from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -14,7 +14,7 @@ import {
BugOutlined, BugOutlined,
CloseCircleOutlined, CloseCircleOutlined,
ClearOutlined, ClearOutlined,
FilterOutlined FilterOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import JsonView from 'react-json-view'; import JsonView from 'react-json-view';
import { Button, Input, Select, Tabs } from 'antd'; import { Button, Input, Select, Tabs } from 'antd';
@ -38,15 +38,17 @@ const Helper = {
return typeof value === 'object' && value !== null; return typeof value === 'object' && value !== null;
}, },
getArgsToString(args: any[]): string { getArgsToString(args: any[]): string {
return args.map(o => { return args
.map((o) => {
if (Helper.isPlainObject(o)) { if (Helper.isPlainObject(o)) {
return JSON.stringify(o); return JSON.stringify(o);
} else { } else {
return o; return o;
} }
}).join(' '); })
} .join(' ');
} },
};
const hijackMap: { [key: string]: any } = { const hijackMap: { [key: string]: any } = {
log: { log: {
@ -54,62 +56,63 @@ const hijackMap: { [key: string]: any } = {
textColor: '#ffffff', textColor: '#ffffff',
borderColor: 'rgba(128, 128, 128, 0.35)', borderColor: 'rgba(128, 128, 128, 0.35)',
icon: <div className={styles.logIcon} />, icon: <div className={styles.logIcon} />,
originMethod: console.log originMethod: console.log,
}, },
info: { info: {
bgColor: '#272823', bgColor: '#272823',
textColor: '#ffffff', textColor: '#ffffff',
borderColor: 'rgba(128, 128, 128, 0.35)', borderColor: 'rgba(128, 128, 128, 0.35)',
icon: <InfoCircleOutlined className={styles.logIcon} />, icon: <InfoCircleOutlined className={styles.logIcon} />,
originMethod: console.info originMethod: console.info,
}, },
warn: { warn: {
bgColor: 'rgb(51, 42, 0)', bgColor: 'rgb(51, 42, 0)',
textColor: 'rgb(245, 211, 150)', textColor: 'rgb(245, 211, 150)',
borderColor: 'rgb(102, 85, 0)', borderColor: 'rgb(102, 85, 0)',
icon: <WarningOutlined className={styles.logIcon} />, icon: <WarningOutlined className={styles.logIcon} />,
originMethod: console.warn originMethod: console.warn,
}, },
debug: { debug: {
bgColor: '#272823', bgColor: '#272823',
textColor: 'rgb(77, 136, 255)', textColor: 'rgb(77, 136, 255)',
borderColor: 'rgba(128, 128, 128, 0.35)', borderColor: 'rgba(128, 128, 128, 0.35)',
icon: <BugOutlined className={styles.logIcon} />, icon: <BugOutlined className={styles.logIcon} />,
originMethod: console.debug originMethod: console.debug,
}, },
error: { error: {
bgColor: 'rgb(40, 0, 0)', bgColor: 'rgb(40, 0, 0)',
textColor: 'rgb(254, 127, 127)', textColor: 'rgb(254, 127, 127)',
borderColor: 'rgb(91, 0, 0)', borderColor: 'rgb(91, 0, 0)',
icon: <CloseCircleOutlined className={styles.logIcon} />, icon: <CloseCircleOutlined className={styles.logIcon} />,
originMethod: console.error originMethod: console.error,
} },
} };
const LogLine: React.FC<ILog> = (props) => { const LogLine: React.FC<ILog> = (props) => {
const { type, data } = props; const { type, data } = props;
const renderLink = (data: string) => { const renderLink = (data: string) => {
return <a href={data} target={'_blank'}>{data}</a> return (
<a href={data} target={'_blank'}>
{data}
</a>
);
}; };
const renderText = (data: any) => { const renderText = (data: any) => {
if(typeof data !== 'string') { if (typeof data !== 'string') {
return data; return data;
} else { } else {
const arr = data.split(linkRegex); const arr = data.split(linkRegex);
return arr.map((str, index) => { return arr.map((str, index) => {
return ( return (
<span key={index}> <span key={index}>{Helper.isLink(str) ? renderLink(str) : str}</span>
{Helper.isLink(str) ? renderLink(str) : str}
</span>
); );
}) });
} }
}; };
const renderJson = (data: Object) => { const renderJson = (data: Record<string, any>) => {
return ( return (
<JsonView <JsonView
name={null} name={null}
@ -124,18 +127,13 @@ const LogLine: React.FC<ILog> = (props) => {
); );
}; };
const { const { icon, textColor, bgColor, borderColor } = hijackMap[type];
icon,
textColor,
bgColor,
borderColor
} = hijackMap[type];
const logLineStyle = { const logLineStyle = {
color: textColor, color: textColor,
backgroundColor: bgColor, backgroundColor: bgColor,
borderTopColor: borderColor, borderTopColor: borderColor,
borderBottomColor: borderColor borderBottomColor: borderColor,
}; };
return ( return (
@ -176,36 +174,36 @@ const MyConsole: React.FC = () => {
useEffect(() => { useEffect(() => {
const filteredLogs = cache.current.allLogs const filteredLogs = cache.current.allLogs
.filter(log => { .filter((log) => {
if (level === 'all') { if (level === 'all') {
return true; return true;
} else { } else {
return log.type === level; return log.type === level;
} }
}) })
.filter(log => { .filter((log) => {
return log.strVal.indexOf(filter) > -1; return log.strVal.indexOf(filter) > -1;
}); });
setLogList(filteredLogs); setLogList(filteredLogs);
}, [filter, level]); }, [filter, level]);
const hijackConsole = () => { const hijackConsole = () => {
Object.keys(hijackMap).forEach(method => { Object.keys(hijackMap).forEach((method) => {
// @ts-ignore // @ts-ignore
window.console[method] = (...args: any[]) => { window.console[method] = (...args: any[]) => {
hijackMap[method].originMethod(...args); hijackMap[method].originMethod(...args);
cache.current.allLogs = cache.current.allLogs.concat({ cache.current.allLogs = cache.current.allLogs.concat({
type: method, type: method,
data: args, data: args,
strVal: Helper.getArgsToString(args) strVal: Helper.getArgsToString(args),
}); });
setLogList(cache.current.allLogs); setLogList(cache.current.allLogs);
}; };
}); });
} };
const resetConsole = () => { const resetConsole = () => {
Object.keys(hijackMap).forEach(method => { Object.keys(hijackMap).forEach((method) => {
// @ts-ignore // @ts-ignore
window.console[method] = hijackMap[method].originMethod; window.console[method] = hijackMap[method].originMethod;
}); });
@ -233,8 +231,12 @@ const MyConsole: React.FC = () => {
prefix={<FilterOutlined />} prefix={<FilterOutlined />}
onChange={onChangeFilter} onChange={onChangeFilter}
/> />
<Select className={styles.levels} defaultValue={'all'} onChange={onChangeLevel}> <Select
{['all', 'info', 'warn', 'error', 'debug'].map(method => ( className={styles.levels}
defaultValue={'all'}
onChange={onChangeLevel}
>
{['all', 'info', 'warn', 'error', 'debug'].map((method) => (
<Select.Option key={method} value={method}> <Select.Option key={method} value={method}>
{method} {method}
</Select.Option> </Select.Option>

150
packages/core/src/components/schemaForm/index.tsx

@ -1,104 +1,148 @@
import React from 'react'; import React from 'react';
import { Form, Input, Switch, Select, Checkbox, DatePicker, TimePicker, Empty, Button, message } from 'antd'; import {
Form,
Input,
Switch,
Select,
Checkbox,
DatePicker,
TimePicker,
Empty,
Button,
message,
} from 'antd';
import moment from 'moment'; import moment from 'moment';
import styles from './index.module.less' import styles from './index.module.less';
const FormItem = Form.Item const FormItem = Form.Item;
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const { Option } = Select; const { Option } = Select;
interface INum { interface INum {
label: string, label: string;
value: string value: string;
} }
interface IConfigData { interface IConfigData {
[key: string]: any [key: string]: any;
} }
interface IProps { interface IProps {
schema: { schema: {
type: string, type: string;
properties: { [key: string]: any } properties: { [key: string]: any };
}, };
configData: IConfigData, configData: IConfigData;
changeConfigData: (data: IConfigData) => void changeConfigData: (data: IConfigData) => void;
} }
const formLayout = { const formLayout = {
labelCol: { span: 8 }, labelCol: { span: 8 },
wrapperCol: { span: 16 } wrapperCol: { span: 16 },
}; };
const SchemaForm: React.FC<IProps> = (props) => { const SchemaForm: React.FC<IProps> = (props) => {
const { schema, configData, changeConfigData } = props const { schema, configData, changeConfigData } = props;
const [form] = Form.useForm() const [form] = Form.useForm();
const onClickSave = (): void => { const onClickSave = (): void => {
changeConfigData(form.getFieldsValue()) changeConfigData(form.getFieldsValue());
message.success('保存成功!') message.success('保存成功!');
} };
return ( return schema && Object.keys(schema).length > 0 ? (
schema && Object.keys(schema).length > 0 ? <Form form={form} initialValues={configData} {...formLayout}>
<Form {schema?.properties &&
form={form} Object.keys(schema.properties).map((key: string) => {
initialValues={configData} const obj = schema.properties[key];
{...formLayout} const options: INum[] = obj.enum
> ? obj.enum.map((item: string, idx: number) => {
{schema?.properties && Object.keys(schema.properties).map((key: string) => { return { label: item, value: obj.enumNames[idx] };
const obj = schema.properties[key] })
const options: INum[] = obj.enum ? obj.enum.map((item: string, idx: number) => { : [];
return { label: item, value: obj.enumNames[idx] } let ele = null;
}) : []
let ele = null
// 输入框 // 输入框
if (obj.type === 'string' && !obj.hasOwnProperty('format') && !obj.hasOwnProperty('enum')) { if (
ele = <Input /> obj.type === 'string' &&
!obj.hasOwnProperty('format') &&
!obj.hasOwnProperty('enum')
) {
ele = <Input />;
} }
// 编辑框 // 编辑框
if (obj.type === 'string' && obj.format === 'textarea') { if (obj.type === 'string' && obj.format === 'textarea') {
ele = <Input.TextArea /> ele = <Input.TextArea />;
} }
// switch // switch
if (obj.type === 'boolean' && obj['ui:widget'] === 'switch') { if (obj.type === 'boolean' && obj['ui:widget'] === 'switch') {
ele = <Switch /> ele = <Switch />;
} }
// 下拉单选 // 下拉单选
if (obj.type === 'string' && obj.hasOwnProperty('enum') && !obj.hasOwnProperty('ui:widget')) { if (
ele = <Select allowClear>{options.map(item => obj.type === 'string' &&
<Option key={item.value} value={item.value}>{item.label}</Option> obj.hasOwnProperty('enum') &&
)}</Select> !obj.hasOwnProperty('ui:widget')
) {
ele = (
<Select allowClear>
{options.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
);
} }
// 下拉多选 // 下拉多选
if (obj.type === 'array' && obj.hasOwnProperty('enum') && obj['ui:widget'] === 'multiSelect') { if (
ele = <Select allowClear mode="multiple"> obj.type === 'array' &&
{options.map(item => <Option key={item.value} value={item.value}>{item.label}</Option>)} obj.hasOwnProperty('enum') &&
obj['ui:widget'] === 'multiSelect'
) {
ele = (
<Select allowClear mode="multiple">
{options.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select> </Select>
);
} }
// 点击多选 // 点击多选
if (obj.type === 'array' && obj.hasOwnProperty('enum') && !obj.hasOwnProperty('ui:widget')) { if (
ele = <Checkbox.Group options={options} /> obj.type === 'array' &&
obj.hasOwnProperty('enum') &&
!obj.hasOwnProperty('ui:widget')
) {
ele = <Checkbox.Group options={options} />;
} }
// 时间选择 // 时间选择
if (obj.type === 'string' && obj.format === 'time') { if (obj.type === 'string' && obj.format === 'time') {
ele = <TimePicker defaultValue={moment('00:00:00', 'HH:mm:ss')} /> ele = <TimePicker defaultValue={moment('00:00:00', 'HH:mm:ss')} />;
} }
// 日期范围 // 日期范围
if (obj.type === 'range' && obj.format === 'date') { if (obj.type === 'range' && obj.format === 'date') {
ele = <DatePicker /> ele = <DatePicker />;
} }
// 日期选择 // 日期选择
if (obj.type === 'string' && obj.format === 'date') { if (obj.type === 'string' && obj.format === 'date') {
ele = <RangePicker /> ele = <RangePicker />;
} }
return <FormItem return (
label={obj.title} <FormItem label={obj.title} name={key}>
name={key}
>
{ele} {ele}
</FormItem> </FormItem>
);
})} })}
<div className={styles.btnWrap}> <div className={styles.btnWrap}>
<Button size='small' className={styles.btn} type={'primary'} onClick={onClickSave}></Button> <Button
size="small"
className={styles.btn}
type={'primary'}
onClick={onClickSave}
>
</Button>
</div> </div>
</Form> : </Form>
) : (
<Empty <Empty
description={'请编辑投放配置schema'} description={'请编辑投放配置schema'}
image={Empty.PRESENTED_IMAGE_SIMPLE} image={Empty.PRESENTED_IMAGE_SIMPLE}
@ -106,4 +150,4 @@ const SchemaForm: React.FC<IProps> = (props) => {
); );
}; };
export default SchemaForm export default SchemaForm;

6
packages/core/src/hooks/useClickAway.ts

@ -1,8 +1,4 @@
import { import { useRef, useEffect, MutableRefObject } from 'react';
useRef,
useEffect,
MutableRefObject
} from 'react';
type EventType = MouseEvent | TouchEvent; type EventType = MouseEvent | TouchEvent;
type TargetElement = HTMLElement | Element | Document | Window; type TargetElement = HTMLElement | Element | Document | Window;

23
packages/core/src/mods/flowChart/codeEditorModel/index.tsx

@ -1,7 +1,4 @@
import React, { import React, { useState, useEffect } from 'react';
useState,
useEffect
} from 'react';
import 'antd/es/modal/style'; import 'antd/es/modal/style';
import styles from './index.module.less'; import styles from './index.module.less';
@ -19,16 +16,14 @@ interface IProps {
} }
const CodeEditModal: React.FC<IProps> = (props) => { const CodeEditModal: React.FC<IProps> = (props) => {
const { title = '编辑代码', flowChart } = props; const { title = '编辑代码', flowChart } = props;
const [code, setCode] = useState<string>(''); const [code, setCode] = useState<string>('');
const [visible, setVisible] = useState<boolean>(false); const [visible, setVisible] = useState<boolean>(false);
const updateNodeCode = (code: string): void => { const updateNodeCode = (code: string): void => {
const cell = flowChart.getSelectedCells()[0]; const cell = flowChart.getSelectedCells()[0];
const { code: oldCode, dependencies } = cell.getData(); const { code: oldCode, dependencies } = cell.getData();
if(code === oldCode) { if (code === oldCode) {
return; return;
} }
@ -55,7 +50,7 @@ const CodeEditModal: React.FC<IProps> = (props) => {
const newDeps = { ...excludeDeps, ...deps }; const newDeps = { ...excludeDeps, ...deps };
cell.setData({ cell.setData({
code, code,
dependencies: JSON.stringify(newDeps, null, 2) dependencies: JSON.stringify(newDeps, null, 2),
}); });
// NOTE: notify basic panel to update dependency // NOTE: notify basic panel to update dependency
flowChart.trigger('settingBar.basicPanel:forceUpdate'); flowChart.trigger('settingBar.basicPanel:forceUpdate');
@ -110,9 +105,15 @@ const CodeEditModal: React.FC<IProps> = (props) => {
visible={visible} visible={visible}
onCancel={onCancel} onCancel={onCancel}
footer={[ footer={[
<Button key={'cancel'} onClick={onCancel}></Button>, <Button key={'cancel'} onClick={onCancel}>
<Button key={'runCode'} type={'primary'} ghost onClick={onRunCode}></Button>,
<Button key={'saveCode'} type={'primary'} onClick={onOk}></Button>, </Button>,
<Button key={'runCode'} type={'primary'} ghost onClick={onRunCode}>
</Button>,
<Button key={'saveCode'} type={'primary'} onClick={onOk}>
</Button>,
]} ]}
> >
<CodeEditor <CodeEditor

9
packages/core/src/mods/flowChart/codeRunModal/index.tsx

@ -1,8 +1,4 @@
import React, { import React, { useState, useEffect, useCallback } from 'react';
useState,
useEffect,
useCallback
} from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -16,7 +12,6 @@ interface IEditModalProps {
} }
const CodeRunModal: React.FC<IEditModalProps> = (props): JSX.Element => { const CodeRunModal: React.FC<IEditModalProps> = (props): JSX.Element => {
const { title = '执行代码', flowChart } = props; const { title = '执行代码', flowChart } = props;
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
@ -42,7 +37,7 @@ const CodeRunModal: React.FC<IEditModalProps> = (props): JSX.Element => {
footer={null} footer={null}
onCancel={onClose} onCancel={onClose}
> >
<CodeRun flowChart={flowChart}/> <CodeRun flowChart={flowChart} />
</Modal> </Modal>
); );
}; };

52
packages/core/src/mods/flowChart/contextMenu/index.tsx

@ -1,7 +1,4 @@
import React, { import React, { useRef, useCallback } from 'react';
useRef,
useCallback
} from 'react';
import styles from '../index.module.less'; import styles from '../index.module.less';
@ -30,25 +27,31 @@ interface IMenuConfig {
const menuConfigMap: { [scene: string]: IMenuConfig[] } = { const menuConfigMap: { [scene: string]: IMenuConfig[] } = {
node: nodeMenuConfig, node: nodeMenuConfig,
blank: blankMenuConfig blank: blankMenuConfig,
}; };
const FlowChartContextMenu: React.FC<IProps> = props => { const FlowChartContextMenu: React.FC<IProps> = (props) => {
const menuRef = useRef(null); const menuRef = useRef(null);
const { x, y, scene, visible, flowChart } = props; const { x, y, scene, visible, flowChart } = props;
const menuConfig = menuConfigMap[scene]; const menuConfig = menuConfigMap[scene];
useClickAway(() => onClickAway(), menuRef); useClickAway(() => onClickAway(), menuRef);
const onClickAway = useCallback(() => flowChart.trigger('graph:hideContextMenu'), [flowChart]); const onClickAway = useCallback(
const onClickMenu = useCallback(({ key }) => { () => flowChart.trigger('graph:hideContextMenu'),
[flowChart],
);
const onClickMenu = useCallback(
({ key }) => {
const handlerMap = Helper.makeMenuHandlerMap(menuConfig); const handlerMap = Helper.makeMenuHandlerMap(menuConfig);
const handler = handlerMap[key]; const handler = handlerMap[key];
if (handler) { if (handler) {
onClickAway(); onClickAway();
handler(flowChart); handler(flowChart);
} }
}, [flowChart, menuConfig]); },
[flowChart, menuConfig],
);
return !visible ? null : ( return !visible ? null : (
<div <div
@ -56,11 +59,7 @@ const FlowChartContextMenu: React.FC<IProps> = props => {
className={styles.contextMenu} className={styles.contextMenu}
style={{ left: x, top: y }} style={{ left: x, top: y }}
> >
<Menu <Menu mode={'vertical'} selectable={false} onClick={onClickMenu}>
mode={'vertical'}
selectable={false}
onClick={onClickMenu}
>
{Helper.makeMenuContent(flowChart, menuConfig)} {Helper.makeMenuContent(flowChart, menuConfig)}
</Menu> </Menu>
</div> </div>
@ -83,15 +82,27 @@ const Helper = {
}, },
makeMenuContent(flowChart: Graph, menuConfig: IMenuConfig[]) { makeMenuContent(flowChart: Graph, menuConfig: IMenuConfig[]) {
const loop = (config: IMenuConfig[]) => { const loop = (config: IMenuConfig[]) => {
return config.map(item => { return config.map((item) => {
let content = null; let content = null;
let { key, title, icon, children, disabled = false, showDividerBehind } = item; let {
key,
title,
icon,
children,
disabled = false,
showDividerBehind,
} = item;
if (typeof disabled === 'function') { if (typeof disabled === 'function') {
disabled = disabled(flowChart); disabled = disabled(flowChart);
} }
if (children && children.length > 0) { if (children && children.length > 0) {
content = ( content = (
<Menu.SubMenu key={key} icon={icon} title={title} disabled={disabled}> <Menu.SubMenu
key={key}
icon={icon}
title={title}
disabled={disabled}
>
{loop(children)} {loop(children)}
</Menu.SubMenu> </Menu.SubMenu>
); );
@ -102,14 +113,11 @@ const Helper = {
</Menu.Item> </Menu.Item>
); );
} }
return [ return [content, showDividerBehind && <Menu.Divider />];
content,
showDividerBehind && <Menu.Divider />
];
}); });
}; };
return loop(menuConfig); return loop(menuConfig);
} },
}; };
export default FlowChartContextMenu; export default FlowChartContextMenu;

6
packages/core/src/mods/flowChart/contextMenu/menuConfig/blank.tsx

@ -10,15 +10,15 @@ const blankMenuConfig = [
key: 'selectAll', key: 'selectAll',
title: '全选', title: '全选',
icon: <XIcon type={'icon-select-all'} />, icon: <XIcon type={'icon-select-all'} />,
handler: shortcuts.selectAll.handler handler: shortcuts.selectAll.handler,
}, },
{ {
key: 'paste', key: 'paste',
title: '粘贴', title: '粘贴',
icon: <SnippetsOutlined />, icon: <SnippetsOutlined />,
disabled: (flowChart: Graph) => flowChart.isClipboardEmpty(), disabled: (flowChart: Graph) => flowChart.isClipboardEmpty(),
handler: shortcuts.paste.handler handler: shortcuts.paste.handler,
} },
]; ];
export default blankMenuConfig; export default blankMenuConfig;

5
packages/core/src/mods/flowChart/contextMenu/menuConfig/index.ts

@ -1,7 +1,4 @@
import nodeMenuConfig from './node'; import nodeMenuConfig from './node';
import blankMenuConfig from './blank'; import blankMenuConfig from './blank';
export { export { nodeMenuConfig, blankMenuConfig };
nodeMenuConfig,
blankMenuConfig
};

16
packages/core/src/mods/flowChart/contextMenu/menuConfig/node.tsx

@ -18,13 +18,13 @@ const nodeMenuConfig = [
key: 'copy', key: 'copy',
title: '复制', title: '复制',
icon: <CopyOutlined />, icon: <CopyOutlined />,
handler: shortcuts.copy.handler handler: shortcuts.copy.handler,
}, },
{ {
key: 'delete', key: 'delete',
title: '删除', title: '删除',
icon: <DeleteOutlined />, icon: <DeleteOutlined />,
handler: shortcuts.delete.handler handler: shortcuts.delete.handler,
}, },
{ {
key: 'rename', key: 'rename',
@ -33,20 +33,20 @@ const nodeMenuConfig = [
showDividerBehind: true, showDividerBehind: true,
handler() { handler() {
// TODO // TODO
} },
}, },
{ {
key: 'bringToTop', key: 'bringToTop',
title: '置于顶层', title: '置于顶层',
icon: <XIcon type={'icon-bring-to-top'} />, icon: <XIcon type={'icon-bring-to-top'} />,
handler: shortcuts.bringToTop.handler handler: shortcuts.bringToTop.handler,
}, },
{ {
key: 'bringToBack', key: 'bringToBack',
title: '置于底层', title: '置于底层',
icon: <XIcon type={'icon-bring-to-bottom'} />, icon: <XIcon type={'icon-bring-to-bottom'} />,
showDividerBehind: true, showDividerBehind: true,
handler: shortcuts.bringToBack.handler handler: shortcuts.bringToBack.handler,
}, },
{ {
key: 'editCode', key: 'editCode',
@ -57,7 +57,7 @@ const nodeMenuConfig = [
}, },
handler(flowChart: Graph) { handler(flowChart: Graph) {
flowChart.trigger('graph:editCode'); flowChart.trigger('graph:editCode');
} },
}, },
{ {
key: 'executeCode', key: 'executeCode',
@ -68,8 +68,8 @@ const nodeMenuConfig = [
}, },
handler(flowChart: Graph) { handler(flowChart: Graph) {
flowChart.trigger('graph:runCode'); flowChart.trigger('graph:runCode');
} },
} },
]; ];
export default nodeMenuConfig; export default nodeMenuConfig;

39
packages/core/src/mods/flowChart/createFlowChart.ts

@ -12,7 +12,7 @@ import MiniMapSimpleNode from '../../components/miniMapSimpleNode';
Object.values(schemas).forEach((schema) => { Object.values(schemas).forEach((schema) => {
const { base, ...rest } = schema; const { base, ...rest } = schema;
base.define(rest); base.define(rest);
}) }),
); );
const registerEvents = (flowChart: Graph): void => { const registerEvents = (flowChart: Graph): void => {
@ -59,18 +59,31 @@ const registerEvents = (flowChart: Graph): void => {
flowChart.trigger('graph:editCode'); flowChart.trigger('graph:editCode');
}); });
flowChart.on('blank:contextmenu', (args) => { flowChart.on('blank:contextmenu', (args) => {
const { e: { clientX, clientY } } = args; const {
e: { clientX, clientY },
} = args;
flowChart.cleanSelection(); flowChart.cleanSelection();
flowChart.trigger('graph:showContextMenu', { x: clientX, y: clientY, scene: 'blank' }); flowChart.trigger('graph:showContextMenu', {
x: clientX,
y: clientY,
scene: 'blank',
});
}); });
flowChart.on('node:contextmenu', (args) => { flowChart.on('node:contextmenu', (args) => {
const { e: { clientX, clientY }, node } = args; const {
e: { clientX, clientY },
node,
} = args;
// NOTE: if the clicked node is not in the selected nodes, then clear selection // NOTE: if the clicked node is not in the selected nodes, then clear selection
if(!flowChart.getSelectedCells().includes(node)) { if (!flowChart.getSelectedCells().includes(node)) {
flowChart.cleanSelection(); flowChart.cleanSelection();
flowChart.select(node); flowChart.select(node);
} }
flowChart.trigger('graph:showContextMenu', { x: clientX, y: clientY, scene: 'node' }); flowChart.trigger('graph:showContextMenu', {
x: clientX,
y: clientY,
scene: 'node',
});
}); });
}; };
@ -81,7 +94,10 @@ const registerShortcuts = (flowChart: Graph): void => {
}); });
}; };
const createFlowChart = (container: HTMLDivElement, miniMapContainer: HTMLDivElement): Graph => { const createFlowChart = (
container: HTMLDivElement,
miniMapContainer: HTMLDivElement,
): Graph => {
const flowChart = new Graph({ const flowChart = new Graph({
container, container,
rotating: false, rotating: false,
@ -101,7 +117,12 @@ const createFlowChart = (container: HTMLDivElement, miniMapContainer: HTMLDivEle
router: { router: {
name: 'manhattan', name: 'manhattan',
}, },
validateConnection({ sourceView, targetView, sourceMagnet, targetMagnet }) { validateConnection({
sourceView,
targetView,
sourceMagnet,
targetMagnet,
}) {
if (!sourceMagnet) { if (!sourceMagnet) {
return false; return false;
} else if (!targetMagnet) { } else if (!targetMagnet) {
@ -126,7 +147,7 @@ const createFlowChart = (container: HTMLDivElement, miniMapContainer: HTMLDivEle
rubberband: true, rubberband: true,
movable: true, movable: true,
strict: true, strict: true,
showNodeSelectionBox: true showNodeSelectionBox: true,
}, },
// https://x6.antv.vision/zh/docs/tutorial/basic/snapline // https://x6.antv.vision/zh/docs/tutorial/basic/snapline
snapline: { snapline: {

24
packages/core/src/mods/flowChart/index.tsx

@ -1,8 +1,4 @@
import React, { import React, { useRef, useState, useEffect } from 'react';
useRef,
useState,
useEffect
} from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -29,7 +25,7 @@ const defaultMenuInfo = {
x: 0, x: 0,
y: 0, y: 0,
scene: 'blank', scene: 'blank',
visible: false visible: false,
}; };
const FlowChart: React.FC<IProps> = (props) => { const FlowChart: React.FC<IProps> = (props) => {
@ -38,7 +34,9 @@ const FlowChart: React.FC<IProps> = (props) => {
const graphRef = useRef<HTMLDivElement>(null); const graphRef = useRef<HTMLDivElement>(null);
const miniMapRef = useRef<HTMLDivElement>(null); const miniMapRef = useRef<HTMLDivElement>(null);
const [flowChart, setFlowChart] = useState<Graph>(); const [flowChart, setFlowChart] = useState<Graph>();
const [contextMenuInfo, setContextMenuInfo] = useState<IMenuInfo>(defaultMenuInfo); const [contextMenuInfo, setContextMenuInfo] = useState<IMenuInfo>(
defaultMenuInfo,
);
useEffect(() => { useEffect(() => {
if (graphRef.current && miniMapRef.current) { if (graphRef.current && miniMapRef.current) {
@ -53,7 +51,7 @@ const FlowChart: React.FC<IProps> = (props) => {
useEffect(() => { useEffect(() => {
const handler = () => { const handler = () => {
requestAnimationFrame(() => { requestAnimationFrame(() => {
if(flowChart && wrapperRef && wrapperRef.current) { if (flowChart && wrapperRef && wrapperRef.current) {
const width = wrapperRef.current.clientWidth; const width = wrapperRef.current.clientWidth;
const height = wrapperRef.current.clientHeight; const height = wrapperRef.current.clientHeight;
flowChart.resize(width, height); flowChart.resize(width, height);
@ -85,7 +83,7 @@ const FlowChart: React.FC<IProps> = (props) => {
flowChart.off('graph:showContextMenu', showHandler); flowChart.off('graph:showContextMenu', showHandler);
flowChart.off('graph:hideContextMenu', hideHandler); flowChart.off('graph:hideContextMenu', hideHandler);
} }
} };
}, [flowChart]); }, [flowChart]);
const fetchData = (flowChart: Graph) => { const fetchData = (flowChart: Graph) => {
@ -104,9 +102,11 @@ const FlowChart: React.FC<IProps> = (props) => {
<div className={styles.container} ref={wrapperRef}> <div className={styles.container} ref={wrapperRef}>
<div className={styles.flowChart} ref={graphRef} /> <div className={styles.flowChart} ref={graphRef} />
<div className={styles.miniMap} ref={miniMapRef} /> <div className={styles.miniMap} ref={miniMapRef} />
{flowChart && <CodeRunModal flowChart={flowChart}/>} {flowChart && <CodeRunModal flowChart={flowChart} />}
{flowChart && <CodeEditorModal flowChart={flowChart}/>} {flowChart && <CodeEditorModal flowChart={flowChart} />}
{flowChart && <FlowChartContextMenu {...contextMenuInfo} flowChart={flowChart} />} {flowChart && (
<FlowChartContextMenu {...contextMenuInfo} flowChart={flowChart} />
)}
</div> </div>
); );
}; };

16
packages/core/src/mods/flowChart/registerServerStorage.ts

@ -22,11 +22,12 @@ const enqueue = (cellType: string, actionType: ActionType, data: any) => {
return; return;
} }
const foundIndex = memQueue.findIndex((item) => ( const foundIndex = memQueue.findIndex(
(item) =>
item.type === cellType && item.type === cellType &&
item.actionType === actionType && item.actionType === actionType &&
item.data.id === data.id item.data.id === data.id,
)); );
if (foundIndex > -1) { if (foundIndex > -1) {
const deleted = memQueue.splice(foundIndex, 1)[0]; const deleted = memQueue.splice(foundIndex, 1)[0];
merge(deleted.data, data); merge(deleted.data, data);
@ -34,8 +35,13 @@ const enqueue = (cellType: string, actionType: ActionType, data: any) => {
memQueue.push({ type: cellType, actionType, data }); memQueue.push({ type: cellType, actionType, data });
}; };
let modifyActionTimer: number = -1; let modifyActionTimer = -1;
const save = (flowChart: Graph, cellType: string, actionType: ActionType, data: any) => { const save = (
flowChart: Graph,
cellType: string,
actionType: ActionType,
data: any,
) => {
enqueue(cellType, actionType, data); enqueue(cellType, actionType, data);
clearTimeout(modifyActionTimer); clearTimeout(modifyActionTimer);
modifyActionTimer = window.setTimeout(() => { modifyActionTimer = window.setTimeout(() => {

12
packages/core/src/mods/header/connectStatus/editModal.tsx

@ -56,10 +56,18 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
onCancel={onCancel} onCancel={onCancel}
> >
<Form ref={formRef} labelCol={{ span: 4 }} initialValues={localConfig}> <Form ref={formRef} labelCol={{ span: 4 }} initialValues={localConfig}>
<Form.Item label={'ip'} name={'ip'} rules={makeRules(IP_REGEX, 'IP格式不合法')}> <Form.Item
label={'ip'}
name={'ip'}
rules={makeRules(IP_REGEX, 'IP格式不合法')}
>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label={'port'} name={'port'} rules={makeRules(PORT_REGEX, 'port格式不合法')}> <Form.Item
label={'port'}
name={'port'}
rules={makeRules(PORT_REGEX, 'port格式不合法')}
>
<Input /> <Input />
</Form.Item> </Form.Item>
</Form> </Form>

27
packages/core/src/mods/header/export/exportModal.tsx

@ -15,7 +15,6 @@ interface IExportModalProps {
} }
const ExportModal: React.FC<IExportModalProps> = (props) => { const ExportModal: React.FC<IExportModalProps> = (props) => {
const { flowChart, visible, onClose } = props; const { flowChart, visible, onClose } = props;
const onExportDSL = () => { const onExportDSL = () => {
const dsl = JSON.stringify(flowChart.toJSON(), null, 2); const dsl = JSON.stringify(flowChart.toJSON(), null, 2);
@ -32,9 +31,12 @@ const ExportModal: React.FC<IExportModalProps> = (props) => {
}); });
}; };
const onExportFlowChart = () => { const onExportFlowChart = () => {
flowChart.toPNG((dataUri: string) => { flowChart.toPNG(
(dataUri: string) => {
DataUri.downloadDataUri(dataUri, 'flowChart.png'); DataUri.downloadDataUri(dataUri, 'flowChart.png');
}, { padding: 50, ratio: '3.0' }); },
{ padding: 50, ratio: '3.0' },
);
}; };
return ( return (
@ -48,15 +50,24 @@ const ExportModal: React.FC<IExportModalProps> = (props) => {
> >
<div className={styles.modalContent}> <div className={styles.modalContent}>
<div className={styles.downloadWrap} onClick={onExportDSL}> <div className={styles.downloadWrap} onClick={onExportDSL}>
<img className={styles.picPreview} src="//img.alicdn.com/imgextra/i2/O1CN01lvanDL1YKp54hGgMS_!!6000000003041-2-tps-247-247.png" /> <img
className={styles.picPreview}
src="//img.alicdn.com/imgextra/i2/O1CN01lvanDL1YKp54hGgMS_!!6000000003041-2-tps-247-247.png"
/>
<span>DSL</span> <span>DSL</span>
</div> </div>
<div className={styles.downloadWrap} onClick={onExportCode}> <div className={styles.downloadWrap} onClick={onExportCode}>
<img className={styles.picPreview} src="//img.alicdn.com/imgextra/i3/O1CN01V3wg6S27JuqePTXZv_!!6000000007777-2-tps-247-247.png" /> <img
className={styles.picPreview}
src="//img.alicdn.com/imgextra/i3/O1CN01V3wg6S27JuqePTXZv_!!6000000007777-2-tps-247-247.png"
/>
<span></span> <span></span>
</div> </div>
<div className={styles.downloadWrap} onClick={onExportFlowChart}> <div className={styles.downloadWrap} onClick={onExportFlowChart}>
<img className={styles.picPreview} src="//img.alicdn.com/imgextra/i1/O1CN01sVItyC1exeLraUhf6_!!6000000003938-2-tps-247-247.png" /> <img
className={styles.picPreview}
src="//img.alicdn.com/imgextra/i1/O1CN01sVItyC1exeLraUhf6_!!6000000003938-2-tps-247-247.png"
/>
<span></span> <span></span>
</div> </div>
</div> </div>
@ -78,7 +89,7 @@ const Helper = {
Helper.recursiveZip(dir, val); Helper.recursiveZip(dir, val);
} }
} }
} },
} };
export default ExportModal; export default ExportModal;

11
packages/core/src/mods/header/export/index.tsx

@ -1,4 +1,4 @@
import React, {useState} from 'react'; import React, { useState } from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -10,9 +10,8 @@ interface IProps {
flowChart: Graph; flowChart: Graph;
} }
const Export: React.FC<IProps> = props => { const Export: React.FC<IProps> = (props) => {
const { flowChart } = props;
const {flowChart} = props;
const [modalVisible, setModalVisible] = useState<boolean>(false); const [modalVisible, setModalVisible] = useState<boolean>(false);
const onOpenModal = () => setModalVisible(true); const onOpenModal = () => setModalVisible(true);
@ -20,7 +19,9 @@ const Export: React.FC<IProps> = props => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<Button type={'primary'} size={'small'} onClick={onOpenModal}></Button> <Button type={'primary'} size={'small'} onClick={onOpenModal}>
</Button>
<ExportModal <ExportModal
flowChart={flowChart} flowChart={flowChart}
visible={modalVisible} visible={modalVisible}

279
packages/core/src/mods/header/guide/guideModal.tsx

@ -1,82 +1,103 @@
import React from 'react'; import React from 'react';
import 'antd/es/modal/style'; import 'antd/es/modal/style';
import './index.less' import './index.less';
import { Modal, Tabs } from 'antd'; import { Modal, Tabs } from 'antd';
const { TabPane } = Tabs; const { TabPane } = Tabs;
interface IExportModalProps { interface IExportModalProps {
visible: boolean; visible: boolean;
onClose: () => void; onClose: () => void;
} }
const renderCode = (code: any) => { const renderCode = (code: any) => {
return <pre>{code}</pre> return <pre>{code}</pre>;
} };
const QuickStart: React.FC = () => { const QuickStart: React.FC = () => {
return <div> return (
<div>
<h2></h2> <h2></h2>
{renderCode(`$ git clone git@github.com:imgcook/imove.git\n {renderCode(`$ git clone git@github.com:imgcook/imove.git\n
$ cd imove\n $ cd imove\n
$ npm install\n $ npm install\n
$ npm run example`)} $ npm run example`)}
<a href="http://localhost:8000/">http://localhost:8000/</a> <a href="http://localhost:8000/">http://localhost:8000/</a>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523659304-84fc5548-3203-48b6-9ec8-81f0b0228245.png" /> <img
width="100%"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523659304-84fc5548-3203-48b6-9ec8-81f0b0228245.png"
/>
<h2></h2> <h2></h2>
<p></p> <p></p>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610529557793-63ef6842-079c-498a-8ad3-dfbdad7549bb.png" /> <img
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523745104-24e94b10-f8d7-4eb2-a282-ca32a19f7ccd.png" /> width="100%"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610529557793-63ef6842-079c-498a-8ad3-dfbdad7549bb.png"
/>
<img
width="100%"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523745104-24e94b10-f8d7-4eb2-a282-ca32a19f7ccd.png"
/>
<h2>使</h2> <h2>使</h2>
<p></p> <p></p>
<ol> <ol>
<li>线</li> <li>线</li>
<li></li> <li></li>
</ol> </ol>
<h3>1. 线</h3> <h3>1. 线</h3>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523805396-4211e9c2-b501-4b23-aeae-f55147d67e8d.png" /> <img
<p> zip 使</p> width="100%"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523805396-4211e9c2-b501-4b23-aeae-f55147d67e8d.png"
/>
<p>
zip
使
</p>
<h3>2. </h3> <h3>2. </h3>
<p> CLI</p> <p> CLI</p>
<pre> <pre>$ npm install -g @imove/cli</pre>
$ npm install -g @imove/cli
</pre>
<p>imove </p> <p>imove </p>
<pre> <pre>$ cd yourProject $ imove --init # imove -i</pre>
$ cd yourProject
$ imove --init # imove -i
</pre>
<p></p> <p></p>
<pre> <pre>$ imove --dev # imove -d</pre>
$ imove --dev # imove -d
</pre>
<p></p> <p></p>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610529177065-5000bb28-10f2-4053-a4bb-af98139f488e.png" /> <img
width="100%"
<p> <b> Ctrl + S</b> src <strong>logic</strong> imove </p> src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610529177065-5000bb28-10f2-4053-a4bb-af98139f488e.png"
/>
<p>
<b> Ctrl + S</b> src
<strong>logic</strong> imove
</p>
<b></b> <b></b>
<ol> <ol>
<li> logic.on ctx.emit </li> <li>
<li> logic.invoke </li> logic.on ctx.emit
</li>
<li>
logic.invoke
</li>
</ol> </ol>
</div> </div>
} );
};
const CreateNode: React.FC = () => { const CreateNode: React.FC = () => {
return ( return (
<div> <div>
<h2></h2> <h2></h2>
<p></p> <p>
<img width="100%" alt="创建节点" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528033956-df4c7cb8-2b35-4065-8f51-a8d98c02f532.png"></img>
</p>
<img
width="100%"
alt="创建节点"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528033956-df4c7cb8-2b35-4065-8f51-a8d98c02f532.png"
></img>
</div> </div>
) );
} };
const NodeType: React.FC = () => { const NodeType: React.FC = () => {
return ( return (
@ -84,99 +105,140 @@ const NodeType: React.FC = () => {
<h2></h2> <h2></h2>
<p> iMove 3 </p> <p> iMove 3 </p>
<ol> <ol>
<li>/</li> <li>
/
</li>
<li>//</li> <li>//</li>
<li></li> <li></li>
</ol> </ol>
<b> </b> <b> </b>
<p><b></b></p> <p>
<img width="100%" alt="节点类型" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528367101-6ff34826-8658-41e8-bb8b-66b254f14cde.png"></img>
<b></b>
</p>
<img
width="100%"
alt="节点类型"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528367101-6ff34826-8658-41e8-bb8b-66b254f14cde.png"
></img>
</div> </div>
) );
} };
const ConfigNode: React.FC = () => { const ConfigNode: React.FC = () => {
return ( return (
<div> <div>
<h2></h2> <h2></h2>
<p></p> <p></p>
<img width="100%" alt="配置节点" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528399738-19c54ce9-25f4-4fc0-93a8-580f9eb9f20f.png"></img> <img
width="100%"
alt="配置节点"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528399738-19c54ce9-25f4-4fc0-93a8-580f9eb9f20f.png"
></img>
<p>使</p> <p>使</p>
<ul> <ul>
<li></li> <li></li>
<li></li> <li></li>
<li>使</li> <li>
使
</li>
</ul> </ul>
</div> </div>
) );
} };
const CodeStyle: React.FC = () => { const CodeStyle: React.FC = () => {
return ( return (
<div> <div>
<h2></h2> <h2></h2>
<p></p> <p></p>
<img width="100%" alt="节点代码规范" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528493678-c08c4d08-380e-44ca-8dfd-33adb5c1604f.png"></img> <img
<p> js import npm export iMove export promise</p> width="100%"
<p> <b></b> </p> alt="节点代码规范"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610528493678-c08c4d08-380e-44ca-8dfd-33adb5c1604f.png"
></img>
<p>
js
import npm
export iMove
export promise
</p>
<p>
<b></b>
</p>
{renderCode(`export default async function() {\n {renderCode(`export default async function() {\n
return fetch('/api/isLogin')\n return fetch('/api/isLogin')\n
.then(res => res.json())\n .then(res => res.json())\n
.then(res => {\n .then(res => {\n
const {success, data: {isLogin} = {}} = res;\n const {success, data: {isLogin} = {}} = res\n
return success && isLogin;\n return success && isLogin\n
}).catch(err => {\n }).catch(err => {\n
console.log('fetch /api/isLogin failed, the err is:', err);\n console.log('fetch /api/isLogin failed, the err is:', err)\n
return false;\n return false\n
});\n })\n
}\n }\n
`)} `)}
<p><b></b> boolean </p> <p>
<b></b> boolean
</p>
</div> </div>
) );
} };
const NodeConnect: React.FC = () => { const NodeConnect: React.FC = () => {
return ( return (
<div> <div>
<h2></h2> <h2></h2>
<p> iMove pipe<b></b> boolean boolean </p> <p>
<p><b>profile接口</b><b></b></p> iMove
pipe
<b></b> boolean
boolean
</p>
<p>
<b>profile接口</b><b></b>
</p>
{renderCode(`// 节点: 请求profile接口\n {renderCode(`// 节点: 请求profile接口\n
export default async function() {\n export default async function() {\n
return fetch('/api/profile')\n return fetch('/api/profile')\n
.then(res => res.json())\n .then(res => res.json())\n
.then(res => {\n .then(res => {\n
const {success, data} = res;\n const {success, data} = res\n
return {success, data};\n return {success, data}\n
}).catch(err => {\n }).catch(err => {\n
console.log('fetch /api/isLogin failed, the err is:', err);\n console.log('fetch /api/isLogin failed, the err is:', err)\n
return {success: false};\n return {success: false}\n
});\n })\n
}\n }\n
`)} `)}
{renderCode(`// 节点: 接口成功\n {renderCode(`// 节点: 接口成功\n
export default async function(ctx) {\n export default async function(ctx) {\n
// 获取上游数据\n // 获取上游数据\n
const pipe = ctx.getPipe() || {};\n const pipe = ctx.getPipe() || {}\n
return pipe.success;\n return pipe.success\n
}\n }\n
`)} `)}
{renderCode(`// 节点: 返回数据\n {renderCode(`// 节点: 返回数据\n
const doSomething = (data) => {\n const doSomething = (data) => {\n
// TODO: 数据加工处理\n // TODO: 数据加工处理\n
return data;\n return data\n
};\n }\n
export default async function(ctx) {\n export default async function(ctx) {\n
// 这里获取到的上游数据,不是"接口成功"这个分支节点的返回值,而是"请求profile接口"这个节点的返回值\n // 这里获取到的上游数据,不是"接口成功"这个分支节点的返回值,而是"请求profile接口"这个节点的返回值\n
const pipe = ctx.getPipe() || {};\n const pipe = ctx.getPipe() || {}\n
ctx.emit('updateUI', {profileData: doSomething(pipe.data)});\n ctx.emit('updateUI', {profileData: doSomething(pipe.data)})\n
}\n }\n
`)} `)}
<b>ctx.getPipe</b> <b></b> <b>ctx.emit('eventName', data)</b> 使 <b>ctx.getPipe</b>{' '}
<b></b>{' '}
<b>ctx.emit('eventName', data)</b>{' '}
使
</div> </div>
) );
} };
const HowToUse: React.FC = () => { const HowToUse: React.FC = () => {
return ( return (
@ -189,47 +251,71 @@ const HowToUse: React.FC = () => {
</ol> </ol>
<h3>1. 线</h3> <h3>1. 线</h3>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523805396-4211e9c2-b501-4b23-aeae-f55147d67e8d.png" /> <img
<p> zip 使</p> width="100%"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523805396-4211e9c2-b501-4b23-aeae-f55147d67e8d.png"
/>
<p>
zip
使
</p>
<h3>2. </h3> <h3>2. </h3>
<p> CLI</p> <p> CLI</p>
<pre> <pre>$ npm install -g @imove/cli</pre>
$ npm install -g @imove/cli
</pre>
<p>imove </p> <p>imove </p>
<pre> <pre>$ cd yourProject $ imove --init # imove -i</pre>
$ cd yourProject
$ imove --init # imove -i
</pre>
<p></p> <p></p>
<pre> <pre>$ imove --dev # imove -d</pre>
$ imove --dev # imove -d
</pre>
<p></p> <p></p>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610529177065-5000bb28-10f2-4053-a4bb-af98139f488e.png" /> <img
width="100%"
src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610529177065-5000bb28-10f2-4053-a4bb-af98139f488e.png"
/>
<p> <b> Ctrl + S</b> src <strong>logic</strong> imove </p> <p>
<b> Ctrl + S</b> src
<strong>logic</strong> imove
</p>
<b></b> <b></b>
<ol> <ol>
<li> logic.on ctx.emit </li> <li>
<li> logic.invoke </li> logic.on ctx.emit
</li>
<li>
logic.invoke
</li>
</ol> </ol>
</div> </div>
) );
} };
const Document: React.FC = () => { const Document: React.FC = () => {
return ( return (
<div> <div>
<h2>使</h2> <h2>使</h2>
<div><a target="blank" href="https://www.yuque.com/imove/docs">使</a></div> <div>
<div><a target="blank" href="https://github.com/imgcook/imove/blob/master/README.md">github README</a></div> <a target="blank" href="https://www.yuque.com/imove/docs">
使
</a>
</div> </div>
) <div>
} <a
target="blank"
href="https://github.com/imgcook/imove/blob/master/README.md"
>
github README
</a>
</div>
</div>
);
};
const GuideModal: React.FC<IExportModalProps> = (props) => { const GuideModal: React.FC<IExportModalProps> = (props) => {
const { visible, onClose } = props; const { visible, onClose } = props;
@ -244,7 +330,7 @@ const GuideModal: React.FC<IExportModalProps> = (props) => {
onCancel={onClose} onCancel={onClose}
> >
<div className="guide-container"> <div className="guide-container">
<Tabs tabPosition='left'> <Tabs tabPosition="left">
<TabPane className="tabPane" tab="简单上手" key="1"> <TabPane className="tabPane" tab="简单上手" key="1">
<QuickStart /> <QuickStart />
</TabPane> </TabPane>
@ -270,7 +356,6 @@ const GuideModal: React.FC<IExportModalProps> = (props) => {
<Document /> <Document />
</TabPane> </TabPane>
</Tabs> </Tabs>
</div> </div>
</Modal> </Modal>
); );

12
packages/core/src/mods/header/guide/index.tsx

@ -3,8 +3,7 @@ import React, { useState } from 'react';
import { Button } from 'antd'; import { Button } from 'antd';
import GuideModal from './guideModal'; import GuideModal from './guideModal';
const Export: React.FC = props => { const Export: React.FC = (props) => {
const [modalVisible, setModalVisible] = useState<boolean>(false); const [modalVisible, setModalVisible] = useState<boolean>(false);
const onOpenModal = () => setModalVisible(true); const onOpenModal = () => setModalVisible(true);
@ -12,11 +11,10 @@ const Export: React.FC = props => {
return ( return (
<div> <div>
<Button type={'primary'} size={'small'} onClick={onOpenModal}></Button> <Button type={'primary'} size={'small'} onClick={onOpenModal}>
<GuideModal
visible={modalVisible} </Button>
onClose={onCloseModal} <GuideModal visible={modalVisible} onClose={onCloseModal} />
/>
</div> </div>
); );
}; };

11
packages/core/src/mods/header/importDSL/index.tsx

@ -9,16 +9,15 @@ interface IProps {
flowChart: Graph; flowChart: Graph;
} }
const ImportDSL: React.FC<IProps> = props => { const ImportDSL: React.FC<IProps> = (props) => {
const { flowChart } = props;
const {flowChart} = props;
const [disabled, setDisabled] = useState(false); const [disabled, setDisabled] = useState(false);
const beforeUpload = (file: any) => { const beforeUpload = (file: any) => {
setDisabled(true); setDisabled(true);
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (evt) => { reader.onload = (evt) => {
setDisabled(false); setDisabled(false);
if(!evt.target) { if (!evt.target) {
message.error('加载文件失败!'); message.error('加载文件失败!');
} else { } else {
const dsl = evt.target.result as string; const dsl = evt.target.result as string;
@ -41,7 +40,9 @@ const ImportDSL: React.FC<IProps> = props => {
showUploadList={false} showUploadList={false}
beforeUpload={beforeUpload} beforeUpload={beforeUpload}
> >
<Button type={'primary'} size={'small'}>DSL</Button> <Button type={'primary'} size={'small'}>
DSL
</Button>
</Upload> </Upload>
); );
}; };

7
packages/core/src/mods/settingBar/components/checkbox/index.tsx

@ -25,7 +25,12 @@ const Checkbox: React.FC<IProps> = (props) => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<p className={styles.titleText}>{title}</p> <p className={styles.titleText}>{title}</p>
<AntdCheckbox name={name} checked={value} disabled={disabled} onChange={onChange} /> <AntdCheckbox
name={name}
checked={value}
disabled={disabled}
onChange={onChange}
/>
</div> </div>
); );
}; };

7
packages/core/src/mods/settingBar/components/input/index.tsx

@ -24,7 +24,12 @@ const Input: React.FC<IProps> = (props) => {
return ( return (
<div> <div>
<p className={styles.titleText}>{title}</p> <p className={styles.titleText}>{title}</p>
<AntdInput name={name} value={value} disabled={disabled} onChange={onChange} /> <AntdInput
name={name}
value={value}
disabled={disabled}
onChange={onChange}
/>
</div> </div>
); );
}; };

143
packages/core/src/mods/settingBar/components/json/index.tsx

@ -7,13 +7,13 @@ import JsonView from 'react-json-view';
import { Button, Tabs, Modal, Card, message } from 'antd'; import { Button, Tabs, Modal, Card, message } from 'antd';
import { safeParse } from '../../../../utils'; import { safeParse } from '../../../../utils';
import CodeEditor from '../../../../components/codeEditor'; import CodeEditor from '../../../../components/codeEditor';
import SchemaForm from '../../../../components/schemaForm' import SchemaForm from '../../../../components/schemaForm';
import { compData } from './json' import { compData } from './json';
import styles from './index.module.less'; import styles from './index.module.less';
const { TabPane } = Tabs; const { TabPane } = Tabs;
const JSON_TAB = 'JsonTab' const JSON_TAB = 'JsonTab';
const VISUAL_TAB = 'VisualTab' const VISUAL_TAB = 'VisualTab';
interface IJsonProps { interface IJsonProps {
value: any; value: any;
@ -25,38 +25,39 @@ interface IJsonProps {
} }
interface IConfigData { interface IConfigData {
[key: string]: any [key: string]: any;
} }
interface ISchemaProps { interface ISchemaProps {
type: string, type: string;
properties: { [key: string]: any } properties: { [key: string]: any };
} }
const Json: React.FC<IJsonProps> = (props) => { const Json: React.FC<IJsonProps> = (props) => {
const { title, value, isConfig, onValueChange, selectedCell } = props; const { title, value, isConfig, onValueChange, selectedCell } = props;
const [visible, setVisible] = useState<boolean>(false); const [visible, setVisible] = useState<boolean>(false);
const [schema, setSchema] = useState<ISchemaProps>({ type: '', properties: {} }) const [schema, setSchema] = useState<ISchemaProps>({
const [configData, setConfigData] = useState<IConfigData>({}); type: '',
properties: {},
});
const defaultSchema = useMemo(() => { const defaultSchema = useMemo(() => {
if (selectedCell) { if (selectedCell) {
const { configSchema } = selectedCell.getData(); const { configSchema } = selectedCell.getData();
if (configSchema) { if (configSchema) {
const schema = JSON.parse(configSchema).schema const schema = JSON.parse(configSchema).schema;
setSchema(schema) setSchema(schema);
return schema return schema;
} }
} }
}, [selectedCell]) }, [selectedCell]);
const defaultConfigData = useMemo(() => { const defaultConfigData = useMemo(() => {
if (selectedCell) { if (selectedCell) {
const { configData = {} } = selectedCell.getData(); const { configData = {} } = selectedCell.getData();
setConfigData(configData); return configData;
return configData
} }
}, [selectedCell]) }, [selectedCell]);
const onClickEdit = (): void => { const onClickEdit = (): void => {
setVisible(true); setVisible(true);
@ -71,27 +72,38 @@ const Json: React.FC<IJsonProps> = (props) => {
const changeSchema = (schema: ISchemaProps) => { const changeSchema = (schema: ISchemaProps) => {
setVisible(false); setVisible(false);
setSchema(schema); setSchema(schema);
} };
const changeConfigData = (configData: IConfigData) => { const changeConfigData = (configData: IConfigData) => {
selectedCell && selectedCell.setData({ configData }); selectedCell && selectedCell.setData({ configData });
} };
return ( return (
<Card title={title} extra={<Button type="link" onClick={onClickEdit}></Button>}> <Card
{!isConfig && <JsonView title={title}
extra={
<Button type="link" onClick={onClickEdit}>
</Button>
}
>
{!isConfig && (
<JsonView
name={null} name={null}
collapsed={true} collapsed={true}
enableClipboard={false} enableClipboard={false}
displayDataTypes={false} displayDataTypes={false}
displayObjectSize={false} displayObjectSize={false}
src={safeParse(value)} src={safeParse(value)}
/>} />
)}
{isConfig && <SchemaForm {isConfig && (
<SchemaForm
schema={schema} schema={schema}
changeConfigData={changeConfigData} changeConfigData={changeConfigData}
configData={defaultConfigData} /> configData={defaultConfigData}
} />
)}
<EditModal <EditModal
title={title} title={title}
@ -115,62 +127,65 @@ interface IEditorModalProps {
defaultSchema: object; defaultSchema: object;
onOk: (val: string) => void; onOk: (val: string) => void;
onCancel: () => void; onCancel: () => void;
changeSchema: (val: ISchemaProps) => void changeSchema: (val: ISchemaProps) => void;
} }
interface FormInstance { interface FormInstance {
setValue: (val: object) => void; setValue: (val: object) => void;
getValue: () => { getValue: () => {
schema: ISchemaProps schema: ISchemaProps;
} };
} }
const EditModal: React.FC<IEditorModalProps> = (props) => { const EditModal: React.FC<IEditorModalProps> = (props) => {
const { visible, title, value, onOk, onCancel, defaultSchema } = props; const { visible, title, value, onOk, onCancel, defaultSchema } = props;
const [json, setJson] = useState<string>(''); const [json, setJson] = useState<string>('');
const [tab, setTab] = useState<string>(VISUAL_TAB) const [tab, setTab] = useState<string>(VISUAL_TAB);
const formRef = useRef<FormInstance>(null); const formRef = useRef<FormInstance>(null);
const defaultValue = useMemo(() => { const defaultValue = useMemo(() => {
if (defaultSchema && Object.keys(defaultSchema).length > 0) { if (defaultSchema && Object.keys(defaultSchema).length > 0) {
let codeObj = { const codeObj = {
"schema": Object.assign({}, defaultSchema), schema: Object.assign({}, defaultSchema),
"displayType": "row", displayType: 'row',
"showDescIcon": true showDescIcon: true,
} };
return codeObj return codeObj;
} }
}, [defaultSchema]) }, [defaultSchema]);
const tabChange = (tab: string) => { const tabChange = (tab: string) => {
if (tab === JSON_TAB) { if (tab === JSON_TAB) {
setTab(JSON_TAB) setTab(JSON_TAB);
form2code() form2code();
} }
if (tab === VISUAL_TAB) { if (tab === VISUAL_TAB) {
setTab(VISUAL_TAB) setTab(VISUAL_TAB);
code2form() code2form();
}
} }
};
const form2code = () => { const form2code = () => {
const formSchema = formRef.current && formRef.current.getValue() const formSchema = formRef.current && formRef.current.getValue();
setJson(JSON.stringify(formSchema, null, 2)) setJson(JSON.stringify(formSchema, null, 2));
} };
const code2form = () => { const code2form = () => {
try { try {
const codeObj = JSON.parse(json) const codeObj = JSON.parse(json);
formRef.current && formRef.current.setValue(codeObj) formRef.current && formRef.current.setValue(codeObj);
} catch (error) { } catch (error) {
console.log('can\'t parse code string to form schema, the error is:', error.message); console.log(
} "can't parse code string to form schema, the error is:",
error.message,
);
} }
};
const onJsonChange = (ev: any, newJson: string | undefined = ''): void => { const onJsonChange = (ev: any, newJson: string | undefined = ''): void => {
setJson(newJson) setJson(newJson);
props.isConfig && code2form() props.isConfig && code2form();
} };
// life // life
useEffect(() => { useEffect(() => {
@ -186,12 +201,12 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
try { try {
if (props.isConfig) { if (props.isConfig) {
if (tab === VISUAL_TAB && formRef.current) { if (tab === VISUAL_TAB && formRef.current) {
const value = formRef.current.getValue() const value = formRef.current.getValue();
props.changeSchema(formRef.current.getValue().schema) props.changeSchema(formRef.current.getValue().schema);
setJson(JSON.stringify(value, null, 2)) setJson(JSON.stringify(value, null, 2));
onOk(JSON.stringify(value)); onOk(JSON.stringify(value));
} else { } else {
props.changeSchema(JSON.parse(json).schema) props.changeSchema(JSON.parse(json).schema);
onOk(json); onOk(json);
} }
} else { } else {
@ -215,22 +230,29 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
onOk={onClickOk} onOk={onClickOk}
onCancel={onCancel} onCancel={onCancel}
> >
{props.isConfig ? {props.isConfig ? (
<Tabs <Tabs
type="card" type="card"
tabBarGutter={0} tabBarGutter={0}
defaultActiveKey={'basic'} defaultActiveKey={'basic'}
onChange={tabChange} onChange={tabChange}
tabBarStyle={{ display: 'flex', flex: 1, justifyContent: 'center', alignItems: 'center' }} tabBarStyle={{
display: 'flex',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}
> >
<TabPane className={styles.tabPane} tab={'可视化'} key={'VisualTab'}> <TabPane className={styles.tabPane} tab={'可视化'} key={'VisualTab'}>
<div className={styles.form}> <div className={styles.form}>
<Generator <Generator
ref={formRef} ref={formRef}
settings={[{ settings={[
{
title: '表单组件库', title: '表单组件库',
widgets: compData, widgets: compData,
}]} },
]}
defaultValue={defaultValue} defaultValue={defaultValue}
/> />
</div> </div>
@ -245,7 +267,8 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
/> />
</div> </div>
</TabPane> </TabPane>
</Tabs> : </Tabs>
) : (
<CodeEditor <CodeEditor
value={json} value={json}
width={'100%'} width={'100%'}
@ -253,7 +276,7 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
language={'json'} language={'json'}
onChange={onJsonChange} onChange={onJsonChange}
/> />
} )}
</Modal> </Modal>
); );
}; };

198
packages/core/src/mods/settingBar/components/json/json.ts

@ -1,133 +1,103 @@
const compData = [ const compData = [
{ {
"text": 'Input', text: 'Input',
"name": 'input', name: 'input',
"schema": { schema: {
"title": "Input", title: 'Input',
"type": "string", type: 'string',
"description": "输入框" description: '输入框',
} },
}, },
{ {
"text": 'Textarea', text: 'Textarea',
"name": 'textarea', name: 'textarea',
"schema": { schema: {
"title": "Textarea", title: 'Textarea',
"type": "string", type: 'string',
"format": "textarea", format: 'textarea',
"description": "文本编辑框" description: '文本编辑框',
} },
}, },
{ {
"text": 'Switch', text: 'Switch',
"name": 'allBoolean', name: 'allBoolean',
"schema": { schema: {
"title": "Switch", title: 'Switch',
"type": "boolean", type: 'boolean',
"ui:widget": "switch", 'ui:widget': 'switch',
"description": "开关控制" description: '开关控制',
} },
}, },
{ {
"text": 'Select', text: 'Select',
"name": 'select', name: 'select',
"schema": { schema: {
"title": "Select", title: 'Select',
"type": "string", type: 'string',
"enum": [ enum: ['a', 'b', 'c'],
"a", enumNames: ['早', '中', '晚'],
"b", description: '下拉单选',
"c" },
],
"enumNames": [
"早",
"中",
"晚"
],
"description": "下拉单选"
}
}, },
{ {
"text": 'MultiSelect', text: 'MultiSelect',
"name": 'multiSelect', name: 'multiSelect',
"schema": { schema: {
"title": "MultiSelect", title: 'MultiSelect',
"description": "下拉多选", description: '下拉多选',
"type": "array", type: 'array',
"items": { items: {
"type": "string" type: 'string',
}, },
"enum": [ enum: ['A', 'B', 'C', 'D'],
"A", enumNames: ['1', '2', '3', '4'],
"B", 'ui:widget': 'multiSelect',
"C", },
"D"
],
"enumNames": [
"1",
"2",
"3",
"4"
],
"ui:widget": "multiSelect"
}
}, },
{ {
"text": 'Checkbox', text: 'Checkbox',
"name": 'checkbox', name: 'checkbox',
"schema": { schema: {
"title": "Checkbox", title: 'Checkbox',
"description": "点击多选", description: '点击多选',
"type": "array", type: 'array',
"items": { items: {
"type": "string" type: 'string',
}, },
"enum": [ enum: ['A', 'B', 'C', 'D'],
"A", enumNames: ['1', '2', '3', '4'],
"B", },
"C",
"D"
],
"enumNames": [
"1",
"2",
"3",
"4"
]
}
}, },
{ {
"text": 'TimePicker', text: 'TimePicker',
"name": 'timeSelect', name: 'timeSelect',
"schema": { schema: {
"title": "TimePicker", title: 'TimePicker',
"type": "string", type: 'string',
"format": "time", format: 'time',
"description": "时间选择" description: '时间选择',
} },
}, },
{ {
"text": 'DatePicker', text: 'DatePicker',
"name": 'dateSelect', name: 'dateSelect',
"schema": { schema: {
"title": "DatePicker", title: 'DatePicker',
"type": "string", type: 'string',
"format": "date", format: 'date',
"description": "日期选择" description: '日期选择',
} },
}, },
{ {
"text": 'DateRange', text: 'DateRange',
"name": 'dateRangeSelect', name: 'dateRangeSelect',
"schema": { schema: {
"title": "DateRange", title: 'DateRange',
"type": "range", type: 'range',
"format": "date", format: 'date',
"description": "日期范围选择" description: '日期范围选择',
} },
} },
] ];
export { export { compData };
compData
}

12
packages/core/src/mods/settingBar/index.tsx

@ -33,7 +33,12 @@ const SettingBar: React.FC<IProps> = (props) => {
<Tabs <Tabs
tabBarGutter={0} tabBarGutter={0}
defaultActiveKey={'basic'} defaultActiveKey={'basic'}
tabBarStyle={{ display: 'flex', flex: 1, justifyContent: 'center', alignItems: 'center' }} tabBarStyle={{
display: 'flex',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}
> >
<TabPane tab={'节点配置'} key={'basic'}> <TabPane tab={'节点配置'} key={'basic'}>
<Basic selectedCell={nodes[0]} flowChart={flowChart} /> <Basic selectedCell={nodes[0]} flowChart={flowChart} />
@ -47,7 +52,10 @@ const SettingBar: React.FC<IProps> = (props) => {
} else { } else {
return ( return (
<div className={`${styles.container} ${styles.center}`}> <div className={`${styles.container} ${styles.center}`}>
<Empty description={'请选择一个节点'} image={Empty.PRESENTED_IMAGE_SIMPLE} /> <Empty
description={'请选择一个节点'}
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
</div> </div>
); );
} }

12
packages/core/src/mods/settingBar/mods/basic/index.tsx

@ -1,7 +1,4 @@
import React, { import React, { useState, useEffect } from 'react';
useState,
useEffect,
} from 'react';
import styles from './index.module.less'; import styles from './index.module.less';
@ -64,7 +61,12 @@ const Basic: React.FC<IProps> = (props) => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
<Card title="名称"> <Card title="名称">
<Input name={'label'} title={'节点显示名称'} value={label} onValueChange={onChangeLabel} /> <Input
name={'label'}
title={'节点显示名称'}
value={label}
onValueChange={onChangeLabel}
/>
{selectedCell.shape === 'imove-start' && ( {selectedCell.shape === 'imove-start' && (
<div className={styles.input}> <div className={styles.input}>
<Input <Input

14
packages/core/src/mods/settingBar/mods/testCase/index.tsx

@ -1,8 +1,4 @@
import React, { import React, { useState, useEffect, useCallback } from 'react';
useState,
useEffect,
useCallback
} from 'react';
import { Modal } from 'antd'; import { Modal } from 'antd';
import { Graph } from '@antv/x6'; import { Graph } from '@antv/x6';
@ -28,11 +24,7 @@ const TestCase: React.FC<IProps> = (props) => {
const closeModal = useCallback(() => setVisible(false), []); const closeModal = useCallback(() => setVisible(false), []);
return ( return (
<EditModal <EditModal visible={visible} flowChart={flowChart} onClose={closeModal} />
visible={visible}
flowChart={flowChart}
onClose={closeModal}
/>
); );
}; };
@ -55,7 +47,7 @@ const EditModal: React.FC<IEditModalProps> = (props): JSX.Element => {
footer={null} footer={null}
onCancel={onClose} onCancel={onClose}
> >
<CodeRun flowChart={flowChart}/> <CodeRun flowChart={flowChart} />
</Modal> </Modal>
); );
}; };

11
packages/core/src/mods/sideBar/index.tsx

@ -14,7 +14,11 @@ const CELL_SCALE = 0.7;
const GENERAL_GROUP = { const GENERAL_GROUP = {
key: 'general', key: 'general',
name: '通用元件', name: '通用元件',
cells: ['imove-start-preview', 'imove-branch-preview', 'imove-behavior-preview'], cells: [
'imove-start-preview',
'imove-branch-preview',
'imove-behavior-preview',
],
}; };
const SHAPE_REFLECT_MAP: { [key: string]: string } = { const SHAPE_REFLECT_MAP: { [key: string]: string } = {
'imove-start-preview': 'imove-start', 'imove-start-preview': 'imove-start',
@ -47,7 +51,10 @@ const SideBar: React.FC<ISideBarProps> = (props) => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
{dnd && ( {dnd && (
<Collapse className={styles.collapse} defaultActiveKey={['general', 'custom']}> <Collapse
className={styles.collapse}
defaultActiveKey={['general', 'custom']}
>
{groups.map((group) => ( {groups.map((group) => (
<Panel key={group.key} header={group.name}> <Panel key={group.key} header={group.name}>
<PanelContent dnd={dnd} cells={group.cells} /> <PanelContent dnd={dnd} cells={group.cells} />

14
packages/core/src/mods/toolBar/widgets/bgColor.tsx

@ -8,7 +8,10 @@ import { safeGet } from '../../../utils';
import XIcon from '../../../components/xIcon'; import XIcon from '../../../components/xIcon';
import ColorPicker from '../../../components/colorPicker'; import ColorPicker from '../../../components/colorPicker';
import makeDropdownWidget from './common/makeDropdownWidget'; import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils'; import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
interface IProps { interface IProps {
flowChart: Graph; flowChart: Graph;
@ -29,7 +32,10 @@ const options = {
return ( return (
<div className={styles.bgColorContainer}> <div className={styles.bgColorContainer}>
<XIcon className={styles.fillIcon} type={'icon-fill'} /> <XIcon className={styles.fillIcon} type={'icon-fill'} />
<div className={styles.colorPreview} style={{ backgroundColor: bgColor }} /> <div
className={styles.colorPreview}
style={{ backgroundColor: bgColor }}
/>
</div> </div>
); );
}, },
@ -39,7 +45,9 @@ const options = {
return <ColorPicker color={bgColor} onChangeComplete={onChangeComplete} />; return <ColorPicker color={bgColor} onChangeComplete={onChangeComplete} />;
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
getSelectedNodes(flowChart).forEach((node) => node.setAttrs({ body: { fill: value } })); getSelectedNodes(flowChart).forEach((node) =>
node.setAttrs({ body: { fill: value } }),
);
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return !hasNodeSelected(flowChart); return !hasNodeSelected(flowChart);

18
packages/core/src/mods/toolBar/widgets/borderColor.tsx

@ -8,7 +8,10 @@ import { safeGet } from '../../../utils';
import { HighlightOutlined } from '@ant-design/icons'; import { HighlightOutlined } from '@ant-design/icons';
import ColorPicker from '../../../components/colorPicker'; import ColorPicker from '../../../components/colorPicker';
import makeDropdownWidget from './common/makeDropdownWidget'; import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils'; import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
interface IProps { interface IProps {
flowChart: Graph; flowChart: Graph;
@ -29,17 +32,24 @@ const options = {
return ( return (
<div className={styles.borderColorContainer}> <div className={styles.borderColorContainer}>
<HighlightOutlined className={styles.borderColorIcon} /> <HighlightOutlined className={styles.borderColorIcon} />
<div className={styles.colorPreview} style={{ backgroundColor: borderColor }} /> <div
className={styles.colorPreview}
style={{ backgroundColor: borderColor }}
/>
</div> </div>
); );
}, },
getOverlay(flowChart: Graph, onChange: (data: any) => void) { getOverlay(flowChart: Graph, onChange: (data: any) => void) {
const borderColor = options.getCurBorderColor(flowChart); const borderColor = options.getCurBorderColor(flowChart);
const onChangeComplete = (color: string) => onChange(color); const onChangeComplete = (color: string) => onChange(color);
return <ColorPicker color={borderColor} onChangeComplete={onChangeComplete} />; return (
<ColorPicker color={borderColor} onChangeComplete={onChangeComplete} />
);
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
getSelectedNodes(flowChart).forEach((node) => node.setAttrs({ body: { stroke: value } })); getSelectedNodes(flowChart).forEach((node) =>
node.setAttrs({ body: { stroke: value } }),
);
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return !hasNodeSelected(flowChart); return !hasNodeSelected(flowChart);

6
packages/core/src/mods/toolBar/widgets/common/makeDropdownWidget.tsx

@ -37,7 +37,11 @@ const makeDropdownWidget = (options: IOptions) => {
}; };
return ( return (
<Tooltip title={tooltip}> <Tooltip title={tooltip}>
<Dropdown disabled={disabled} overlay={getOverlay(flowChart, onChange)} trigger={['click']}> <Dropdown
disabled={disabled}
overlay={getOverlay(flowChart, onChange)}
trigger={['click']}
>
<div className={iconWrapperCls.join(' ')}> <div className={iconWrapperCls.join(' ')}>
{getIcon(flowChart)} <CaretDownOutlined className={styles.caret} /> {getIcon(flowChart)} <CaretDownOutlined className={styles.caret} />
</div> </div>

4
packages/core/src/mods/toolBar/widgets/fontSize.tsx

@ -36,7 +36,9 @@ const FontSize: React.FC<IProps> = makeDropdownWidget({
); );
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
flowChart.getSelectedCells().forEach((cell) => cell.setAttrs({ label: { fontSize: value } })); flowChart
.getSelectedCells()
.forEach((cell) => cell.setAttrs({ label: { fontSize: value } }));
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return flowChart.getSelectedCellCount() === 0; return flowChart.getSelectedCellCount() === 0;

15
packages/core/src/mods/toolBar/widgets/horizontalAlign.tsx

@ -6,8 +6,15 @@ import { Menu } from 'antd';
import { Graph } from '@antv/x6'; import { Graph } from '@antv/x6';
import { safeGet } from '../../../utils'; import { safeGet } from '../../../utils';
import makeDropdownWidget from './common/makeDropdownWidget'; import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils'; import {
import { AlignLeftOutlined, AlignCenterOutlined, AlignRightOutlined } from '@ant-design/icons'; hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
import {
AlignLeftOutlined,
AlignCenterOutlined,
AlignRightOutlined,
} from '@ant-design/icons';
interface IProps { interface IProps {
flowChart: Graph; flowChart: Graph;
@ -89,7 +96,9 @@ const HorizontalAlign: React.FC<IProps> = makeDropdownWidget({
); );
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
getSelectedNodes(flowChart).forEach((node) => node.setAttrs({ label: value })); getSelectedNodes(flowChart).forEach((node) =>
node.setAttrs({ label: value }),
);
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return !hasNodeSelected(flowChart); return !hasNodeSelected(flowChart);

9
packages/core/src/mods/toolBar/widgets/lineStyle.tsx

@ -7,7 +7,10 @@ import { Graph } from '@antv/x6';
import { safeGet } from '../../../utils'; import { safeGet } from '../../../utils';
import XIcon from '../../../components/xIcon'; import XIcon from '../../../components/xIcon';
import makeDropdownWidget from './common/makeDropdownWidget'; import makeDropdownWidget from './common/makeDropdownWidget';
import { hasEdgeSelected, getSelectedEdges } from '../../../utils/flowChartUtils'; import {
hasEdgeSelected,
getSelectedEdges,
} from '../../../utils/flowChartUtils';
interface IProps { interface IProps {
flowChart: Graph; flowChart: Graph;
@ -65,7 +68,9 @@ const LineStyle: React.FC<IProps> = makeDropdownWidget({
); );
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
getSelectedEdges(flowChart).forEach((edge) => edge.setAttrs({ line: value })); getSelectedEdges(flowChart).forEach((edge) =>
edge.setAttrs({ line: value }),
);
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return !hasEdgeSelected(flowChart); return !hasEdgeSelected(flowChart);

90
packages/core/src/mods/toolBar/widgets/nodeAlign.tsx

@ -25,14 +25,16 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
icon: <XIcon type={'icon-align-left'} />, icon: <XIcon type={'icon-align-left'} />,
handler(selectedNodes: Cell[]) { handler(selectedNodes: Cell[]) {
let minLeftValue = Infinity; let minLeftValue = Infinity;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { x } = node.getProp<{ x: number, y: number }>('position'); const { x } = node.getProp<{ x: number; y: number }>('position');
if (x < minLeftValue) { if (x < minLeftValue) {
minLeftValue = x; minLeftValue = x;
} }
}); });
selectedNodes.forEach(node => node.setProp({ position: { x: minLeftValue } })); selectedNodes.forEach((node) =>
} node.setProp({ position: { x: minLeftValue } }),
);
},
}, },
horizontalCenter: { horizontalCenter: {
text: '水平居中', text: '水平居中',
@ -40,9 +42,11 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
handler(selectedNodes: Cell[]) { handler(selectedNodes: Cell[]) {
let minLeftValue = Infinity; let minLeftValue = Infinity;
let maxRightValue = -Infinity; let maxRightValue = -Infinity;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { x } = node.getProp<{ x: number, y: number }>('position'); const { x } = node.getProp<{ x: number; y: number }>('position');
const { width } = node.getProp<{ width: number, height: number }>('size'); const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
if (x < minLeftValue) { if (x < minLeftValue) {
minLeftValue = x; minLeftValue = x;
} }
@ -51,43 +55,51 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
} }
}); });
const centerValue = (minLeftValue + maxRightValue) / 2; const centerValue = (minLeftValue + maxRightValue) / 2;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { width } = node.getProp<{ width: number, height: number }>('size'); const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
node.setProp({ position: { x: centerValue - width / 2 } }); node.setProp({ position: { x: centerValue - width / 2 } });
}); });
} },
}, },
right: { right: {
text: '右对齐', text: '右对齐',
icon: <XIcon type={'icon-align-right'} />, icon: <XIcon type={'icon-align-right'} />,
handler(selectedNodes: Cell[]) { handler(selectedNodes: Cell[]) {
let maxRightValue = -Infinity; let maxRightValue = -Infinity;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { x } = node.getProp<{ x: number, y: number }>('position'); const { x } = node.getProp<{ x: number; y: number }>('position');
const { width } = node.getProp<{ width: number, height: number }>('size'); const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
if (x + width > maxRightValue) { if (x + width > maxRightValue) {
maxRightValue = x + width; maxRightValue = x + width;
} }
}); });
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { width } = node.getProp<{ width: number, height: number }>('size'); const { width } = node.getProp<{ width: number; height: number }>(
node.setProp({ position: { x: maxRightValue - width } }) 'size',
);
node.setProp({ position: { x: maxRightValue - width } });
}); });
} },
}, },
top: { top: {
text: '顶部对齐', text: '顶部对齐',
icon: <XIcon type={'icon-align-top'} />, icon: <XIcon type={'icon-align-top'} />,
handler(selectedNodes: Cell[]) { handler(selectedNodes: Cell[]) {
let minTopValue = Infinity; let minTopValue = Infinity;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { y } = node.getProp<{ x: number, y: number }>('position'); const { y } = node.getProp<{ x: number; y: number }>('position');
if (y < minTopValue) { if (y < minTopValue) {
minTopValue = y; minTopValue = y;
} }
}); });
selectedNodes.forEach(node => node.setProp({ position: { y: minTopValue } })); selectedNodes.forEach((node) =>
} node.setProp({ position: { y: minTopValue } }),
);
},
}, },
verticalCenter: { verticalCenter: {
text: '垂直居中', text: '垂直居中',
@ -95,9 +107,11 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
handler(selectedNodes: Cell[]) { handler(selectedNodes: Cell[]) {
let minTopValue = Infinity; let minTopValue = Infinity;
let maxBottomValue = -Infinity; let maxBottomValue = -Infinity;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { y } = node.getProp<{ x: number, y: number }>('position'); const { y } = node.getProp<{ x: number; y: number }>('position');
const { height } = node.getProp<{ width: number, height: number }>('size'); const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
if (y < minTopValue) { if (y < minTopValue) {
minTopValue = y; minTopValue = y;
} }
@ -106,30 +120,36 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
} }
}); });
const centerValue = (minTopValue + maxBottomValue) / 2; const centerValue = (minTopValue + maxBottomValue) / 2;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { height } = node.getProp<{ width: number, height: number }>('size'); const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
node.setProp({ position: { y: centerValue - height / 2 } }); node.setProp({ position: { y: centerValue - height / 2 } });
}); });
} },
}, },
bottom: { bottom: {
text: '底部对齐', text: '底部对齐',
icon: <XIcon type={'icon-align-bottom'} />, icon: <XIcon type={'icon-align-bottom'} />,
handler(selectedNodes: Cell[]) { handler(selectedNodes: Cell[]) {
let maxTopValue = -Infinity; let maxTopValue = -Infinity;
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { y } = node.getProp<{ x: number, y: number }>('position'); const { y } = node.getProp<{ x: number; y: number }>('position');
const { height } = node.getProp<{ width: number, height: number }>('size'); const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
if (y + height > maxTopValue) { if (y + height > maxTopValue) {
maxTopValue = y + height; maxTopValue = y + height;
} }
}); });
selectedNodes.forEach(node => { selectedNodes.forEach((node) => {
const { height } = node.getProp<{ width: number, height: number }>('size'); const { height } = node.getProp<{ width: number; height: number }>(
node.setProp({ position: { y: maxTopValue - height } }) 'size',
);
node.setProp({ position: { y: maxTopValue - height } });
}); });
} },
} },
}; };
const NodeAlign: React.FC<IProps> = makeDropdownWidget({ const NodeAlign: React.FC<IProps> = makeDropdownWidget({

18
packages/core/src/mods/toolBar/widgets/textColor.tsx

@ -8,7 +8,10 @@ import { safeGet } from '../../../utils';
import XIcon from '../../../components/xIcon'; import XIcon from '../../../components/xIcon';
import ColorPicker from '../../../components/colorPicker'; import ColorPicker from '../../../components/colorPicker';
import makeDropdownWidget from './common/makeDropdownWidget'; import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils'; import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
interface IProps { interface IProps {
flowChart: Graph; flowChart: Graph;
@ -29,17 +32,24 @@ const options = {
return ( return (
<div className={styles.textColorContainer}> <div className={styles.textColorContainer}>
<XIcon className={styles.textIcon} type={'icon-font'} /> <XIcon className={styles.textIcon} type={'icon-font'} />
<div className={styles.colorPreview} style={{ backgroundColor: textColor }} /> <div
className={styles.colorPreview}
style={{ backgroundColor: textColor }}
/>
</div> </div>
); );
}, },
getOverlay(flowChart: Graph, onChange: (data: any) => void) { getOverlay(flowChart: Graph, onChange: (data: any) => void) {
const textColor = options.getCurTextColor(flowChart); const textColor = options.getCurTextColor(flowChart);
const onChangeComplete = (color: string) => onChange(color); const onChangeComplete = (color: string) => onChange(color);
return <ColorPicker color={textColor} onChangeComplete={onChangeComplete} />; return (
<ColorPicker color={textColor} onChangeComplete={onChangeComplete} />
);
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
flowChart.getSelectedCells().forEach((node) => node.setAttrs({ label: { fill: value } })); flowChart
.getSelectedCells()
.forEach((node) => node.setAttrs({ label: { fill: value } }));
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return !hasNodeSelected(flowChart); return !hasNodeSelected(flowChart);

4
packages/core/src/mods/toolBar/widgets/underline.tsx

@ -22,7 +22,9 @@ const Underline: React.FC<IProps> = makeBtnWidget({
selected(flowChart: Graph) { selected(flowChart: Graph) {
const cells = flowChart.getSelectedCells(); const cells = flowChart.getSelectedCells();
if (cells.length > 0) { if (cells.length > 0) {
return safeGet(cells, '0.attrs.label.textDecoration', 'none') === 'underline'; return (
safeGet(cells, '0.attrs.label.textDecoration', 'none') === 'underline'
);
} else { } else {
return false; return false;
} }

9
packages/core/src/mods/toolBar/widgets/verticalAlign.tsx

@ -6,7 +6,10 @@ import { Menu } from 'antd';
import { Graph } from '@antv/x6'; import { Graph } from '@antv/x6';
import { safeGet } from '../../../utils'; import { safeGet } from '../../../utils';
import makeDropdownWidget from './common/makeDropdownWidget'; import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils'; import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
import { import {
VerticalAlignTopOutlined, VerticalAlignTopOutlined,
VerticalAlignMiddleOutlined, VerticalAlignMiddleOutlined,
@ -93,7 +96,9 @@ const VerticalAlign: React.FC<IProps> = makeDropdownWidget({
); );
}, },
handler: (flowChart: Graph, value: any) => { handler: (flowChart: Graph, value: any) => {
getSelectedNodes(flowChart).forEach((node) => node.setAttrs({ label: value })); getSelectedNodes(flowChart).forEach((node) =>
node.setAttrs({ label: value }),
);
}, },
disabled(flowChart: Graph) { disabled(flowChart: Graph) {
return !hasNodeSelected(flowChart); return !hasNodeSelected(flowChart);

5
packages/core/src/typings.d.ts

@ -8,12 +8,11 @@ declare module '*.less' {
export default classes; export default classes;
} }
declare module 'fr-generator' { declare module 'fr-generator' {
import React from 'react'; import React from 'react';
export interface FRProps { export interface FRProps {
settings?: object settings?: object;
} }
class FormRender extends React.Component<FRProps> { } class FormRender extends React.Component<FRProps> {}
export default FRGenerator; export default FRGenerator;
} }

25
packages/core/src/utils/analyzeDeps.ts

@ -1,13 +1,13 @@
import axios from 'axios'; import axios from 'axios';
import { safeGet } from './index'; import { safeGet } from './index';
const regex = /import\s([\s\S]*?)\sfrom\s(?:('[@\.\/\-\w]+')|("[@\.\/\-\w]+"))/mg; const regex = /import\s([\s\S]*?)\sfrom\s(?:('[@\.\/\-\w]+')|("[@\.\/\-\w]+"))/gm;
const extractPkgs = (code: string, excludePkgs?: string[]): string[] => { const extractPkgs = (code: string, excludePkgs?: string[]): string[] => {
let matchRet = null; let matchRet = null;
const pkgNames: string[] = []; const pkgNames: string[] = [];
while ((matchRet = regex.exec(code)) != null) { while ((matchRet = regex.exec(code)) != null) {
let pkgName = (matchRet[2] || matchRet[3]); let pkgName = matchRet[2] || matchRet[3];
pkgName = pkgName.slice(1, pkgName.length - 1); pkgName = pkgName.slice(1, pkgName.length - 1);
// NOTE: ignore relative path (./ and ../) and excludePkgs // NOTE: ignore relative path (./ and ../) and excludePkgs
if ( if (
@ -22,26 +22,31 @@ const extractPkgs = (code: string, excludePkgs?: string[]): string[] => {
}; };
const getPkgLatestVersion = (pkg: string): Promise<string[]> => { const getPkgLatestVersion = (pkg: string): Promise<string[]> => {
return axios.get(`https://registry.npm.taobao.org/${pkg}`) return axios
.then(res => { .get(`https://registry.npm.taobao.org/${pkg}`)
.then((res) => {
return [pkg, safeGet(res, 'data.dist-tags.latest', '*')]; return [pkg, safeGet(res, 'data.dist-tags.latest', '*')];
}).catch(err => { })
.catch((err) => {
console.log(`get package ${pkg} info failed, the error is:`, err); console.log(`get package ${pkg} info failed, the error is:`, err);
return [pkg, '*']; return [pkg, '*'];
}); });
}; };
const analyzeDeps = (code: string, excludePkgs?: string[]): Promise<{ [pkgName: string]: string }> => { const analyzeDeps = (
code: string,
excludePkgs?: string[],
): Promise<{ [pkgName: string]: string }> => {
const pkgs = extractPkgs(code, excludePkgs); const pkgs = extractPkgs(code, excludePkgs);
return Promise return Promise.all(pkgs.map((pkg) => getPkgLatestVersion(pkg)))
.all(pkgs.map(pkg => getPkgLatestVersion(pkg))) .then((pkgResults) => {
.then(pkgResults => {
const map: any = {}; const map: any = {};
pkgResults.forEach(([pkg, version]) => { pkgResults.forEach(([pkg, version]) => {
map[pkg] = version; map[pkg] = version;
}); });
return map; return map;
}).catch(err => { })
.catch((err) => {
console.log('analyze deps failed, the error is:', err); console.log('analyze deps failed, the error is:', err);
}); });
}; };

20
packages/core/src/utils/flowChartUtils.ts

@ -5,11 +5,17 @@ export const hasCellSelected = (flowChart: Graph): boolean => {
}; };
export const hasNodeSelected = (flowChart: Graph): boolean => { export const hasNodeSelected = (flowChart: Graph): boolean => {
return flowChart.getSelectedCells().filter((cell: Cell) => cell.isNode()).length > 0; return (
flowChart.getSelectedCells().filter((cell: Cell) => cell.isNode()).length >
0
);
}; };
export const hasEdgeSelected = (flowChart: Graph): boolean => { export const hasEdgeSelected = (flowChart: Graph): boolean => {
return flowChart.getSelectedCells().filter((cell: Cell) => cell.isEdge()).length > 0; return (
flowChart.getSelectedCells().filter((cell: Cell) => cell.isEdge()).length >
0
);
}; };
export const getSelectedNodes = (flowChart: Graph): Cell[] => { export const getSelectedNodes = (flowChart: Graph): Cell[] => {
@ -20,10 +26,14 @@ export const getSelectedEdges = (flowChart: Graph): Cell[] => {
return flowChart.getSelectedCells().filter((cell: Cell) => cell.isEdge()); return flowChart.getSelectedCells().filter((cell: Cell) => cell.isEdge());
}; };
export const toSelectedCellsJSON = (flowChart: Graph): {cells: Cell.Properties[]} => { export const toSelectedCellsJSON = (
flowChart: Graph,
): { cells: Cell.Properties[] } => {
const json = flowChart.toJSON(); const json = flowChart.toJSON();
const selectedCells = flowChart.getSelectedCells(); const selectedCells = flowChart.getSelectedCells();
return { return {
cells: json.cells.filter(cell => selectedCells.find(o => o.id === cell.id)) cells: json.cells.filter((cell) =>
selectedCells.find((o) => o.id === cell.id),
),
}; };
} };

2
packages/core/src/utils/index.ts

@ -37,7 +37,7 @@ export const parseQuery = (): { [key: string]: any } => {
return parse(location.search, parseConfig); return parse(location.search, parseConfig);
}; };
export const executeScript = (code: string, type: string = 'module') => { export const executeScript = (code: string, type = 'module') => {
const script = document.createElement('script'); const script = document.createElement('script');
script.type = type; script.type = type;
script.text = code; script.text = code;

19
packages/json-schema-editor/src/components/add-node.tsx

@ -8,17 +8,30 @@ interface AddNodeProps {
addSiblingField: () => void; addSiblingField: () => void;
} }
function AddNodeIcon({ addChildField, addSiblingField }: AddNodeProps): JSX.Element { function AddNodeIcon({
addChildField,
addSiblingField,
}: AddNodeProps): JSX.Element {
const { t } = useTranslation(); const { t } = useTranslation();
const menu = ( const menu = (
<Menu> <Menu>
<Menu.Item> <Menu.Item>
<div tabIndex={0} role="button" onClick={addChildField} onKeyDown={addChildField}> <div
tabIndex={0}
role="button"
onClick={addChildField}
onKeyDown={addChildField}
>
{t('child_node')} {t('child_node')}
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<div tabIndex={0} role="button" onClick={addSiblingField} onKeyDown={addSiblingField}> <div
tabIndex={0}
role="button"
onClick={addSiblingField}
onKeyDown={addSiblingField}
>
{t('sibling_node')} {t('sibling_node')}
</div> </div>
</Menu.Item> </Menu.Item>

5
packages/json-schema-editor/src/components/schema-form.tsx

@ -29,7 +29,10 @@ function SchemaForm({ data, keyRoute }: SchemaFormProps): JSX.Element {
keyRoute={fieldKeyRoute} keyRoute={fieldKeyRoute}
> >
{itemData.type === 'object' && !isUndefined(itemData.properties) ? ( {itemData.type === 'object' && !isUndefined(itemData.properties) ? (
<SchemaForm data={itemData} keyRoute={fieldKeyRoute.concat('properties')} /> <SchemaForm
data={itemData}
keyRoute={fieldKeyRoute.concat('properties')}
/>
) : null} ) : null}
</SchemaItem> </SchemaItem>
); );

12
packages/json-schema-editor/src/components/schema-head.tsx

@ -79,10 +79,18 @@ function EditorHead(): JSX.Element {
</Select> </Select>
</Col> </Col>
<Col span="5"> <Col span="5">
<Input value="" placeholder={t('title')} addonAfter={<EditIcon />} /> <Input
value=""
placeholder={t('title')}
addonAfter={<EditIcon />}
/>
</Col> </Col>
<Col span="5"> <Col span="5">
<Input value="" placeholder={t('description')} addonAfter={<EditIcon />} /> <Input
value=""
placeholder={t('description')}
addonAfter={<EditIcon />}
/>
</Col> </Col>
<Col span="3"> <Col span="3">
<Tooltip placement="top" title={t('setting')}> <Tooltip placement="top" title={t('setting')}>

22
packages/json-schema-editor/src/components/schema-item.tsx

@ -51,7 +51,10 @@ function SchemaFormItem(props: SchemaItemProps): JSX.Element {
}; };
const addChildField = (): void => { const addChildField = (): void => {
schemaDispatch({ type: 'ADD_CHILD_FIELD', keyRoute: keyRoute.concat('properties') }); schemaDispatch({
type: 'ADD_CHILD_FIELD',
keyRoute: keyRoute.concat('properties'),
});
}; };
const addSiblingField = (): void => { const addSiblingField = (): void => {
@ -84,8 +87,12 @@ function SchemaFormItem(props: SchemaItemProps): JSX.Element {
<Col span="8" css={{ paddingLeft: `${keyRoute.length * 10}px` }}> <Col span="8" css={{ paddingLeft: `${keyRoute.length * 10}px` }}>
<Row align="middle"> <Row align="middle">
<Col span="2"> <Col span="2">
{data.type === 'object' && visible && <CaretDownIcon onClick={toggleFormVisible} />} {data.type === 'object' && visible && (
{data.type === 'object' && !visible && <CaretRightIcon onClick={toggleFormVisible} />} <CaretDownIcon onClick={toggleFormVisible} />
)}
{data.type === 'object' && !visible && (
<CaretRightIcon onClick={toggleFormVisible} />
)}
</Col> </Col>
<Col span="22"> <Col span="22">
<FieldInput <FieldInput
@ -128,7 +135,10 @@ function SchemaFormItem(props: SchemaItemProps): JSX.Element {
</Tooltip> </Tooltip>
<CloseIcon onClick={removeField} /> <CloseIcon onClick={removeField} />
{data.type === 'object' ? ( {data.type === 'object' ? (
<AddNode addChildField={addChildField} addSiblingField={addSiblingField} /> <AddNode
addChildField={addChildField}
addSiblingField={addSiblingField}
/>
) : ( ) : (
<Tooltip placement="top" title={t('add_sibling_node')}> <Tooltip placement="top" title={t('add_sibling_node')}>
<PlusIcon onClick={addSiblingField} /> <PlusIcon onClick={addSiblingField} />
@ -136,7 +146,9 @@ function SchemaFormItem(props: SchemaItemProps): JSX.Element {
)} )}
</Col> </Col>
</Row> </Row>
{visible && children ? <div css={{ marginTop: '10px' }}>{children}</div> : null} {visible && children ? (
<div css={{ marginTop: '10px' }}>{children}</div>
) : null}
</div> </div>
); );
} }

8
packages/json-schema-editor/src/model/schema.ts

@ -1,4 +1,10 @@
export type SchemaType = 'string' | 'number' | 'object' | 'array' | 'boolean' | 'integer'; export type SchemaType =
| 'string'
| 'number'
| 'object'
| 'array'
| 'boolean'
| 'integer';
type basicSchemaType = 'string' | 'number' | 'boolean' | 'integer'; type basicSchemaType = 'string' | 'number' | 'boolean' | 'integer';

38
packages/json-schema-editor/src/reducer/schema.ts

@ -1,6 +1,12 @@
import produce, { Draft } from 'immer'; import produce, { Draft } from 'immer';
import { get, set, unset, remove } from 'lodash-es'; import { get, set, unset, remove } from 'lodash-es';
import { SchemaType, SchemaItem, SchemaState, SchemaAction, KeyRoute } from '../model'; import {
SchemaType,
SchemaItem,
SchemaState,
SchemaAction,
KeyRoute,
} from '../model';
import { defaultSchema, setAllFieldRequired } from '../utils/schema'; import { defaultSchema, setAllFieldRequired } from '../utils/schema';
let fieldNum = 0; let fieldNum = 0;
@ -25,7 +31,11 @@ const removeRequired = (state: SchemaState, keyRoute: KeyRoute): void => {
set(state, requiredKeyRoute, required); set(state, requiredKeyRoute, required);
}; };
const changeRequiredField = (state: SchemaState, oldKeyRoute: KeyRoute, newField: string): void => { const changeRequiredField = (
state: SchemaState,
oldKeyRoute: KeyRoute,
newField: string,
): void => {
const oldField = oldKeyRoute.slice(-1)[0]; const oldField = oldKeyRoute.slice(-1)[0];
const requiredKeyRoute = oldKeyRoute.slice(0, -2).concat('required'); const requiredKeyRoute = oldKeyRoute.slice(0, -2).concat('required');
const required = get(state, requiredKeyRoute) || []; const required = get(state, requiredKeyRoute) || [];
@ -76,7 +86,11 @@ const removeField = (state: SchemaState, keyRoute: KeyRoute): void => {
removeRequired(state, keyRoute); removeRequired(state, keyRoute);
}; };
const changeField = (state: SchemaState, keyRoute: KeyRoute, newField: string): void => { const changeField = (
state: SchemaState,
keyRoute: KeyRoute,
newField: string,
): void => {
const oldField = keyRoute.slice(-1)[0]; const oldField = keyRoute.slice(-1)[0];
const parentKeyRoute = keyRoute.slice(0, -1); const parentKeyRoute = keyRoute.slice(0, -1);
const properties = get(state, parentKeyRoute); const properties = get(state, parentKeyRoute);
@ -99,18 +113,30 @@ const changeField = (state: SchemaState, keyRoute: KeyRoute, newField: string):
changeRequiredField(state, keyRoute, newField); changeRequiredField(state, keyRoute, newField);
}; };
const changeType = (state: SchemaState, keyRoute: KeyRoute, fieldType: SchemaType): void => { const changeType = (
state: SchemaState,
keyRoute: KeyRoute,
fieldType: SchemaType,
): void => {
// set field new value // set field new value
set(state, keyRoute, defaultSchema[fieldType]); set(state, keyRoute, defaultSchema[fieldType]);
}; };
const changeTitle = (state: SchemaState, keyRoute: KeyRoute, title: string): void => { const changeTitle = (
state: SchemaState,
keyRoute: KeyRoute,
title: string,
): void => {
// point to title and set new value // point to title and set new value
const titleKeyRoute = keyRoute.concat('title'); const titleKeyRoute = keyRoute.concat('title');
set(state, titleKeyRoute, title); set(state, titleKeyRoute, title);
}; };
const changeDesc = (state: SchemaState, keyRoute: KeyRoute, desc: string): void => { const changeDesc = (
state: SchemaState,
keyRoute: KeyRoute,
desc: string,
): void => {
// point to desc and set new value // point to desc and set new value
const descKeyRoute = keyRoute.concat('description'); const descKeyRoute = keyRoute.concat('description');
set(state, descKeyRoute, desc); set(state, descKeyRoute, desc);

14
packages/json-schema-editor/src/utils/schema.ts

@ -25,9 +25,19 @@ export const defaultSchema = {
}, },
}; };
export const schemaType = ['string', 'number', 'array', 'object', 'boolean', 'integer']; export const schemaType = [
'string',
'number',
'array',
'object',
'boolean',
'integer',
];
export const setAllFieldRequired = (schema: SchemaItem, checked: boolean): void => { export const setAllFieldRequired = (
schema: SchemaItem,
checked: boolean,
): void => {
if (schema.type === 'object') { if (schema.type === 'object') {
const { properties } = schema; const { properties } = schema;
const fields = Object.keys(properties); const fields = Object.keys(properties);

Loading…
Cancel
Save