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 = {
extends: [
'standard',
'plugin:import/typescript',
'prettier',
'prettier/react',
"prettier/@typescript-eslint",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
plugins: ['prettier', 'react'],
env: {
node: true,
browser: true,
},
settings: {
react: {
version: "detect"
}
},
rules: {
'import/no-extraneous-dependencies': ['error', { peerDependencies: true }],
'prettier/prettier': 'error',
// 修复 tsx 文件引用 tsx 文件报错的问题
'react/jsx-filename-extension': ['warn', { extensions: ['.ts', '.tsx'] }],
"semi": [2, "always"],//语句强制分号结尾
"semi-spacing": [0, { "before": false, "after": true }],//分号前后空格
'react/jsx-filename-extension': ['warn', { extensions: ['.ts', '.tsx'] }], // 修复 tsx 文件引用 tsx 文件报错的问题
"no-var": 0,//禁用var,用let和const代替
"no-use-before-define": 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\"",
"postinstall": "lerna init && lerna bootstrap && npm link packages/cli"
},
"pre-commit": [
"lint"
],
"jest": {
"projects": [
"config/cli.jest.config.js",
@ -24,27 +27,28 @@
"@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-strip": "^1.3.2",
"@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",
"concurrently": "^5.3.0",
"cross-env": "^7.0.3",
"cypress": "^6.2.0",
"dumi": "^1.0.38",
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-standard": "^5.0.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.22.0",
"lerna": "^3.22.1",
"less": "^3.12.2",
"lowdb": "^1.0.0",
"ora": "^4.1.1",
"postcss": "^8.2.1",
"pre-commit": "^1.2.2",
"prettier": "^2.2.1",
"rollup": "^2.6.1",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-size-snapshot": "^0.11.0",
"rollup-plugin-typescript": "^1.0.1",
"snazzy": "^9.0.0",
"standard": "^16.0.3",
"ts-jest": "^26.4.4",
"typescript": "^4.1.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');
class Dev extends Base {
async writeOutputIntoFiles(curPath, output) {
for(let key in output) {
for (const key in output) {
const newPath = path.join(curPath, key);
if(path.extname(newPath)) {
if (path.extname(newPath)) {
await fs.writeFile(newPath, output[key]);
} else {
await fs.ensureDir(newPath);

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

@ -14,9 +14,14 @@ const extractDep = (dsl) => {
const { dependencies } = cell.data || {};
try {
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) {
console.log('extract dependencies failed, the error is:', error.message);
console.log(
'extract dependencies failed, the error is:',
error.message,
);
}
});
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((req, res, next) => {
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();
});
app.listen(port, () => {

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

@ -1,11 +1,12 @@
const INSERT_IMPORT_PLUGINS_COMMENT = '// import 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
.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), () => {
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 simplifyDSL from './simplifyDSL';
@ -23,46 +23,46 @@ interface DSL {
const INSERT_DSL_COMMENT = '// define dsl 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 = {
id: 'virtual-imove-start',
shape: 'imove-start',
data: {
trigger: 'virtual-imove-start',
configData: {},
code: 'export default async function(ctx) {\n \n}'
}
code: 'export default async function(ctx) {\n \n}',
},
};
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');
const edges = dsl.cells.filter(cell => cell.shape === 'edge');
if(nodes.length === 0) {
if (nodes.length === 0) {
throw new Error('Compile failed, no node is selected');
}
let foundEdge = null;
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;
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') {
dsl.cells.push(
virtualSourceNode,
{
shape: "edge",
if (startNode.shape !== 'imove-start') {
dsl.cells.push(virtualSourceNode, {
shape: 'edge',
source: {
cell: 'virtual-imove-start',
},
target: {
cell: startNode.id
}
}
);
cell: startNode.id,
},
});
startNode = virtualSourceNode;
}
@ -70,13 +70,12 @@ const findStartNode = (dsl: DSL): Cell.Properties => {
};
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 edges = dsl.cells.filter(cell => cell.shape === 'edge');
const foundEdge = edges.find(edge => edge.source.cell === curNode.id);
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 {data: {label, code}} = node;
const newCode = code.replace(importRegex, (match: string, p1: string, p2: string, p3: string) => {
const {
data: { label, code },
} = node;
const newCode = code
.replace(
importRegex,
(match: string, p1: string, p2: string, p3: string) => {
const pkgName = (p2 || p3).replace(/('|")/g, '');
return `const ${p1} = (await import('https://jspm.dev/${pkgName}')).default;`;
}).replace(/export\s+default/, 'return');
},
)
.replace(/export\s+default/, 'return');
return `await (async function() {
${newCode}
@ -98,9 +104,9 @@ const compileNodeFn = (node: Cell.Properties): string => {
};
const compileNodeFnsMap = (dsl: DSL): string => {
const nodes = dsl.cells.filter(cell => cell.shape !== 'edge');
const kvs = nodes.map(node => {
const {id} = node;
const nodes = dsl.cells.filter((cell) => cell.shape !== 'edge');
const kvs = nodes.map((node) => {
const { id } = 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 simplifyDSL from './simplifyDSL';
import extractNodeFns from './extractNodeFns';
@ -11,8 +11,8 @@ interface DSL {
}
interface IOutput {
'nodeFns': {
[fileName: string]: string
nodeFns: {
[fileName: string]: string;
};
'context.js': string;
'dsl.json': string;
@ -22,11 +22,11 @@ interface IOutput {
const compile = (dsl: DSL, plugins = []): IOutput => {
const output: IOutput = {
'nodeFns': extractNodeFns(dsl),
"context.js": contextTpl,
nodeFns: extractNodeFns(dsl),
'context.js': contextTpl,
'dsl.json': JSON.stringify(simplifyDSL(dsl), null, 2),
'index.js': addPlugins(indexTpl, plugins),
'logic.js': logicTpl
'logic.js': logicTpl,
};
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 {
cells: Cell.Properties[];
@ -34,8 +34,8 @@ const genNodeFns = (dsl: DSL): INodesFns => {
data: { label, code },
} of nodes) {
const fileName: string = id + '.js';
const descData: string = `// ${shape}: ${label}\n`;
const saveData: string = `${descData}\n${code}`;
const descData = `// ${shape}: ${label}\n`;
const saveData = `${descData}\n${code}`;
nodeFns[fileName] = saveData;
}
return nodeFns;
@ -43,7 +43,7 @@ const genNodeFns = (dsl: DSL): INodesFns => {
const extract = (dsl: DSL): INodesFns => {
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);
nodeFns['index.js'] = entryFileContent;
return nodeFns;

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

@ -1,7 +1,4 @@
import compileForOnline from './compileForOnline';
import compileForProject from './compileForProject';
export {
compileForOnline,
compileForProject
};
export { 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 {
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 = {};
keys.forEach((key) => {
if (obj[key]) {
@ -22,7 +25,11 @@ const simplifyDSL = (dsl: DSL): Cell.Properties => {
return extractObj(cell, ['id', 'shape', 'source', 'target']);
} else {
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;
}
}),

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

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

1
packages/core/package.json

@ -42,7 +42,6 @@
"@monaco-editor/react": "^3.7.4",
"antd": "^4.5.3",
"axios": "^0.21.0",
"form-render": "^0.9.5",
"fr-generator": "^1.0.0",
"jszip": "^3.5.0",
"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({
url: '/api/modifyGraph',
params: {

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

@ -77,10 +77,18 @@ const shortcuts: { [key: string]: Shortcut } = {
const onEdgeDel = (edge: Edge) => {
const srcNode = edge.getSourceNode() as Node;
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();
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);
}
}
@ -109,9 +117,12 @@ const shortcuts: { [key: string]: Shortcut } = {
handler(flowChart: Graph) {
const cells = flowChart.getSelectedCells();
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) => {
cell.setAttrs({ label: { fontWeight: isAlreadyBold ? 'normal' : 'bold' } });
cell.setAttrs({
label: { fontWeight: isAlreadyBold ? 'normal' : 'bold' },
});
});
flowChart.trigger('toolBar:forceUpdate');
}
@ -123,9 +134,12 @@ const shortcuts: { [key: string]: Shortcut } = {
handler(flowChart: Graph) {
const cells = flowChart.getSelectedCells();
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) => {
cell.setAttrs({ label: { fontStyle: isAlreadyItalic ? 'normal' : 'italic' } });
cell.setAttrs({
label: { fontStyle: isAlreadyItalic ? 'normal' : 'italic' },
});
});
flowChart.trigger('toolBar:forceUpdate');
}
@ -138,9 +152,14 @@ const shortcuts: { [key: string]: Shortcut } = {
const cells = flowChart.getSelectedCells();
if (cells.length > 0) {
const isAlreadyUnderline =
safeGet(cells, '0.attrs.label.textDecoration', 'none') === 'underline';
safeGet(cells, '0.attrs.label.textDecoration', 'none') ===
'underline';
cells.forEach((cell) => {
cell.setAttrs({ label: { textDecoration: isAlreadyUnderline ? 'none' : 'underline' } });
cell.setAttrs({
label: {
textDecoration: isAlreadyUnderline ? 'none' : 'underline',
},
});
});
flowChart.trigger('toolBar:forceUpdate');
}
@ -151,14 +170,14 @@ const shortcuts: { [key: string]: Shortcut } = {
keys: 'meta + ]',
handler(flowChart: Graph) {
getSelectedNodes(flowChart).forEach((node) => node.toFront());
}
},
},
bringToBack: {
keys: 'meta + [',
handler(flowChart: Graph) {
getSelectedNodes(flowChart).forEach((node) => node.toBack());
}
}
},
},
};
export default shortcuts;

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

@ -1,14 +1,10 @@
import React, {
useMemo,
useState,
useEffect
} from 'react';
import React, { useMemo, useState, useEffect } from 'react';
import {
monaco,
EditorDidMount,
ControlledEditor,
ControlledEditorProps
ControlledEditorProps,
} from '@monaco-editor/react';
import monokaiTheme from './theme-monokai';
@ -22,7 +18,7 @@ monaco.init().then((monaco) => {
});
const CODE_EDITOR_OPTIONS = {
fontSize: 14
fontSize: 14,
};
interface IProps extends ControlledEditorProps {
@ -30,7 +26,7 @@ interface IProps extends ControlledEditorProps {
}
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 editorOptions = useMemo(() => {

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

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

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

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

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

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

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

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

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

@ -1,104 +1,148 @@
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 styles from './index.module.less'
const FormItem = Form.Item
import styles from './index.module.less';
const FormItem = Form.Item;
const { RangePicker } = DatePicker;
const { Option } = Select;
interface INum {
label: string,
value: string
label: string;
value: string;
}
interface IConfigData {
[key: string]: any
[key: string]: any;
}
interface IProps {
schema: {
type: string,
properties: { [key: string]: any }
},
configData: IConfigData,
changeConfigData: (data: IConfigData) => void
type: string;
properties: { [key: string]: any };
};
configData: IConfigData;
changeConfigData: (data: IConfigData) => void;
}
const formLayout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 }
wrapperCol: { span: 16 },
};
const SchemaForm: React.FC<IProps> = (props) => {
const { schema, configData, changeConfigData } = props
const [form] = Form.useForm()
const { schema, configData, changeConfigData } = props;
const [form] = Form.useForm();
const onClickSave = (): void => {
changeConfigData(form.getFieldsValue())
message.success('保存成功!')
}
changeConfigData(form.getFieldsValue());
message.success('保存成功!');
};
return (
schema && Object.keys(schema).length > 0 ?
<Form
form={form}
initialValues={configData}
{...formLayout}
>
{schema?.properties && Object.keys(schema.properties).map((key: string) => {
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
return schema && Object.keys(schema).length > 0 ? (
<Form form={form} initialValues={configData} {...formLayout}>
{schema?.properties &&
Object.keys(schema.properties).map((key: string) => {
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;
// 输入框
if (obj.type === 'string' && !obj.hasOwnProperty('format') && !obj.hasOwnProperty('enum')) {
ele = <Input />
if (
obj.type === 'string' &&
!obj.hasOwnProperty('format') &&
!obj.hasOwnProperty('enum')
) {
ele = <Input />;
}
// 编辑框
if (obj.type === 'string' && obj.format === 'textarea') {
ele = <Input.TextArea />
ele = <Input.TextArea />;
}
// switch
if (obj.type === 'boolean' && obj['ui:widget'] === 'switch') {
ele = <Switch />
ele = <Switch />;
}
// 下拉单选
if (obj.type === 'string' && obj.hasOwnProperty('enum') && !obj.hasOwnProperty('ui:widget')) {
ele = <Select allowClear>{options.map(item =>
<Option key={item.value} value={item.value}>{item.label}</Option>
)}</Select>
if (
obj.type === 'string' &&
obj.hasOwnProperty('enum') &&
!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') {
ele = <Select allowClear mode="multiple">
{options.map(item => <Option key={item.value} value={item.value}>{item.label}</Option>)}
if (
obj.type === 'array' &&
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>
);
}
// 点击多选
if (obj.type === 'array' && obj.hasOwnProperty('enum') && !obj.hasOwnProperty('ui:widget')) {
ele = <Checkbox.Group options={options} />
if (
obj.type === 'array' &&
obj.hasOwnProperty('enum') &&
!obj.hasOwnProperty('ui:widget')
) {
ele = <Checkbox.Group options={options} />;
}
// 时间选择
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') {
ele = <DatePicker />
ele = <DatePicker />;
}
// 日期选择
if (obj.type === 'string' && obj.format === 'date') {
ele = <RangePicker />
ele = <RangePicker />;
}
return <FormItem
label={obj.title}
name={key}
>
return (
<FormItem label={obj.title} name={key}>
{ele}
</FormItem>
);
})}
<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>
</Form> :
</Form>
) : (
<Empty
description={'请编辑投放配置schema'}
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 {
useRef,
useEffect,
MutableRefObject
} from 'react';
import { useRef, useEffect, MutableRefObject } from 'react';
type EventType = MouseEvent | TouchEvent;
type TargetElement = HTMLElement | Element | Document | Window;

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

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

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

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

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

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

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

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

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

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

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

@ -18,13 +18,13 @@ const nodeMenuConfig = [
key: 'copy',
title: '复制',
icon: <CopyOutlined />,
handler: shortcuts.copy.handler
handler: shortcuts.copy.handler,
},
{
key: 'delete',
title: '删除',
icon: <DeleteOutlined />,
handler: shortcuts.delete.handler
handler: shortcuts.delete.handler,
},
{
key: 'rename',
@ -33,20 +33,20 @@ const nodeMenuConfig = [
showDividerBehind: true,
handler() {
// TODO
}
},
},
{
key: 'bringToTop',
title: '置于顶层',
icon: <XIcon type={'icon-bring-to-top'} />,
handler: shortcuts.bringToTop.handler
handler: shortcuts.bringToTop.handler,
},
{
key: 'bringToBack',
title: '置于底层',
icon: <XIcon type={'icon-bring-to-bottom'} />,
showDividerBehind: true,
handler: shortcuts.bringToBack.handler
handler: shortcuts.bringToBack.handler,
},
{
key: 'editCode',
@ -57,7 +57,7 @@ const nodeMenuConfig = [
},
handler(flowChart: Graph) {
flowChart.trigger('graph:editCode');
}
},
},
{
key: 'executeCode',
@ -68,8 +68,8 @@ const nodeMenuConfig = [
},
handler(flowChart: Graph) {
flowChart.trigger('graph:runCode');
}
}
},
},
];
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) => {
const { base, ...rest } = schema;
base.define(rest);
})
}),
);
const registerEvents = (flowChart: Graph): void => {
@ -59,18 +59,31 @@ const registerEvents = (flowChart: Graph): void => {
flowChart.trigger('graph:editCode');
});
flowChart.on('blank:contextmenu', (args) => {
const { e: { clientX, clientY } } = args;
const {
e: { clientX, clientY },
} = args;
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) => {
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
if(!flowChart.getSelectedCells().includes(node)) {
if (!flowChart.getSelectedCells().includes(node)) {
flowChart.cleanSelection();
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({
container,
rotating: false,
@ -101,7 +117,12 @@ const createFlowChart = (container: HTMLDivElement, miniMapContainer: HTMLDivEle
router: {
name: 'manhattan',
},
validateConnection({ sourceView, targetView, sourceMagnet, targetMagnet }) {
validateConnection({
sourceView,
targetView,
sourceMagnet,
targetMagnet,
}) {
if (!sourceMagnet) {
return false;
} else if (!targetMagnet) {
@ -126,7 +147,7 @@ const createFlowChart = (container: HTMLDivElement, miniMapContainer: HTMLDivEle
rubberband: true,
movable: true,
strict: true,
showNodeSelectionBox: true
showNodeSelectionBox: true,
},
// https://x6.antv.vision/zh/docs/tutorial/basic/snapline
snapline: {

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

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

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

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

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

@ -56,10 +56,18 @@ const EditModal: React.FC<IEditorModalProps> = (props) => {
onCancel={onCancel}
>
<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 />
</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 />
</Form.Item>
</Form>

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

@ -15,7 +15,6 @@ interface IExportModalProps {
}
const ExportModal: React.FC<IExportModalProps> = (props) => {
const { flowChart, visible, onClose } = props;
const onExportDSL = () => {
const dsl = JSON.stringify(flowChart.toJSON(), null, 2);
@ -32,9 +31,12 @@ const ExportModal: React.FC<IExportModalProps> = (props) => {
});
};
const onExportFlowChart = () => {
flowChart.toPNG((dataUri: string) => {
flowChart.toPNG(
(dataUri: string) => {
DataUri.downloadDataUri(dataUri, 'flowChart.png');
}, { padding: 50, ratio: '3.0' });
},
{ padding: 50, ratio: '3.0' },
);
};
return (
@ -48,15 +50,24 @@ const ExportModal: React.FC<IExportModalProps> = (props) => {
>
<div className={styles.modalContent}>
<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>
</div>
<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>
</div>
<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>
</div>
</div>
@ -78,7 +89,7 @@ const Helper = {
Helper.recursiveZip(dir, val);
}
}
}
}
},
};
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';
@ -10,9 +10,8 @@ interface IProps {
flowChart: Graph;
}
const Export: React.FC<IProps> = props => {
const {flowChart} = props;
const Export: React.FC<IProps> = (props) => {
const { flowChart } = props;
const [modalVisible, setModalVisible] = useState<boolean>(false);
const onOpenModal = () => setModalVisible(true);
@ -20,7 +19,9 @@ const Export: React.FC<IProps> = props => {
return (
<div className={styles.container}>
<Button type={'primary'} size={'small'} onClick={onOpenModal}></Button>
<Button type={'primary'} size={'small'} onClick={onOpenModal}>
</Button>
<ExportModal
flowChart={flowChart}
visible={modalVisible}

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

@ -1,82 +1,103 @@
import React from 'react';
import 'antd/es/modal/style';
import './index.less'
import './index.less';
import { Modal, Tabs } from 'antd';
const { TabPane } = Tabs;
interface IExportModalProps {
visible: boolean;
onClose: () => void;
}
const renderCode = (code: any) => {
return <pre>{code}</pre>
}
return <pre>{code}</pre>;
};
const QuickStart: React.FC = () => {
return <div>
return (
<div>
<h2></h2>
{renderCode(`$ git clone git@github.com:imgcook/imove.git\n
$ cd imove\n
$ npm install\n
$ npm run example`)}
<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>
<p></p>
<img 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" />
<img
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>
<p></p>
<ol>
<li>线</li>
<li></li>
</ol>
<h3>1. 线</h3>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523805396-4211e9c2-b501-4b23-aeae-f55147d67e8d.png" />
<p> zip 使</p>
<img
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>
<p> CLI</p>
<pre>
$ npm install -g @imove/cli
</pre>
<pre>$ npm install -g @imove/cli</pre>
<p>imove </p>
<pre>
$ cd yourProject
$ imove --init # imove -i
</pre>
<pre>$ cd yourProject $ imove --init # imove -i</pre>
<p></p>
<pre>
$ imove --dev # imove -d
</pre>
<pre>$ imove --dev # imove -d</pre>
<p></p>
<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>
<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>
<b></b>
<ol>
<li> logic.on ctx.emit </li>
<li> logic.invoke </li>
<li>
logic.on ctx.emit
</li>
<li>
logic.invoke
</li>
</ol>
</div>
}
);
};
const CreateNode: React.FC = () => {
return (
<div>
<h2></h2>
<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>
</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>
)
}
);
};
const NodeType: React.FC = () => {
return (
@ -84,99 +105,140 @@ const NodeType: React.FC = () => {
<h2></h2>
<p> iMove 3 </p>
<ol>
<li>/</li>
<li>
/
</li>
<li>//</li>
<li></li>
</ol>
<b> </b>
<p><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>
<p>
<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>
)
}
);
};
const ConfigNode: React.FC = () => {
return (
<div>
<h2></h2>
<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>
<ul>
<li></li>
<li></li>
<li>使</li>
<li>
使
</li>
</ul>
</div>
)
}
);
};
const CodeStyle: React.FC = () => {
return (
<div>
<h2></h2>
<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>
<p> js import npm export iMove export promise</p>
<p> <b></b> </p>
<img
width="100%"
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
return fetch('/api/isLogin')\n
.then(res => res.json())\n
.then(res => {\n
const {success, data: {isLogin} = {}} = res;\n
return success && isLogin;\n
const {success, data: {isLogin} = {}} = res\n
return success && isLogin\n
}).catch(err => {\n
console.log('fetch /api/isLogin failed, the err is:', err);\n
return false;\n
});\n
console.log('fetch /api/isLogin failed, the err is:', err)\n
return false\n
})\n
}\n
`)}
<p><b></b> boolean </p>
<p>
<b></b> boolean
</p>
</div>
)
}
);
};
const NodeConnect: React.FC = () => {
return (
<div>
<h2></h2>
<p> iMove pipe<b></b> boolean boolean </p>
<p><b>profile接口</b><b></b></p>
<p>
iMove
pipe
<b></b> boolean
boolean
</p>
<p>
<b>profile接口</b><b></b>
</p>
{renderCode(`// 节点: 请求profile接口\n
export default async function() {\n
return fetch('/api/profile')\n
.then(res => res.json())\n
.then(res => {\n
const {success, data} = res;\n
return {success, data};\n
const {success, data} = res\n
return {success, data}\n
}).catch(err => {\n
console.log('fetch /api/isLogin failed, the err is:', err);\n
return {success: false};\n
});\n
console.log('fetch /api/isLogin failed, the err is:', err)\n
return {success: false}\n
})\n
}\n
`)}
{renderCode(`// 节点: 接口成功\n
export default async function(ctx) {\n
// 获取上游数据\n
const pipe = ctx.getPipe() || {};\n
return pipe.success;\n
const pipe = ctx.getPipe() || {}\n
return pipe.success\n
}\n
`)}
{renderCode(`// 节点: 返回数据\n
const doSomething = (data) => {\n
// TODO: 数据加工处理\n
return data;\n
};\n
return data\n
}\n
export default async function(ctx) {\n
// 这里获取到的上游数据,不是"接口成功"这个分支节点的返回值,而是"请求profile接口"这个节点的返回值\n
const pipe = ctx.getPipe() || {};\n
ctx.emit('updateUI', {profileData: doSomething(pipe.data)});\n
const pipe = ctx.getPipe() || {}\n
ctx.emit('updateUI', {profileData: doSomething(pipe.data)})\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>
)
}
);
};
const HowToUse: React.FC = () => {
return (
@ -189,47 +251,71 @@ const HowToUse: React.FC = () => {
</ol>
<h3>1. 线</h3>
<img width="100%" src="https://intranetproxy.alipay.com/skylark/lark/0/2021/png/317407/1610523805396-4211e9c2-b501-4b23-aeae-f55147d67e8d.png" />
<p> zip 使</p>
<img
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>
<p> CLI</p>
<pre>
$ npm install -g @imove/cli
</pre>
<pre>$ npm install -g @imove/cli</pre>
<p>imove </p>
<pre>
$ cd yourProject
$ imove --init # imove -i
</pre>
<pre>$ cd yourProject $ imove --init # imove -i</pre>
<p></p>
<pre>
$ imove --dev # imove -d
</pre>
<pre>$ imove --dev # imove -d</pre>
<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>
<ol>
<li> logic.on ctx.emit </li>
<li> logic.invoke </li>
<li>
logic.on ctx.emit
</li>
<li>
logic.invoke
</li>
</ol>
</div>
)
}
);
};
const Document: React.FC = () => {
return (
<div>
<h2>使</h2>
<div><a target="blank" href="https://www.yuque.com/imove/docs">使</a></div>
<div><a target="blank" href="https://github.com/imgcook/imove/blob/master/README.md">github README</a></div>
<div>
<a target="blank" href="https://www.yuque.com/imove/docs">
使
</a>
</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 { visible, onClose } = props;
@ -244,7 +330,7 @@ const GuideModal: React.FC<IExportModalProps> = (props) => {
onCancel={onClose}
>
<div className="guide-container">
<Tabs tabPosition='left'>
<Tabs tabPosition="left">
<TabPane className="tabPane" tab="简单上手" key="1">
<QuickStart />
</TabPane>
@ -270,7 +356,6 @@ const GuideModal: React.FC<IExportModalProps> = (props) => {
<Document />
</TabPane>
</Tabs>
</div>
</Modal>
);

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

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

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

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

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

@ -25,7 +25,12 @@ const Checkbox: React.FC<IProps> = (props) => {
return (
<div className={styles.container}>
<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>
);
};

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

@ -24,7 +24,12 @@ const Input: React.FC<IProps> = (props) => {
return (
<div>
<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>
);
};

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

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

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

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

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

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

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

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

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

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

@ -14,7 +14,11 @@ const CELL_SCALE = 0.7;
const GENERAL_GROUP = {
key: 'general',
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 } = {
'imove-start-preview': 'imove-start',
@ -47,7 +51,10 @@ const SideBar: React.FC<ISideBarProps> = (props) => {
return (
<div className={styles.container}>
{dnd && (
<Collapse className={styles.collapse} defaultActiveKey={['general', 'custom']}>
<Collapse
className={styles.collapse}
defaultActiveKey={['general', 'custom']}
>
{groups.map((group) => (
<Panel key={group.key} header={group.name}>
<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 ColorPicker from '../../../components/colorPicker';
import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils';
import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
interface IProps {
flowChart: Graph;
@ -29,7 +32,10 @@ const options = {
return (
<div className={styles.bgColorContainer}>
<XIcon className={styles.fillIcon} type={'icon-fill'} />
<div className={styles.colorPreview} style={{ backgroundColor: bgColor }} />
<div
className={styles.colorPreview}
style={{ backgroundColor: bgColor }}
/>
</div>
);
},
@ -39,7 +45,9 @@ const options = {
return <ColorPicker color={bgColor} onChangeComplete={onChangeComplete} />;
},
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) {
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 ColorPicker from '../../../components/colorPicker';
import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils';
import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
interface IProps {
flowChart: Graph;
@ -29,17 +32,24 @@ const options = {
return (
<div className={styles.borderColorContainer}>
<HighlightOutlined className={styles.borderColorIcon} />
<div className={styles.colorPreview} style={{ backgroundColor: borderColor }} />
<div
className={styles.colorPreview}
style={{ backgroundColor: borderColor }}
/>
</div>
);
},
getOverlay(flowChart: Graph, onChange: (data: any) => void) {
const borderColor = options.getCurBorderColor(flowChart);
const onChangeComplete = (color: string) => onChange(color);
return <ColorPicker color={borderColor} onChangeComplete={onChangeComplete} />;
return (
<ColorPicker color={borderColor} onChangeComplete={onChangeComplete} />
);
},
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) {
return !hasNodeSelected(flowChart);

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

@ -37,7 +37,11 @@ const makeDropdownWidget = (options: IOptions) => {
};
return (
<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(' ')}>
{getIcon(flowChart)} <CaretDownOutlined className={styles.caret} />
</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) => {
flowChart.getSelectedCells().forEach((cell) => cell.setAttrs({ label: { fontSize: value } }));
flowChart
.getSelectedCells()
.forEach((cell) => cell.setAttrs({ label: { fontSize: value } }));
},
disabled(flowChart: Graph) {
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 { safeGet } from '../../../utils';
import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils';
import { AlignLeftOutlined, AlignCenterOutlined, AlignRightOutlined } from '@ant-design/icons';
import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
import {
AlignLeftOutlined,
AlignCenterOutlined,
AlignRightOutlined,
} from '@ant-design/icons';
interface IProps {
flowChart: Graph;
@ -89,7 +96,9 @@ const HorizontalAlign: React.FC<IProps> = makeDropdownWidget({
);
},
handler: (flowChart: Graph, value: any) => {
getSelectedNodes(flowChart).forEach((node) => node.setAttrs({ label: value }));
getSelectedNodes(flowChart).forEach((node) =>
node.setAttrs({ label: value }),
);
},
disabled(flowChart: Graph) {
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 XIcon from '../../../components/xIcon';
import makeDropdownWidget from './common/makeDropdownWidget';
import { hasEdgeSelected, getSelectedEdges } from '../../../utils/flowChartUtils';
import {
hasEdgeSelected,
getSelectedEdges,
} from '../../../utils/flowChartUtils';
interface IProps {
flowChart: Graph;
@ -65,7 +68,9 @@ const LineStyle: React.FC<IProps> = makeDropdownWidget({
);
},
handler: (flowChart: Graph, value: any) => {
getSelectedEdges(flowChart).forEach((edge) => edge.setAttrs({ line: value }));
getSelectedEdges(flowChart).forEach((edge) =>
edge.setAttrs({ line: value }),
);
},
disabled(flowChart: Graph) {
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'} />,
handler(selectedNodes: Cell[]) {
let minLeftValue = Infinity;
selectedNodes.forEach(node => {
const { x } = node.getProp<{ x: number, y: number }>('position');
selectedNodes.forEach((node) => {
const { x } = node.getProp<{ x: number; y: number }>('position');
if (x < minLeftValue) {
minLeftValue = x;
}
});
selectedNodes.forEach(node => node.setProp({ position: { x: minLeftValue } }));
}
selectedNodes.forEach((node) =>
node.setProp({ position: { x: minLeftValue } }),
);
},
},
horizontalCenter: {
text: '水平居中',
@ -40,9 +42,11 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
handler(selectedNodes: Cell[]) {
let minLeftValue = Infinity;
let maxRightValue = -Infinity;
selectedNodes.forEach(node => {
const { x } = node.getProp<{ x: number, y: number }>('position');
const { width } = node.getProp<{ width: number, height: number }>('size');
selectedNodes.forEach((node) => {
const { x } = node.getProp<{ x: number; y: number }>('position');
const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
if (x < minLeftValue) {
minLeftValue = x;
}
@ -51,43 +55,51 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
}
});
const centerValue = (minLeftValue + maxRightValue) / 2;
selectedNodes.forEach(node => {
const { width } = node.getProp<{ width: number, height: number }>('size');
selectedNodes.forEach((node) => {
const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
node.setProp({ position: { x: centerValue - width / 2 } });
});
}
},
},
right: {
text: '右对齐',
icon: <XIcon type={'icon-align-right'} />,
handler(selectedNodes: Cell[]) {
let maxRightValue = -Infinity;
selectedNodes.forEach(node => {
const { x } = node.getProp<{ x: number, y: number }>('position');
const { width } = node.getProp<{ width: number, height: number }>('size');
selectedNodes.forEach((node) => {
const { x } = node.getProp<{ x: number; y: number }>('position');
const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
if (x + width > maxRightValue) {
maxRightValue = x + width;
}
});
selectedNodes.forEach(node => {
const { width } = node.getProp<{ width: number, height: number }>('size');
node.setProp({ position: { x: maxRightValue - width } })
selectedNodes.forEach((node) => {
const { width } = node.getProp<{ width: number; height: number }>(
'size',
);
node.setProp({ position: { x: maxRightValue - width } });
});
}
},
},
top: {
text: '顶部对齐',
icon: <XIcon type={'icon-align-top'} />,
handler(selectedNodes: Cell[]) {
let minTopValue = Infinity;
selectedNodes.forEach(node => {
const { y } = node.getProp<{ x: number, y: number }>('position');
selectedNodes.forEach((node) => {
const { y } = node.getProp<{ x: number; y: number }>('position');
if (y < minTopValue) {
minTopValue = y;
}
});
selectedNodes.forEach(node => node.setProp({ position: { y: minTopValue } }));
}
selectedNodes.forEach((node) =>
node.setProp({ position: { y: minTopValue } }),
);
},
},
verticalCenter: {
text: '垂直居中',
@ -95,9 +107,11 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
handler(selectedNodes: Cell[]) {
let minTopValue = Infinity;
let maxBottomValue = -Infinity;
selectedNodes.forEach(node => {
const { y } = node.getProp<{ x: number, y: number }>('position');
const { height } = node.getProp<{ width: number, height: number }>('size');
selectedNodes.forEach((node) => {
const { y } = node.getProp<{ x: number; y: number }>('position');
const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
if (y < minTopValue) {
minTopValue = y;
}
@ -106,30 +120,36 @@ const ALIGN_MAP: { [key: string]: AlignItem } = {
}
});
const centerValue = (minTopValue + maxBottomValue) / 2;
selectedNodes.forEach(node => {
const { height } = node.getProp<{ width: number, height: number }>('size');
selectedNodes.forEach((node) => {
const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
node.setProp({ position: { y: centerValue - height / 2 } });
});
}
},
},
bottom: {
text: '底部对齐',
icon: <XIcon type={'icon-align-bottom'} />,
handler(selectedNodes: Cell[]) {
let maxTopValue = -Infinity;
selectedNodes.forEach(node => {
const { y } = node.getProp<{ x: number, y: number }>('position');
const { height } = node.getProp<{ width: number, height: number }>('size');
selectedNodes.forEach((node) => {
const { y } = node.getProp<{ x: number; y: number }>('position');
const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
if (y + height > maxTopValue) {
maxTopValue = y + height;
}
});
selectedNodes.forEach(node => {
const { height } = node.getProp<{ width: number, height: number }>('size');
node.setProp({ position: { y: maxTopValue - height } })
selectedNodes.forEach((node) => {
const { height } = node.getProp<{ width: number; height: number }>(
'size',
);
node.setProp({ position: { y: maxTopValue - height } });
});
}
}
},
},
};
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 ColorPicker from '../../../components/colorPicker';
import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils';
import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
interface IProps {
flowChart: Graph;
@ -29,17 +32,24 @@ const options = {
return (
<div className={styles.textColorContainer}>
<XIcon className={styles.textIcon} type={'icon-font'} />
<div className={styles.colorPreview} style={{ backgroundColor: textColor }} />
<div
className={styles.colorPreview}
style={{ backgroundColor: textColor }}
/>
</div>
);
},
getOverlay(flowChart: Graph, onChange: (data: any) => void) {
const textColor = options.getCurTextColor(flowChart);
const onChangeComplete = (color: string) => onChange(color);
return <ColorPicker color={textColor} onChangeComplete={onChangeComplete} />;
return (
<ColorPicker color={textColor} onChangeComplete={onChangeComplete} />
);
},
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) {
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) {
const cells = flowChart.getSelectedCells();
if (cells.length > 0) {
return safeGet(cells, '0.attrs.label.textDecoration', 'none') === 'underline';
return (
safeGet(cells, '0.attrs.label.textDecoration', 'none') === 'underline'
);
} else {
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 { safeGet } from '../../../utils';
import makeDropdownWidget from './common/makeDropdownWidget';
import { hasNodeSelected, getSelectedNodes } from '../../../utils/flowChartUtils';
import {
hasNodeSelected,
getSelectedNodes,
} from '../../../utils/flowChartUtils';
import {
VerticalAlignTopOutlined,
VerticalAlignMiddleOutlined,
@ -93,7 +96,9 @@ const VerticalAlign: React.FC<IProps> = makeDropdownWidget({
);
},
handler: (flowChart: Graph, value: any) => {
getSelectedNodes(flowChart).forEach((node) => node.setAttrs({ label: value }));
getSelectedNodes(flowChart).forEach((node) =>
node.setAttrs({ label: value }),
);
},
disabled(flowChart: Graph) {
return !hasNodeSelected(flowChart);

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

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

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

@ -1,13 +1,13 @@
import axios from 'axios';
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[] => {
let matchRet = null;
const pkgNames: string[] = [];
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);
// NOTE: ignore relative path (./ and ../) and excludePkgs
if (
@ -22,26 +22,31 @@ const extractPkgs = (code: string, excludePkgs?: string[]): string[] => {
};
const getPkgLatestVersion = (pkg: string): Promise<string[]> => {
return axios.get(`https://registry.npm.taobao.org/${pkg}`)
.then(res => {
return axios
.get(`https://registry.npm.taobao.org/${pkg}`)
.then((res) => {
return [pkg, safeGet(res, 'data.dist-tags.latest', '*')];
}).catch(err => {
})
.catch((err) => {
console.log(`get package ${pkg} info failed, the error is:`, err);
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);
return Promise
.all(pkgs.map(pkg => getPkgLatestVersion(pkg)))
.then(pkgResults => {
return Promise.all(pkgs.map((pkg) => getPkgLatestVersion(pkg)))
.then((pkgResults) => {
const map: any = {};
pkgResults.forEach(([pkg, version]) => {
map[pkg] = version;
});
return map;
}).catch(err => {
})
.catch((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 => {
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 => {
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[] => {
@ -20,10 +26,14 @@ export const getSelectedEdges = (flowChart: Graph): Cell[] => {
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 selectedCells = flowChart.getSelectedCells();
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);
};
export const executeScript = (code: string, type: string = 'module') => {
export const executeScript = (code: string, type = 'module') => {
const script = document.createElement('script');
script.type = type;
script.text = code;

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

@ -8,17 +8,30 @@ interface AddNodeProps {
addSiblingField: () => void;
}
function AddNodeIcon({ addChildField, addSiblingField }: AddNodeProps): JSX.Element {
function AddNodeIcon({
addChildField,
addSiblingField,
}: AddNodeProps): JSX.Element {
const { t } = useTranslation();
const menu = (
<Menu>
<Menu.Item>
<div tabIndex={0} role="button" onClick={addChildField} onKeyDown={addChildField}>
<div
tabIndex={0}
role="button"
onClick={addChildField}
onKeyDown={addChildField}
>
{t('child_node')}
</div>
</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')}
</div>
</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}
>
{itemData.type === 'object' && !isUndefined(itemData.properties) ? (
<SchemaForm data={itemData} keyRoute={fieldKeyRoute.concat('properties')} />
<SchemaForm
data={itemData}
keyRoute={fieldKeyRoute.concat('properties')}
/>
) : null}
</SchemaItem>
);

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

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

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

@ -1,6 +1,12 @@
import produce, { Draft } from 'immer';
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';
let fieldNum = 0;
@ -25,7 +31,11 @@ const removeRequired = (state: SchemaState, keyRoute: KeyRoute): void => {
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 requiredKeyRoute = oldKeyRoute.slice(0, -2).concat('required');
const required = get(state, requiredKeyRoute) || [];
@ -76,7 +86,11 @@ const removeField = (state: SchemaState, keyRoute: KeyRoute): void => {
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 parentKeyRoute = keyRoute.slice(0, -1);
const properties = get(state, parentKeyRoute);
@ -99,18 +113,30 @@ const changeField = (state: SchemaState, keyRoute: KeyRoute, newField: string):
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(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
const titleKeyRoute = keyRoute.concat('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
const descKeyRoute = keyRoute.concat('description');
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') {
const { properties } = schema;
const fields = Object.keys(properties);

Loading…
Cancel
Save