diff --git a/README.md b/README.md index 3dda2b1b..6b76ad40 100644 --- a/README.md +++ b/README.md @@ -25,17 +25,19 @@ Github:[releases](https://github.com/wanglin2/mind-map/releases)。 百度云盘:[地址](https://pan.baidu.com/s/1huasEbKsGNH2Af68dvWiOg?pwd=3bp3)。 +> 客户端版本会落后于在线版本,尝试最新功能请优先使用在线版。 + # 特性 - [x] 插件化架构,除核心功能外,其他功能作为插件提供,按需使用,减小打包体积 - [x] 支持逻辑结构图、思维导图、组织结构图、目录组织图、时间轴(横向、竖向)、鱼骨图等结构 - [x] 内置多种主题,允许高度自定义样式,支持注册新主题 -- [x] 节点内容支持文本(普通文本、富文本)、图片、图标、超链接、备注、标签、概要 +- [x] 节点内容支持文本(普通文本、富文本)、图片、图标、超链接、备注、标签、概要、数学公式 - [x] 节点支持拖拽(拖拽移动、自由调整)、多种节点形状,支持使用 DDM 完全自定义节点内容 - [x] 支持画布拖动、缩放 - [x] 支持鼠标按键拖动选择和Ctrl+左键两种多选节点方式 - [x] 支持导出为`json`、`png`、`svg`、`pdf`、`markdown`、`xmind`,支持从`json`、`xmind`、`markdown`导入 -- [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印 +- [x] 支持快捷键、前进后退、关联线、搜索替换、小地图、水印、滚动条 - [x] 提供丰富的配置,满足各种场景各种使用习惯 # 安装 @@ -171,4 +173,20 @@ const mindMap = new MindMap({ 南风 + + + 蜉蝣撼大叔 + + + + + + + + + + + + 沐风牧草 +

\ No newline at end of file diff --git a/index.html b/index.html index fdc5067b..683b4ee7 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ 思绪思维导图
+ } diff --git a/simple-mind-map/full.js b/simple-mind-map/full.js index 8ee27854..68f47c64 100644 --- a/simple-mind-map/full.js +++ b/simple-mind-map/full.js @@ -14,6 +14,7 @@ import TouchEvent from './src/plugins/TouchEvent.js' import Search from './src/plugins/Search.js' import Painter from './src/plugins/Painter.js' import Scrollbar from './src/plugins/Scrollbar.js' +import Formula from './src/plugins/Formula.js' import xmind from './src/parse/xmind.js' import markdown from './src/parse/markdown.js' import icons from './src/svg/icons.js' @@ -28,8 +29,7 @@ MindMap.constants = constants MindMap.themes = themes MindMap.defaultTheme = defaultTheme -MindMap - .usePlugin(MiniMap) +MindMap.usePlugin(MiniMap) .usePlugin(Watermark) .usePlugin(Drag) .usePlugin(KeyboardNavigation) @@ -44,5 +44,6 @@ MindMap .usePlugin(Search) .usePlugin(Painter) .usePlugin(Scrollbar) + .usePlugin(Formula) -export default MindMap \ No newline at end of file +export default MindMap diff --git a/simple-mind-map/index.d.ts b/simple-mind-map/index.d.ts deleted file mode 100644 index 88636563..00000000 --- a/simple-mind-map/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -// declare module "simple-mind-map"; -declare module 'simple-mind-map'{ - class MindMap { - constructor(opt:any); - handleOpt(opt:any):void; - render(callback:any, source:string):void; - reRender(callback:any, source:string):void; - resize():void; - on(event:any, fn:any):void; - setFullData(data:any):void; - getData(withConfig:any):any; - } - export default MindMap; -} diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 26715799..031571cc 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -24,6 +24,10 @@ import { defaultOpt } from './src/constants/defaultOptions' // 思维导图 class MindMap { // 构造函数 + /** + * + * @param {defaultOpt} opt + */ constructor(opt = {}) { // 合并选项 this.opt = this.handleOpt(merge(defaultOpt, opt)) @@ -36,7 +40,8 @@ class MindMap { // 画布宽高 this.width = this.elRect.width this.height = this.elRect.height - if (this.width <= 0 || this.height <= 0) throw new Error('容器元素el的宽高不能为0') + if (this.width <= 0 || this.height <= 0) + throw new Error('容器元素el的宽高不能为0') // 添加css this.cssEl = null @@ -87,7 +92,7 @@ class MindMap { }) // 初始渲染 - this.render() + this.render(this.opt.fit ? () => this.view.fit() : () => {}) setTimeout(() => { this.command.addHistory() }, 0) @@ -360,7 +365,7 @@ class MindMap { // 克隆一份数据 let clone = svg.clone() // 添加必要的样式 - clone.add(SVG(``)) + clone.add(SVG(``)) // 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题 if ( (rect.width > origWidth || rect.height > origHeight) && @@ -448,6 +453,7 @@ class MindMap { // 插件列表 MindMap.pluginList = [] MindMap.usePlugin = (plugin, opt = {}) => { + if (MindMap.hasPlugin(plugin) !== -1) return MindMap plugin.pluginOpt = opt MindMap.pluginList.push(plugin) return MindMap diff --git a/simple-mind-map/package-lock.json b/simple-mind-map/package-lock.json index d05ccda1..5efac16e 100644 --- a/simple-mind-map/package-lock.json +++ b/simple-mind-map/package-lock.json @@ -1,11 +1,11 @@ { "name": "simple-mind-map", - "version": "0.6.15-fix.2", + "version": "0.7.2", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.6.15-fix.2", + "version": "0.7.2", "license": "MIT", "dependencies": { "@svgdotjs/svg.js": "^3.0.16", @@ -13,8 +13,10 @@ "eventemitter3": "^4.0.7", "jspdf": "^2.5.1", "jszip": "^3.10.1", + "katex": "^0.16.8", "mdast-util-from-markdown": "^1.3.0", "quill": "^1.3.6", + "tern": "^0.24.3", "uuid": "^9.0.0", "xml-js": "^1.6.11" }, @@ -187,6 +189,36 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-loose": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-6.1.0.tgz", + "integrity": "sha512-FHhXoiF0Uch3IqsrnPpWwCtiv5PYvipTpT1k9lDMgQVVYc9iDuSl5zdJV358aI8twfHCYMFBRVYvAVki9wC/ng==", + "dependencies": { + "acorn": "^6.2.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-loose/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -247,8 +279,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-arraybuffer": { "version": "1.0.2", @@ -263,7 +294,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -371,11 +401,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/core-js": { "version": "3.27.1", @@ -523,6 +560,31 @@ "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==", "optional": true }, + "node_modules/enhanced-resolve": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz", + "integrity": "sha512-n6e4bsCpzsP0OB76X+vEWhySUQI8GHPVFVK+3QkX35tbryy2WoeGeK5kQ+oxzgDVHjIZyz5fyS60Mi3EpQLc0Q==", + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.3.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.3" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -796,8 +858,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/function-bind": { "version": "1.1.1", @@ -829,7 +890,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -872,6 +932,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "node_modules/grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", @@ -990,7 +1055,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1148,6 +1212,21 @@ "setimmediate": "^1.0.5" } }, + "node_modules/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -1233,6 +1312,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha512-QTNXnl79X97kZ9jJk/meJrtDuvgvRakX5LU7HZW1L7MsXHuSTwoMIzN9tOLLH3Xfsj/gbsSqX/ovnsqz246zKQ==", + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, "node_modules/micromark": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", @@ -1659,7 +1747,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1686,6 +1773,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -1713,7 +1808,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -1800,7 +1894,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -1849,6 +1942,11 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -2140,6 +2238,50 @@ "node": ">=12.0.0" } }, + "node_modules/tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tern": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/tern/-/tern-0.24.3.tgz", + "integrity": "sha512-Z8uvtdWIlFn1GWy0HW5FhZ8VDryZwoJUdnjZU25C7/PBOltLIn1uv+WF3rVq6S1761YbsmbZYRP/l0ZJBCkvrw==", + "dependencies": { + "acorn": "^6.0.0", + "acorn-loose": "^6.0.0", + "acorn-walk": "^6.0.0", + "enhanced-resolve": "^2.2.2", + "glob": "^7.1.1", + "minimatch": "^3.0.3", + "resolve-from": "2.0.0" + }, + "bin": { + "tern": "bin/tern" + } + }, + "node_modules/tern/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/tern/node_modules/resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -2266,8 +2408,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/xml-js": { "version": "1.6.11", @@ -2418,6 +2559,26 @@ "dev": true, "requires": {} }, + "acorn-loose": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-6.1.0.tgz", + "integrity": "sha512-FHhXoiF0Uch3IqsrnPpWwCtiv5PYvipTpT1k9lDMgQVVYc9iDuSl5zdJV358aI8twfHCYMFBRVYvAVki9wC/ng==", + "requires": { + "acorn": "^6.2.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2459,8 +2620,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-arraybuffer": { "version": "1.0.2", @@ -2472,7 +2632,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2549,11 +2708,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "core-js": { "version": "3.27.1", @@ -2660,6 +2823,25 @@ "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==", "optional": true }, + "enhanced-resolve": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz", + "integrity": "sha512-n6e4bsCpzsP0OB76X+vEWhySUQI8GHPVFVK+3QkX35tbryy2WoeGeK5kQ+oxzgDVHjIZyz5fyS60Mi3EpQLc0Q==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.3.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.3" + } + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2872,8 +3054,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "function-bind": { "version": "1.1.1", @@ -2899,7 +3080,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2927,6 +3107,11 @@ "type-fest": "^0.20.2" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, "grapheme-splitter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", @@ -3009,7 +3194,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -3131,6 +3315,14 @@ "setimmediate": "^1.0.5" } }, + "katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==", + "requires": { + "commander": "^8.3.0" + } + }, "kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -3196,6 +3388,15 @@ "@types/mdast": "^3.0.0" } }, + "memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha512-QTNXnl79X97kZ9jJk/meJrtDuvgvRakX5LU7HZW1L7MsXHuSTwoMIzN9tOLLH3Xfsj/gbsSqX/ovnsqz246zKQ==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, "micromark": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.1.0.tgz", @@ -3412,7 +3613,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -3433,6 +3633,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -3451,7 +3656,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "requires": { "wrappy": "1" } @@ -3516,8 +3720,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -3548,6 +3751,11 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3752,6 +3960,37 @@ "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", "optional": true }, + "tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==" + }, + "tern": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/tern/-/tern-0.24.3.tgz", + "integrity": "sha512-Z8uvtdWIlFn1GWy0HW5FhZ8VDryZwoJUdnjZU25C7/PBOltLIn1uv+WF3rVq6S1761YbsmbZYRP/l0ZJBCkvrw==", + "requires": { + "acorn": "^6.0.0", + "acorn-loose": "^6.0.0", + "acorn-walk": "^6.0.0", + "enhanced-resolve": "^2.2.2", + "glob": "^7.1.1", + "minimatch": "^3.0.3", + "resolve-from": "2.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==" + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ==" + } + } + }, "text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -3847,8 +4086,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "xml-js": { "version": "1.6.11", diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index 30feca85..506c5f20 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.7.0", + "version": "0.7.2", "description": "一个简单的web在线思维导图", "authors": [ { @@ -12,6 +12,8 @@ "url": "http://lxqnsys.com/" } ], + "types": "./types/index.d.ts", + "typings": "./types/index.d.ts", "license": "MIT", "repository": { "type": "git", @@ -19,18 +21,21 @@ }, "scripts": { "lint": "eslint src/", - "format": "prettier --write ." + "format": "prettier --write .", + "types": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir types --target es2017" }, "module": "index.js", - "__main": "./dist/simpleMindMap.umd.min.js", + "main": "./dist/simpleMindMap.umd.min.js", "dependencies": { "@svgdotjs/svg.js": "^3.0.16", "deepmerge": "^1.5.2", "eventemitter3": "^4.0.7", "jspdf": "^2.5.1", "jszip": "^3.10.1", + "katex": "^0.16.8", "mdast-util-from-markdown": "^1.3.0", "quill": "^1.3.6", + "tern": "^0.24.3", "uuid": "^9.0.0", "xml-js": "^1.6.11" }, diff --git a/simple-mind-map/scripts/walkJsFiles.js b/simple-mind-map/scripts/walkJsFiles.js index 96124251..69ed7c1e 100644 --- a/simple-mind-map/scripts/walkJsFiles.js +++ b/simple-mind-map/scripts/walkJsFiles.js @@ -17,7 +17,7 @@ const transform = dir => { } const transformFile = file => { - console.log(file); + console.log(file) let content = fs.readFileSync(file, 'utf-8') countCodeLines(content) // transformComments(file, content) @@ -25,7 +25,7 @@ const transformFile = file => { // 统计代码行数 let totalLines = 0 -const countCodeLines = (content) => { +const countCodeLines = content => { totalLines += content.split(/\n/).length } @@ -43,4 +43,4 @@ const transformComments = (file, content) => { transform(entryPath) transformFile(path.join(__dirname, '../index.js')) -console.log(totalLines); \ No newline at end of file +console.log(totalLines) diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index cc316228..c538e5ad 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -1,27 +1,3 @@ -// 标签颜色列表 -export const tagColorList = [ - { - color: 'rgb(77, 65, 0)', - background: 'rgb(255, 244, 179)' - }, - { - color: 'rgb(0, 50, 77)', - background: 'rgb(179, 229, 255)' - }, - { - color: 'rgb(77, 0, 73)', - background: 'rgb(255, 179, 251)' - }, - { - color: 'rgb(57, 77, 0)', - background: 'rgb(236, 255, 179)' - }, - { - color: 'rgb(0, 77, 47)', - background: 'rgb(179, 255, 226)' - } -] - // 主题列表 export const themeList = [ { @@ -249,6 +225,10 @@ export const CONSTANTS = { PASTE_TYPE: { CLIP_BOARD: 'clipBoard', CANVAS: 'canvas' + }, + SCROLL_BAR_DIR: { + VERTICAL: 'vertical', + HORIZONTAL: 'horizontal' } } @@ -257,42 +237,42 @@ export const initRootNodePositionMap = { [CONSTANTS.INIT_ROOT_NODE_POSITION.TOP]: 0, [CONSTANTS.INIT_ROOT_NODE_POSITION.RIGHT]: 1, [CONSTANTS.INIT_ROOT_NODE_POSITION.BOTTOM]: 1, - [CONSTANTS.INIT_ROOT_NODE_POSITION.CENTER]: 0.5, + [CONSTANTS.INIT_ROOT_NODE_POSITION.CENTER]: 0.5 } // 布局结构列表 export const layoutList = [ { name: '逻辑结构图', - value: CONSTANTS.LAYOUT.LOGICAL_STRUCTURE, + value: CONSTANTS.LAYOUT.LOGICAL_STRUCTURE }, { name: '思维导图', - value: CONSTANTS.LAYOUT.MIND_MAP, + value: CONSTANTS.LAYOUT.MIND_MAP }, { name: '组织结构图', - value: CONSTANTS.LAYOUT.ORGANIZATION_STRUCTURE, + value: CONSTANTS.LAYOUT.ORGANIZATION_STRUCTURE }, { name: '目录组织图', - value: CONSTANTS.LAYOUT.CATALOG_ORGANIZATION, + value: CONSTANTS.LAYOUT.CATALOG_ORGANIZATION }, { name: '时间轴', - value: CONSTANTS.LAYOUT.TIMELINE, + value: CONSTANTS.LAYOUT.TIMELINE }, { name: '时间轴2', - value: CONSTANTS.LAYOUT.TIMELINE2, + value: CONSTANTS.LAYOUT.TIMELINE2 }, { name: '竖向时间轴', - value: CONSTANTS.LAYOUT.VERTICAL_TIMELINE, + value: CONSTANTS.LAYOUT.VERTICAL_TIMELINE }, { name: '鱼骨图', - value: CONSTANTS.LAYOUT.FISHBONE, + value: CONSTANTS.LAYOUT.FISHBONE } ] export const layoutValueList = [ @@ -361,7 +341,7 @@ export const cssContent = ` stroke-width: 1; } - .smm-node:hover .smm-hover-node{ + .smm-node:not(.smm-node-dragging):hover .smm-hover-node{ display: block; } @@ -370,4 +350,4 @@ export const cssContent = ` opacity: 1; stroke-width: 2; } -` \ No newline at end of file +` diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 1f1b5709..6a4ad674 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -182,5 +182,27 @@ export const defaultOpt = { // 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 hoverRectPadding: 2, // 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中 - selectTextOnEnterEditText: false + selectTextOnEnterEditText: false, + // 删除节点后激活相邻节点 + deleteNodeActive: true, + // 拖拽节点时鼠标移动到画布边缘是否开启画布自动移动 + autoMoveWhenMouseInEdgeOnDrag: true, + // 是否首次加载fit view + fit: false, + // 拖拽多个节点时随鼠标移动的示意矩形的样式配置 + dragMultiNodeRectConfig: { + width: 40, + height: 20, + fill: '' // 填充颜色,如果不传默认使用连线的颜色 + }, + // 节点拖拽时新位置的示意矩形的填充颜色,如果不传默认使用连线的颜色 + dragPlaceholderRectFill: '', + // 节点拖拽时的透明度配置 + dragOpacityConfig: { + cloneNodeOpacity: 0.5, // 跟随鼠标移动的克隆节点或矩形的透明度 + beingDragNodeOpacity: 0.3 // 被拖拽节点的透明度 + }, + // 自定义标签的颜色 + // {pass: 'green, unpass: 'red'} + tagsColorMap: {} } diff --git a/simple-mind-map/src/core/command/Command.js b/simple-mind-map/src/core/command/Command.js index d8edde54..8b59b98d 100644 --- a/simple-mind-map/src/core/command/Command.js +++ b/simple-mind-map/src/core/command/Command.js @@ -37,7 +37,11 @@ class Command { this.commands[name].forEach(fn => { fn(...args) }) - if (['BACK', 'FORWARD', 'SET_NODE_ACTIVE', 'CLEAR_ACTIVE_NODE'].includes(name)) { + if ( + ['BACK', 'FORWARD', 'SET_NODE_ACTIVE', 'CLEAR_ACTIVE_NODE'].includes( + name + ) + ) { return } this.addHistory() @@ -78,7 +82,11 @@ class Command { } let data = this.getCopyData() // 此次数据和上次一样则不重复添加 - if (this.history.length > 0 && JSON.stringify(this.history[this.history.length - 1]) === JSON.stringify(data)) { + if ( + this.history.length > 0 && + JSON.stringify(this.history[this.history.length - 1]) === + JSON.stringify(data) + ) { return } // 删除当前历史指针后面的数据 @@ -123,7 +131,11 @@ class Command { let len = this.history.length if (this.activeHistoryIndex + step <= len - 1) { this.activeHistoryIndex += step - this.mindMap.emit('back_forward', this.activeHistoryIndex, this.history.length) + this.mindMap.emit( + 'back_forward', + this.activeHistoryIndex, + this.history.length + ) let data = simpleDeepClone(this.history[this.activeHistoryIndex]) this.mindMap.emit('data_change', data) return data @@ -138,10 +150,10 @@ class Command { // 移除节点数据中的uid removeDataUid(data) { data = simpleDeepClone(data) - let walk = (root) => { + let walk = root => { delete root.data.uid if (root.children && root.children.length > 0) { - root.children.forEach((item) => { + root.children.forEach(item => { walk(item) }) } diff --git a/simple-mind-map/src/core/command/KeyCommand.js b/simple-mind-map/src/core/command/KeyCommand.js index 4b493fd6..39abc3c6 100644 --- a/simple-mind-map/src/core/command/KeyCommand.js +++ b/simple-mind-map/src/core/command/KeyCommand.js @@ -46,13 +46,20 @@ export default class KeyCommand { if (this.mindMap.richText && this.mindMap.richText.showTextEdit) { return } - if (this.mindMap.renderer.textEdit.showTextEdit || (this.mindMap.associativeLine && this.mindMap.associativeLine.showTextEdit)) { + if ( + this.mindMap.renderer.textEdit.showTextEdit || + (this.mindMap.associativeLine && + this.mindMap.associativeLine.showTextEdit) + ) { return } this.isInSvg = false }) window.addEventListener('keydown', e => { - if (this.isPause || (this.mindMap.opt.enableShortcutOnlyWhenMouseInSvg && !this.isInSvg)) { + if ( + this.isPause || + (this.mindMap.opt.enableShortcutOnlyWhenMouseInSvg && !this.isInSvg) + ) { return } Object.keys(this.shortcutMap).forEach(key => { diff --git a/simple-mind-map/src/core/event/Event.js b/simple-mind-map/src/core/event/Event.js index f1313b37..5aa9faea 100644 --- a/simple-mind-map/src/core/event/Event.js +++ b/simple-mind-map/src/core/event/Event.js @@ -113,7 +113,7 @@ class Event extends EventEmitter { this.isMiddleMousedown || (useLeftKeySelectionRightKeyDrag ? this.isRightMousedown - : this.isLeftMousedown) + : this.isLeftMousedown) ) { e.preventDefault() this.emit('drag', e, this) diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 3aa2500e..8ec946ce 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -13,7 +13,12 @@ import { walk, bfsWalk, loadImage, - isUndef + isUndef, + getTopAncestorsFomNodeList, + addDataToAppointNodes, + createUidForAppointNodes, + formatDataToArray, + getNodeIndex } from '../../utils' import { shapeList } from './node/Shape' import { lineStyleProps } from '../../themes/default' @@ -40,7 +45,6 @@ const layouts = { } // 渲染 - class Render { // 构造函数 constructor(opt = {}) { @@ -132,9 +136,18 @@ class Render { // 插入同级节点 this.insertNode = this.insertNode.bind(this) this.mindMap.command.add('INSERT_NODE', this.insertNode) + // 插入多个同级节点 + this.insertMultiNode = this.insertMultiNode.bind(this) + this.mindMap.command.add('INSERT_MULTI_NODE', this.insertMultiNode) // 插入子节点 this.insertChildNode = this.insertChildNode.bind(this) this.mindMap.command.add('INSERT_CHILD_NODE', this.insertChildNode) + // 插入多个子节点 + this.insertMultiChildNode = this.insertMultiChildNode.bind(this) + this.mindMap.command.add( + 'INSERT_MULTI_CHILD_NODE', + this.insertMultiChildNode + ) // 上移节点 this.upNode = this.upNode.bind(this) this.mindMap.command.add('UP_NODE', this.upNode) @@ -202,6 +215,9 @@ class Render { // 设置节点标签 this.setNodeTag = this.setNodeTag.bind(this) this.mindMap.command.add('SET_NODE_TAG', this.setNodeTag) + // 设置节点公式 + this.insertFormula = this.insertFormula.bind(this) + this.mindMap.command.add('INSERT_FORMULA', this.insertFormula) // 添加节点概要 this.addGeneralization = this.addGeneralization.bind(this) this.mindMap.command.add('ADD_GENERALIZATION', this.addGeneralization) @@ -382,19 +398,10 @@ class Render { // 检索某个节点在激活列表里的索引 findActiveNodeIndex(node) { return this.activeNodeList.findIndex(item => { - return item === node + return item.uid === node.uid }) } - // 获取节点在同级里的索引位置 - getNodeIndex(node) { - return node.parent - ? node.parent.children.findIndex(item => { - return item === node - }) - : 0 - } - // 全选 selectAll() { walk( @@ -439,58 +446,117 @@ class Render { } } - // 规范指定节点数据 - formatAppointNodes(appointNodes) { - if (!appointNodes) return [] - return Array.isArray(appointNodes) ? appointNodes : [appointNodes] - } - - // 插入同级节点,多个节点只会操作第一个节点 + // 插入同级节点 insertNode( openEdit = true, appointNodes = [], appointData = null, appointChildren = [] ) { - appointNodes = this.formatAppointNodes(appointNodes) + appointNodes = formatDataToArray(appointNodes) if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { return } this.textEdit.hideEditTextBox() - let { + const { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt - let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList - let first = list[0] - if (first.isGeneralization) { - return + const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList + const handleMultiNodes = list.length > 1 + const isRichText = !!this.mindMap.richText + const params = { + expand: true, + richText: isRichText, + resetRichText: isRichText, + isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态 } - if (first.isRoot) { - this.insertChildNode(openEdit, appointNodes, appointData) - } else { - let text = - first.layerIndex === 1 - ? defaultInsertSecondLevelNodeText - : defaultInsertBelowSecondLevelNodeText - if (first.layerIndex === 1) { - first.parent.destroy() + // 动态指定的子节点数据也需要添加相关属性 + appointChildren = addDataToAppointNodes(appointChildren, { + ...params + }) + const needDestroyNodeList = {} + list.forEach(node => { + if (node.isGeneralization || node.isRoot) { + return } - let index = this.getNodeIndex(first) - let isRichText = !!this.mindMap.richText - first.parent.nodeData.children.splice(index + 1, 0, { - inserting: openEdit, + const parent = node.parent + const isOneLayer = node.layerIndex === 1 + // 插入二级节点时根节点需要重新渲染 + if (isOneLayer && !needDestroyNodeList[parent.uid]) { + needDestroyNodeList[parent.uid] = parent + } + // 新插入节点的默认文本 + const text = isOneLayer + ? defaultInsertSecondLevelNodeText + : defaultInsertBelowSecondLevelNodeText + // 计算插入位置 + const index = parent.nodeData.children.findIndex(item => { + return item.data.uid === node.uid + }) + parent.nodeData.children.splice(index + 1, 0, { + inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式, data: { text: text, - expand: true, - richText: isRichText, - resetRichText: isRichText, + ...params, ...(appointData || {}) }, children: [...appointChildren] }) - this.mindMap.render() + }) + Object.keys(needDestroyNodeList).forEach(key => { + needDestroyNodeList[key].destroy() + }) + // 如果同时对多个节点插入子节点,需要清除原来激活的节点 + if (handleMultiNodes || !openEdit) { + this.clearActive() } + this.mindMap.render() + } + + // 插入多个同级节点 + insertMultiNode(appointNodes, nodeList) { + if (!nodeList || nodeList.length <= 0) return + appointNodes = formatDataToArray(appointNodes) + if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { + return + } + this.textEdit.hideEditTextBox() + const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList + const isRichText = !!this.mindMap.richText + const params = { + expand: true, + richText: isRichText, + resetRichText: isRichText, + isActive: true + } + nodeList = addDataToAppointNodes(nodeList, params) + const needDestroyNodeList = {} + list.forEach(node => { + if (node.isGeneralization || node.isRoot) { + return + } + const parent = node.parent + const isOneLayer = node.layerIndex === 1 + // 插入二级节点时根节点需要重新渲染 + if (isOneLayer && !needDestroyNodeList[parent.uid]) { + needDestroyNodeList[parent.uid] = parent + } + // 计算插入位置 + const index = parent.nodeData.children.findIndex(item => { + return item.data.uid === node.uid + }) + parent.nodeData.children.splice( + index + 1, + 0, + ...createUidForAppointNodes(simpleDeepClone(nodeList)) + ) + }) + Object.keys(needDestroyNodeList).forEach(key => { + needDestroyNodeList[key].destroy() + }) + this.clearActive() + this.mindMap.render() } // 插入子节点 @@ -500,16 +566,28 @@ class Render { appointData = null, appointChildren = [] ) { - appointNodes = this.formatAppointNodes(appointNodes) + appointNodes = formatDataToArray(appointNodes) if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { return } this.textEdit.hideEditTextBox() - let { + const { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt - let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList + const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList + const handleMultiNodes = list.length > 1 + const isRichText = !!this.mindMap.richText + const params = { + expand: true, + richText: isRichText, + resetRichText: isRichText, + isActive: handleMultiNodes || !openEdit // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态 + } + // 动态指定的子节点数据也需要添加相关属性 + appointChildren = addDataToAppointNodes(appointChildren, { + ...params + }) list.forEach(node => { if (node.isGeneralization) { return @@ -517,17 +595,14 @@ class Render { if (!node.nodeData.children) { node.nodeData.children = [] } - let text = node.isRoot + const text = node.isRoot ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText - let isRichText = !!this.mindMap.richText node.nodeData.children.push({ - inserting: openEdit, + inserting: handleMultiNodes ? false : openEdit, // 如果同时对多个节点插入子节点,那么无需进入编辑模式 data: { text: text, - expand: true, - richText: isRichText, - resetRichText: isRichText, + ...params, ...(appointData || {}) }, children: [...appointChildren] @@ -538,6 +613,45 @@ class Render { node.destroy() } }) + // 如果同时对多个节点插入子节点,需要清除原来激活的节点 + if (handleMultiNodes || !openEdit) { + this.clearActive() + } + this.mindMap.render() + } + + // 插入多个子节点 + insertMultiChildNode(appointNodes, childList) { + if (!childList || childList.length <= 0) return + appointNodes = formatDataToArray(appointNodes) + if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { + return + } + this.textEdit.hideEditTextBox() + const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList + const isRichText = !!this.mindMap.richText + const params = { + expand: true, + richText: isRichText, + resetRichText: isRichText, + isActive: true + } + childList = addDataToAppointNodes(childList, params) + list.forEach(node => { + if (node.isGeneralization) { + return + } + if (!node.nodeData.children) { + node.nodeData.children = [] + } + node.nodeData.children.push(...childList) + // 插入子节点时自动展开子节点 + node.nodeData.data.expand = true + if (node.isRoot) { + node.destroy() + } + }) + this.clearActive() this.mindMap.render() } @@ -553,7 +667,7 @@ class Render { let parent = node.parent let childList = parent.children let index = childList.findIndex(item => { - return item === node + return item.uid === node.uid }) if (index === -1 || index === 0) { return @@ -580,7 +694,7 @@ class Render { let parent = node.parent let childList = parent.children let index = childList.findIndex(item => { - return item === node + return item.uid === node.uid }) if (index === -1 || index === childList.length - 1) { return @@ -598,19 +712,19 @@ class Render { // 复制节点 copy() { this.beingCopyData = this.copyNode() - this.setCoptyDataToClipboard(this.beingCopyData) + this.setCopyDataToClipboard(this.beingCopyData) } // 剪切节点 cut() { this.mindMap.execCommand('CUT_NODE', copyData => { this.beingCopyData = copyData - this.setCoptyDataToClipboard(copyData) + this.setCopyDataToClipboard(copyData) }) } // 将粘贴或剪切的数据设置到用户剪切板中 - setCoptyDataToClipboard(data) { + setCopyDataToClipboard(data) { if (navigator.clipboard) { navigator.clipboard.writeText( JSON.stringify({ @@ -701,13 +815,9 @@ class Render { } if (smmData) { this.mindMap.execCommand( - 'INSERT_CHILD_NODE', - false, + 'INSERT_MULTI_CHILD_NODE', [], - { - ...smmData.data - }, - [...smmData.children] + Array.isArray(smmData) ? smmData : [smmData] ) } else { this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], { @@ -741,89 +851,75 @@ class Render { // 将节点移动到另一个节点的前面 insertBefore(node, exist) { - if (node.isRoot) { - return - } - // 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新 - let nodeLayerChanged = - (node.layerIndex === 1 && exist.layerIndex !== 1) || - (node.layerIndex !== 1 && exist.layerIndex === 1) - // 移动节点 - let nodeParent = node.parent - let nodeBorthers = nodeParent.children - let nodeIndex = nodeBorthers.findIndex(item => { - return item === node - }) - if (nodeIndex === -1) { - return - } - nodeBorthers.splice(nodeIndex, 1) - nodeParent.nodeData.children.splice(nodeIndex, 1) - - // 目标节点 - let existParent = exist.parent - let existBorthers = existParent.children - let existIndex = existBorthers.findIndex(item => { - return item === exist - }) - if (existIndex === -1) { - return - } - existBorthers.splice(existIndex, 0, node) - existParent.nodeData.children.splice(existIndex, 0, node.nodeData) - this.mindMap.render(() => { - if (nodeLayerChanged) { - node.reRender() - } - }) + this.insertTo(node, exist, 'before') } // 将节点移动到另一个节点的后面 insertAfter(node, exist) { - if (node.isRoot) { - return - } - // 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新 - let nodeLayerChanged = - (node.layerIndex === 1 && exist.layerIndex !== 1) || - (node.layerIndex !== 1 && exist.layerIndex === 1) - // 移动节点 - let nodeParent = node.parent - let nodeBorthers = nodeParent.children - let nodeIndex = nodeBorthers.findIndex(item => { - return item === node - }) - if (nodeIndex === -1) { - return - } - nodeBorthers.splice(nodeIndex, 1) - nodeParent.nodeData.children.splice(nodeIndex, 1) + this.insertTo(node, exist, 'after') + } - // 目标节点 - let existParent = exist.parent - let existBorthers = existParent.children - let existIndex = existBorthers.findIndex(item => { - return item === exist + // 将节点移动到另一个节点的前面或后面 + insertTo(node, exist, dir = 'before') { + let nodeList = formatDataToArray(node) + nodeList = nodeList.filter(item => { + return !item.isRoot }) - if (existIndex === -1) { - return + if (dir === 'after') { + nodeList.reverse() } - existIndex++ - existBorthers.splice(existIndex, 0, node) - existParent.nodeData.children.splice(existIndex, 0, node.nodeData) - this.mindMap.render(() => { - if (nodeLayerChanged) { - node.reRender() + nodeList.forEach(item => { + this.checkNodeLayerChange(item, exist) + // 移动节点 + let nodeParent = item.parent + let nodeBorthers = nodeParent.children + let nodeIndex = nodeBorthers.findIndex(item2 => { + return item.uid === item2.uid + }) + if (nodeIndex === -1) { + return } + nodeBorthers.splice(nodeIndex, 1) + nodeParent.nodeData.children.splice(nodeIndex, 1) + + // 目标节点 + let existParent = exist.parent + let existBorthers = existParent.children + let existIndex = existBorthers.findIndex(item2 => { + return item2.uid === exist.uid + }) + if (existIndex === -1) { + return + } + if (dir === 'after') { + existIndex++ + } + existBorthers.splice(existIndex, 0, item) + existParent.nodeData.children.splice(existIndex, 0, item.nodeData) }) + this.mindMap.render() + } + + // 如果是富文本模式,那么某些层级变化需要更新样式 + checkNodeLayerChange(node, toNode) { + if (this.mindMap.richText) { + let nodeLayerChanged = + (node.layerIndex === 1 && toNode.layerIndex !== 1) || + (node.layerIndex !== 1 && toNode.layerIndex === 1) + if (nodeLayerChanged) { + node.nodeData.data.resetRichText = true + } + } } // 移除节点 removeNode(appointNodes = []) { - appointNodes = this.formatAppointNodes(appointNodes) + appointNodes = formatDataToArray(appointNodes) if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) { return } + // 删除节点后需要激活的节点 + let needActiveNode = null let isAppointNodes = appointNodes.length > 0 let list = isAppointNodes ? appointNodes : this.activeNodeList let root = list.find(node => { @@ -837,6 +933,28 @@ class Render { root.children = [] root.nodeData.children = [] } else { + // 如果只选中了一个节点,删除后激活其兄弟节点或者父节点 + if ( + this.activeNodeList.length === 1 && + !this.activeNodeList[0].isGeneralization && + this.mindMap.opt.deleteNodeActive + ) { + const node = this.activeNodeList[0] + const broList = node.parent.children + const nodeIndex = broList.findIndex(item => item.uid === node.uid) + // 如果后面有兄弟节点 + if (nodeIndex < broList.length - 1) { + needActiveNode = broList[nodeIndex + 1] + } else { + // 如果前面有兄弟节点 + if (nodeIndex > 0) { + needActiveNode = broList[nodeIndex - 1] + } else { + // 没有兄弟节点 + needActiveNode = node.parent + } + } + } for (let i = 0; i < list.length; i++) { let node = list[i] if (isAppointNodes) list.splice(i, 1) @@ -855,38 +973,53 @@ class Render { } } } + this.activeNodeList = [] + // 激活被删除节点的兄弟节点或父节点 + if (needActiveNode) { + this.activeNodeList.push(needActiveNode) + this.setNodeActive(needActiveNode, true) + needActiveNode = null + } this.mindMap.emit('node_active', null, [...this.activeNodeList]) this.mindMap.render() } // 移除某个指定节点 removeOneNode(node) { - let index = this.getNodeIndex(node) + let index = getNodeIndex(node) node.remove() node.parent.children.splice(index, 1) node.parent.nodeData.children.splice(index, 1) } - // 复制节点,多个节点只会操作第一个节点 + // 复制节点 copyNode() { if (this.activeNodeList.length <= 0) { return } - return copyNodeTree({}, this.activeNodeList[0], true) + const nodeList = getTopAncestorsFomNodeList(this.activeNodeList) + return nodeList.map(node => { + return copyNodeTree({}, node, true) + }) } - // 剪切节点,多个节点只会操作第一个节点 + // 剪切节点 cutNode(callback) { if (this.activeNodeList.length <= 0) { return } - let node = this.activeNodeList[0] - if (node.isRoot) { - return null - } - let copyData = copyNodeTree({}, node, true) - this.removeActiveNode(node) - this.removeOneNode(node) + const nodeList = getTopAncestorsFomNodeList(this.activeNodeList).filter( + node => { + return !node.isRoot + } + ) + const copyData = nodeList.map(node => { + return copyNodeTree({}, node, true) + }) + nodeList.forEach(node => { + this.removeActiveNode(node) + this.removeOneNode(node) + }) this.mindMap.emit('node_active', null, [...this.activeNodeList]) this.mindMap.render() if (callback && typeof callback === 'function') { @@ -894,16 +1027,19 @@ class Render { } } - // 移动一个节点作为另一个节点的子节点 + // 移动节点作为另一个节点的子节点 moveNodeTo(node, toNode) { - if (node.isRoot) { - return - } - // let copyData = copyNodeTree({}, node, false, true) - this.removeActiveNode(node) - this.removeOneNode(node) + let nodeList = formatDataToArray(node) + nodeList = nodeList.filter(item => { + return !item.isRoot + }) + nodeList.forEach(item => { + this.checkNodeLayerChange(item, toNode) + this.removeActiveNode(item) + this.removeOneNode(item) + toNode.nodeData.children.push(item.nodeData) + }) this.mindMap.emit('node_active', null, [...this.activeNodeList]) - toNode.nodeData.children.push(node.nodeData) this.mindMap.render() if (toNode.isRoot) { toNode.destroy() @@ -912,11 +1048,16 @@ class Render { // 粘贴节点到节点 pasteNode(data) { - if (this.activeNodeList.length <= 0 || !data) { + data = formatDataToArray(data) + if (this.activeNodeList.length <= 0 || data.length <= 0) { return } - this.activeNodeList.forEach(item => { - item.nodeData.children.push(simpleDeepClone(data)) + this.activeNodeList.forEach(node => { + node.nodeData.children.push( + ...data.map(item => { + return simpleDeepClone(item) + }) + ) }) this.mindMap.render() } @@ -1093,7 +1234,13 @@ class Render { // 设置节点图片 setNodeImage(node, data) { - const { url, title, width, height, custom = false } = data || { url: '', title: '', width: 0, height: 0, custom: false } + const { + url, + title, + width, + height, + custom = false + } = data || { url: '', title: '', width: 0, height: 0, custom: false } this.setNodeDataRender(node, { image: url, imageTitle: title || '', @@ -1134,6 +1281,17 @@ class Render { }) } + // 设置节点公式 + insertFormula(formula, appointNodes = []) { + // 只在富文本模式下可用,并且需要注册Formula插件 + if (!this.mindMap.richText || !this.mindMap.formula) return + appointNodes = formatDataToArray(appointNodes) + const list = appointNodes.length > 0 ? appointNodes : this.activeNodeList + list.forEach(node => { + this.mindMap.formula.insertFormulaToNode(node, formula) + }) + } + // 添加节点概要 addGeneralization(data) { if (this.activeNodeList.length <= 0) { diff --git a/simple-mind-map/src/core/render/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js index fe5faf95..19336af2 100644 --- a/simple-mind-map/src/core/render/TextEdit.js +++ b/simple-mind-map/src/core/render/TextEdit.js @@ -1,4 +1,10 @@ -import { getStrWithBrFromHtml, checkNodeOuter } from '../../utils' +import { + getStrWithBrFromHtml, + checkNodeOuter, + focusInput, + selectAllInput, + htmlEscape +} from '../../utils' import { ERROR_TYPES } from '../../constants/constant' // 节点文字编辑类 @@ -167,9 +173,11 @@ export default class TextEdit { let scale = this.mindMap.view.scale let lineHeight = node.style.merge('lineHeight') let fontSize = node.style.merge('fontSize') - let textLines = (this.cacheEditingText || node.nodeData.data.text).split( - /\n/gim - ) + let textLines = (this.cacheEditingText || node.nodeData.data.text) + .split(/\n/gim) + .map(item => { + return htmlEscape(item) + }) let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true' node.style.domText(this.textEditNode, scale, isMultiLine) this.textEditNode.style.zIndex = nodeTextEditZIndex @@ -188,35 +196,16 @@ export default class TextEdit { this.showTextEdit = true // 选中文本 // if (!this.cacheEditingText) { - // this.selectNodeText() + // selectAllInput(this.textEditNode) // } if (isInserting || (selectTextOnEnterEditText && !isFromKeyDown)) { - this.selectNodeText() + selectAllInput(this.textEditNode) } else { - this.focus() + focusInput(this.textEditNode) } this.cacheEditingText = '' } - // 聚焦 - focus() { - let selection = window.getSelection() - let range = document.createRange() - range.selectNodeContents(this.textEditNode) - range.collapse() - selection.removeAllRanges() - selection.addRange(range) - } - - // 选中文本 - selectNodeText() { - let selection = window.getSelection() - let range = document.createRange() - range.selectNodeContents(this.textEditNode) - selection.removeAllRanges() - selection.addRange(range) - } - // 获取当前正在编辑的内容 getEditText() { return getStrWithBrFromHtml(this.textEditNode.innerHTML) diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index 9bca88b9..d287755b 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -14,7 +14,7 @@ class Node { constructor(opt = {}) { // 节点数据 this.nodeData = this.handleData(opt.data || {}) - // id + // uid this.uid = opt.uid // 控制实例 this.mindMap = opt.mindMap @@ -394,7 +394,11 @@ class Node { this.active(e) }) this.group.on('mousedown', e => { - const { readonly, enableCtrlKeyNodeSelection, useLeftKeySelectionRightKeyDrag } = this.mindMap.opt + const { + readonly, + enableCtrlKeyNodeSelection, + useLeftKeySelectionRightKeyDrag + } = this.mindMap.opt // 只读模式不需要阻止冒泡 if (!readonly) { if (this.isRoot) { @@ -423,11 +427,9 @@ class Node { this.mindMap.renderer[isActive ? 'removeActiveNode' : 'addActiveNode']( this ) - this.mindMap.emit( - 'node_active', - isActive ? null : this, - [...this.mindMap.renderer.activeNodeList] - ) + this.mindMap.emit('node_active', isActive ? null : this, [ + ...this.mindMap.renderer.activeNodeList + ]) } this.mindMap.emit('node_mousedown', this, e) }) @@ -438,12 +440,14 @@ class Node { this.mindMap.emit('node_mouseup', this, e) }) this.group.on('mouseenter', e => { + if (this.isDrag) return this._isMouseenter = true // 显示展开收起按钮 this.showExpandBtn() this.mindMap.emit('node_mouseenter', this, e) }) this.group.on('mouseleave', e => { + if (!this._isMouseenter) return this._isMouseenter = false this.hideExpandBtn() this.mindMap.emit('node_mouseleave', this, e) @@ -466,7 +470,11 @@ class Node { e.stopPropagation() e.preventDefault() // 如果是多选节点结束,那么不要触发右键菜单事件 - if(!useLeftKeySelectionRightKeyDrag && this.mindMap.select.hasSelectRange()) { + if ( + this.mindMap.select && + !useLeftKeySelectionRightKeyDrag && + this.mindMap.select.hasSelectRange() + ) { return } if (this.nodeData.data.isActive) { @@ -695,6 +703,61 @@ class Node { } } + // 设置节点透明度 + // 包括连接线和下级节点 + setOpacity(val) { + // 自身及连线 + this.group.opacity(val) + this._lines.forEach(line => { + line.opacity(val) + }) + // 子节点 + this.children.forEach(item => { + item.setOpacity(val) + }) + // 概要节点 + if (this._generalizationNode) { + this._generalizationLine.opacity(val) + this._generalizationNode.group.opacity(val) + } + } + + // 隐藏子节点 + hideChildren() { + this._lines.forEach(item => { + item.hide() + }) + if (this.children && this.children.length) { + this.children.forEach(item => { + item.hide() + }) + } + } + + // 显示子节点 + showChildren() { + this._lines.forEach(item => { + item.show() + }) + if (this.children && this.children.length) { + this.children.forEach(item => { + item.show() + }) + } + } + + // 被拖拽中 + startDrag() { + this.isDrag = true + this.group.addClass('smm-node-dragging') + } + + // 拖拽结束 + endDrag() { + this.isDrag = false + this.group.removeClass('smm-node-dragging') + } + // 连线 renderLine(deep = false) { if (this.nodeData.data.expand === false) { @@ -794,12 +857,12 @@ class Node { // 检测当前节点是否是某个节点的祖先节点 isParent(node) { - if (this === node) { + if (this.uid === node.uid) { return false } let parent = node.parent while (parent) { - if (this === parent) { + if (this.uid === parent.uid) { return true } parent = parent.parent @@ -809,11 +872,11 @@ class Node { // 检测当前节点是否是某个节点的兄弟节点 isBrother(node) { - if (!this.parent || this === node) { + if (!this.parent || this.uid === node.uid) { return false } return this.parent.children.find(item => { - return item === node + return item.uid === node.uid }) } diff --git a/simple-mind-map/src/core/render/node/Style.js b/simple-mind-map/src/core/render/node/Style.js index 98341bee..b7cb95e4 100644 --- a/simple-mind-map/src/core/render/node/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -1,7 +1,8 @@ import { - tagColorList, - nodeDataNoStylePropList -} from '../../../constants/constant' + checkIsNodeStyleDataKey, + generateColorByContent +} from '../../../utils/index' + const rootProp = ['paddingX', 'paddingY'] const backgroundStyleProps = [ 'backgroundColor', @@ -164,10 +165,10 @@ class Style { } // 标签文字 - tagText(node, index) { + tagText(node) { node .fill({ - color: tagColorList[index].color + color: '#fff' }) .css({ 'font-size': '12px' @@ -175,9 +176,9 @@ class Style { } // 标签矩形 - tagRect(node, index) { + tagRect(node, text, color) { node.fill({ - color: tagColorList[index].background + color: color || generateColorByContent(text.node.textContent) }) } @@ -225,7 +226,7 @@ class Style { hasCustomStyle() { let res = false Object.keys(this.ctx.nodeData.data).forEach(item => { - if (!nodeDataNoStylePropList.includes(item)) { + if (checkIsNodeStyleDataKey(item)) { res = true } }) @@ -235,12 +236,9 @@ class Style { // hover和激活节点 hoverNode(node) { const { hoverRectColor } = this.ctx.mindMap.opt - node - .radius(5) - .fill('none') - .stroke({ - color: hoverRectColor - }) + node.radius(5).fill('none').stroke({ + color: hoverRectColor + }) } } diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index 9ff93cf5..e4487fa6 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -3,7 +3,8 @@ import { resizeImgSize, removeHtmlStyle, addHtmlStyle, - checkIsRichText + checkIsRichText, + isUndef } from '../../../utils' import { Image, SVG, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js' import iconsSvg from '../../../svg/icons' @@ -164,7 +165,10 @@ function createTextNode() { let lineHeight = this.getStyle('lineHeight', false) // 文本超长自动换行 let textStyle = this.style.getTextFontStyle() - let textArr = this.nodeData.data.text.split(/\n/gim) + let textArr = [] + if (!isUndef(this.nodeData.data.text)) { + textArr = String(this.nodeData.data.text).split(/\n/gim) + } let maxWidth = this.mindMap.opt.textAutoWrapWidth let isMultiLine = false textArr.forEach((item, index) => { @@ -249,12 +253,15 @@ function createTagNode() { tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => { let tag = new G() // 标签文本 - let text = new Text().text(item).x(8).cy(10) + let text = new Text().text(item).x(8).cy(8) this.style.tagText(text, index) let { width } = text.bbox() // 标签矩形 let rect = new Rect().size(width + 16, 20) - this.style.tagRect(rect, index) + // 先从自定义的颜色中获取颜色,没有的话就按照内容生成 + const tagsColorList = this.mindMap.opt.tagsColorMap || {} + const color = tagsColorList[text.node.textContent] + this.style.tagRect(rect, text, color) tag.add(rect).add(text) nodes.push({ node: tag, diff --git a/simple-mind-map/src/core/render/node/nodeExpandBtn.js b/simple-mind-map/src/core/render/node/nodeExpandBtn.js index 61936759..ee79db27 100644 --- a/simple-mind-map/src/core/render/node/nodeExpandBtn.js +++ b/simple-mind-map/src/core/render/node/nodeExpandBtn.js @@ -70,7 +70,8 @@ function updateExpandBtnNode() { if (this._expandBtn) { // 如果是收起按钮加上边框 - let { isShowExpandNum, expandBtnStyle, expandBtnNumHandler } = this.mindMap.opt + let { isShowExpandNum, expandBtnStyle, expandBtnNumHandler } = + this.mindMap.opt if (isShowExpandNum) { if (!expand) { // 数字按钮添加边框 @@ -135,6 +136,7 @@ function renderExpandBtn() { this._expandBtn.on('dblclick', e => { e.stopPropagation() }) + this._expandBtn.addClass('smm-expand-btn') this.group.add(this._expandBtn) } this._showExpandBtn = true diff --git a/simple-mind-map/src/core/render/node/nodeGeneralization.js b/simple-mind-map/src/core/render/node/nodeGeneralization.js index ec7f95aa..3459e0e2 100644 --- a/simple-mind-map/src/core/render/node/nodeGeneralization.js +++ b/simple-mind-map/src/core/render/node/nodeGeneralization.js @@ -2,12 +2,12 @@ import Node from './Node' import { createUid } from '../../../utils/index' // 检查是否存在概要 -function checkHasGeneralization () { +function checkHasGeneralization() { return !!this.nodeData.data.generalization } // 创建概要节点 -function createGeneralizationNode () { +function createGeneralizationNode() { if (this.isGeneralization || !this.checkHasGeneralization()) { return } @@ -35,14 +35,14 @@ function createGeneralizationNode () { } // 更新概要节点 -function updateGeneralization () { +function updateGeneralization() { if (this.isGeneralization) return this.removeGeneralization() this.createGeneralizationNode() } // 渲染概要节点 -function renderGeneralization () { +function renderGeneralization() { if (this.isGeneralization) return if (!this.checkHasGeneralization()) { this.removeGeneralization() @@ -65,7 +65,7 @@ function renderGeneralization () { } // 删除概要节点 -function removeGeneralization () { +function removeGeneralization() { if (this.isGeneralization) return if (this._generalizationLine) { this._generalizationLine.remove() @@ -86,7 +86,7 @@ function removeGeneralization () { } // 隐藏概要节点 -function hideGeneralization () { +function hideGeneralization() { if (this.isGeneralization) return if (this._generalizationLine) { this._generalizationLine.hide() @@ -97,7 +97,7 @@ function hideGeneralization () { } // 显示概要节点 -function showGeneralization () { +function showGeneralization() { if (this.isGeneralization) return if (this._generalizationLine) { this._generalizationLine.show() @@ -108,11 +108,11 @@ function showGeneralization () { } export default { - checkHasGeneralization, - createGeneralizationNode, - updateGeneralization, - renderGeneralization, - removeGeneralization, - hideGeneralization, - showGeneralization -} \ No newline at end of file + checkHasGeneralization, + createGeneralizationNode, + updateGeneralization, + renderGeneralization, + removeGeneralization, + hideGeneralization, + showGeneralization +} diff --git a/simple-mind-map/src/core/view/View.js b/simple-mind-map/src/core/view/View.js index 78e8a549..ac235615 100644 --- a/simple-mind-map/src/core/view/View.js +++ b/simple-mind-map/src/core/view/View.js @@ -79,7 +79,10 @@ class View { // 鼠标滚轮事件控制缩放 if (mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM) { if (disableMouseWheelZoom) return - const { x: clientX, y: clientY } = this.mindMap.toPos(e.clientX, e.clientY) + const { x: clientX, y: clientY } = this.mindMap.toPos( + e.clientX, + e.clientY + ) let cx = mouseScaleCenterUseMousePosition ? clientX : undefined let cy = mouseScaleCenterUseMousePosition ? clientY : undefined switch (dir) { @@ -171,7 +174,6 @@ class View { // 平移x方式到 translateXTo(x) { - if (x === 0) return this.x = x this.transform() } @@ -185,7 +187,6 @@ class View { // 平移y方向到 translateYTo(y) { - if (y === 0) return this.y = y this.transform() } diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index 92c4e0e4..6199059f 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -202,9 +202,13 @@ class Base { } // 递归计算节点的宽度 - getNodeAreaWidth(node) { + getNodeAreaWidth(node, withGeneralization = false) { let widthArr = [] + let totalGeneralizationNodeWidth = 0 let loop = (node, width) => { + if (withGeneralization && node.checkHasGeneralization()) { + totalGeneralizationNodeWidth += node._generalizationNodeWidth + } if (node.children.length) { width += node.width / 2 node.children.forEach(item => { @@ -216,7 +220,7 @@ class Base { } } loop(node, 0) - return Math.max(...widthArr) + return Math.max(...widthArr) + totalGeneralizationNodeWidth } // 二次贝塞尔曲线 diff --git a/simple-mind-map/src/layouts/CatalogOrganization.js b/simple-mind-map/src/layouts/CatalogOrganization.js index 3e780c0d..0162434c 100644 --- a/simple-mind-map/src/layouts/CatalogOrganization.js +++ b/simple-mind-map/src/layouts/CatalogOrganization.js @@ -87,11 +87,18 @@ class CatalogOrganization extends Base { totalLeft += cur.width + marginX }) } else { - let totalTop = node.top + node.height + marginY + (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + let totalTop = + node.top + + this.getNodeHeightWithGeneralization(node) + + marginY + + (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) node.children.forEach(cur => { cur.left = node.left + node.width * 0.5 cur.top = totalTop - totalTop += cur.height + marginY + (this.getNodeActChildrenLength(cur) > 0 ? cur.expandBtnSize : 0) + totalTop += + this.getNodeHeightWithGeneralization(cur) + + marginY + + (this.getNodeActChildrenLength(cur) > 0 ? cur.expandBtnSize : 0) }) } } @@ -112,7 +119,7 @@ class CatalogOrganization extends Base { } // 调整left if (parent && parent.isRoot) { - let areaWidth = this.getNodeAreaWidth(node) + let areaWidth = this.getNodeAreaWidth(node, true) let difference = areaWidth - node.width if (difference > 0) { this.updateBrothersLeft(node, difference) @@ -124,7 +131,13 @@ class CatalogOrganization extends Base { let marginY = this.getMarginY(layerIndex + 1) let totalHeight = node.children.reduce((h, item) => { - return h + item.height + (this.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + return ( + h + + this.getNodeHeightWithGeneralization(item) + + (this.getNodeActChildrenLength(item) > 0 + ? item.expandBtnSize + : 0) + ) }, 0) + len * marginY this.updateBrothersTop(node, totalHeight) @@ -134,7 +147,7 @@ class CatalogOrganization extends Base { if (isRoot) { let { right, left } = this.getNodeBoundaries(node, 'h') let childrenWidth = right - left - let offset = (node.left - left) - (childrenWidth - node.width) / 2 + let offset = node.left - left - (childrenWidth - node.width) / 2 this.updateChildren(node.children, 'left', offset) } }, @@ -147,7 +160,7 @@ class CatalogOrganization extends Base { if (node.parent) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition() || _index <= index) { @@ -170,7 +183,7 @@ class CatalogOrganization extends Base { if (node.parent && !node.parent.isRoot) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { diff --git a/simple-mind-map/src/layouts/Fishbone.js b/simple-mind-map/src/layouts/Fishbone.js index 0706caba..fff92a49 100644 --- a/simple-mind-map/src/layouts/Fishbone.js +++ b/simple-mind-map/src/layouts/Fishbone.js @@ -159,7 +159,8 @@ class Fishbone extends Base { let marginY = this.getMarginY(node.layerIndex) totalHeight += node.height + - (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY + (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + + marginY if (node.children.length) { node.children.forEach(item => { loop(item) @@ -193,7 +194,7 @@ class Fishbone extends Base { if (node.parent && !node.parent.isRoot) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { diff --git a/simple-mind-map/src/layouts/FishboneBottom.js b/simple-mind-map/src/layouts/FishboneBottom.js index ca66b6a6..c267d62e 100644 --- a/simple-mind-map/src/layouts/FishboneBottom.js +++ b/simple-mind-map/src/layouts/FishboneBottom.js @@ -170,7 +170,8 @@ class Fishbone extends Base { item.top += _top // 调整left let offsetLeft = - (totalHeight2 + nodeTotalHeight) / Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) + (totalHeight2 + nodeTotalHeight) / + Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) item.left += offsetLeft totalHeight += offset totalHeight2 += nodeTotalHeight @@ -237,7 +238,7 @@ class Fishbone extends Base { if (node.parent && !node.parent.isRoot) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { @@ -312,7 +313,9 @@ class Fishbone extends Base { if (node.parent && node.parent.isRoot) { line.plot( `M ${x},${top + height} L ${x + lineLength},${ - top + height + Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) * lineLength + top + + height + + Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) * lineLength }` ) } else { diff --git a/simple-mind-map/src/layouts/FishboneTop.js b/simple-mind-map/src/layouts/FishboneTop.js index 0f4fa57a..fb31e5e6 100644 --- a/simple-mind-map/src/layouts/FishboneTop.js +++ b/simple-mind-map/src/layouts/FishboneTop.js @@ -140,7 +140,8 @@ class Fishbone extends Base { node.top - (item.top - node.top) - nodeTotalHeight + node.height // 调整left let offsetLeft = - (nodeTotalHeight + totalHeight) / Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) + (nodeTotalHeight + totalHeight) / + Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) item.left += offsetLeft totalHeight += nodeTotalHeight // 同步更新后代节点 @@ -206,7 +207,7 @@ class Fishbone extends Base { if (node.parent && !node.parent.isRoot) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { @@ -285,14 +286,16 @@ class Fishbone extends Base { ) { line.plot( `M ${x},${top} L ${x + lineLength},${ - top - Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) * lineLength + top - + Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) * lineLength }` ) } else { if (node.parent && node.parent.isRoot) { line.plot( `M ${x},${top} L ${x + lineLength},${ - top - Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) * lineLength + top - + Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) * lineLength }` ) } else { diff --git a/simple-mind-map/src/layouts/LogicalStructure.js b/simple-mind-map/src/layouts/LogicalStructure.js index 01ac0325..88da77fe 100644 --- a/simple-mind-map/src/layouts/LogicalStructure.js +++ b/simple-mind-map/src/layouts/LogicalStructure.js @@ -56,6 +56,15 @@ class LogicalStructure extends Base { }, 0) + (len + 1) * this.getMarginY(layerIndex + 1) : 0 + // 如果存在概要,则和概要的高度取最大值 + let generalizationNodeHeight = cur._node.checkHasGeneralization() + ? cur._node._generalizationNodeHeight + + this.getMarginY(layerIndex + 1) + : 0 + cur._node.childrenAreaHeight2 = Math.max( + cur._node.childrenAreaHeight, + generalizationNodeHeight + ) }, true, 0 @@ -99,7 +108,7 @@ class LogicalStructure extends Base { } // 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置 let difference = - node.childrenAreaHeight - + node.childrenAreaHeight2 - this.getMarginY(layerIndex + 1) * 2 - node.height if (difference > 0) { @@ -116,10 +125,10 @@ class LogicalStructure extends Base { if (node.parent) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { - if (item === node || item.hasCustomPosition()) { + if (item.uid === node.uid || item.hasCustomPosition()) { // 适配自定义位置 return } diff --git a/simple-mind-map/src/layouts/MindMap.js b/simple-mind-map/src/layouts/MindMap.js index 6d9f7bf5..3e1b1fbd 100644 --- a/simple-mind-map/src/layouts/MindMap.js +++ b/simple-mind-map/src/layouts/MindMap.js @@ -90,6 +90,20 @@ class MindMap extends Base { cur._node.rightChildrenAreaHeight = rightChildrenAreaHeight + (rightLen + 1) * this.getMarginY(layerIndex + 1) + + // 如果存在概要,则和概要的高度取最大值 + let generalizationNodeHeight = cur._node.checkHasGeneralization() + ? cur._node._generalizationNodeHeight + + this.getMarginY(layerIndex + 1) + : 0 + cur._node.leftChildrenAreaHeight2 = Math.max( + cur._node.leftChildrenAreaHeight, + generalizationNodeHeight + ) + cur._node.rightChildrenAreaHeight2 = Math.max( + cur._node.rightChildrenAreaHeight, + generalizationNodeHeight + ) }, true, 0 @@ -139,8 +153,8 @@ class MindMap extends Base { } // 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置 let base = this.getMarginY(layerIndex + 1) * 2 + node.height - let leftDifference = node.leftChildrenAreaHeight - base - let rightDifference = node.rightChildrenAreaHeight - base + let leftDifference = node.leftChildrenAreaHeight2 - base + let rightDifference = node.rightChildrenAreaHeight2 - base if (leftDifference > 0 || rightDifference > 0) { this.updateBrothers(node, leftDifference / 2, rightDifference / 2) } @@ -158,7 +172,7 @@ class MindMap extends Base { return item.dir === node.dir }) let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { diff --git a/simple-mind-map/src/layouts/OrganizationStructure.js b/simple-mind-map/src/layouts/OrganizationStructure.js index 179ef4c2..ba41a454 100644 --- a/simple-mind-map/src/layouts/OrganizationStructure.js +++ b/simple-mind-map/src/layouts/OrganizationStructure.js @@ -57,6 +57,15 @@ class OrganizationStructure extends Base { }, 0) + (len + 1) * this.getMarginY(layerIndex + 1) : 0 + + // 如果存在概要,则和概要的高度取最大值 + let generalizationNodeWidth = cur._node.checkHasGeneralization() + ? cur._node._generalizationNodeWidth + this.getMarginY(layerIndex + 1) + : 0 + cur._node.childrenAreaWidth2 = Math.max( + cur._node.childrenAreaWidth, + generalizationNodeWidth + ) }, true, 0 @@ -100,7 +109,7 @@ class OrganizationStructure extends Base { } // 判断子节点所占的宽度之和是否大于该节点自身,大于则需要调整位置 let difference = - node.childrenAreaWidth - + node.childrenAreaWidth2 - this.getMarginY(layerIndex + 1) * 2 - node.width if (difference > 0) { @@ -117,7 +126,7 @@ class OrganizationStructure extends Base { if (node.parent) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { diff --git a/simple-mind-map/src/layouts/Timeline.js b/simple-mind-map/src/layouts/Timeline.js index a3f166fa..ee1c0f9b 100644 --- a/simple-mind-map/src/layouts/Timeline.js +++ b/simple-mind-map/src/layouts/Timeline.js @@ -209,7 +209,7 @@ class Timeline extends Base { if (node.parent && !node.parent.isRoot) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { diff --git a/simple-mind-map/src/layouts/VerticalTimeline.js b/simple-mind-map/src/layouts/VerticalTimeline.js index d7e24829..a8079944 100644 --- a/simple-mind-map/src/layouts/VerticalTimeline.js +++ b/simple-mind-map/src/layouts/VerticalTimeline.js @@ -156,13 +156,13 @@ class VerticalTimeline extends Base { if (node.parent) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { // 自定义节点位置 if (item.hasCustomPosition()) return // 三级或三级以下节点自身位置不需要动 - if (!node.parent.isRoot && item === node) return + if (!node.parent.isRoot && item.uid === node.uid) return let _offset = 0 // 二级节点上面的兄弟节点不需要移动,自身需要往下移动 if (node.parent.isRoot) { @@ -202,7 +202,7 @@ class VerticalTimeline extends Base { if (node.parent && !node.parent.isRoot) { let childrenList = node.parent.children let index = childrenList.findIndex(item => { - return item === node + return item.uid === node.uid }) childrenList.forEach((item, _index) => { if (item.hasCustomPosition()) { diff --git a/simple-mind-map/src/layouts/fishboneUtils.js b/simple-mind-map/src/layouts/fishboneUtils.js index e95e2ec2..f4fbf137 100644 --- a/simple-mind-map/src/layouts/fishboneUtils.js +++ b/simple-mind-map/src/layouts/fishboneUtils.js @@ -52,13 +52,15 @@ export default { let totalTop = node.top + node.height + - (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + + marginY node.children.forEach(item => { item.left = startLeft item.top += totalTop totalTop += item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + + marginY }) } }, @@ -72,7 +74,8 @@ export default { return ( h + item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + + marginY ) }, 0) ctx.updateBrothersTop(node, totalHeight) @@ -92,7 +95,11 @@ export default { item.top = node.top - (item.top - node.top) - nodeTotalHeight + node.height // 调整left - item.left = node.left + node.width * ctx.indent + (nodeTotalHeight + totalHeight) / Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) + item.left = + node.left + + node.width * ctx.indent + + (nodeTotalHeight + totalHeight) / + Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) totalHeight += nodeTotalHeight // 同步更新后代节点 ctx.updateChildrenPro(item.children, { @@ -129,7 +136,9 @@ export default { if (node.parent && node.parent.isRoot) { line.plot( `M ${x},${top + height} L ${x + lineLength},${ - top + height + Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) * lineLength + top + + height + + Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) * lineLength }` ) } else { @@ -144,7 +153,8 @@ export default { let totalTop = node.top + node.height + - (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + + marginY node.children.forEach(item => { item.left = startLeft @@ -153,7 +163,8 @@ export default { (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) totalTop += item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + + marginY }) } if (layerIndex > 1 && node.children) { @@ -161,13 +172,15 @@ export default { let startLeft = node.left + node.width * ctx.childIndent let totalTop = node.top - - (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) - marginY + (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) - + marginY node.children.forEach(item => { item.left = startLeft item.top = totalTop - item.height totalTop -= item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + + marginY }) } }, @@ -180,7 +193,8 @@ export default { return ( h + item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + + marginY ) }, 0) ctx.updateBrothersTop(node, -totalHeight) @@ -197,18 +211,21 @@ export default { // 调整top let hasChildren = ctx.getNodeActChildrenLength(item) > 0 let nodeTotalHeight = ctx.getNodeAreaHeight(item) - let offset = - hasChildren - ? nodeTotalHeight - - item.height - - (hasChildren ? item.expandBtnSize : 0) - : 0 - offset -= (hasChildren ? marginY : 0) + let offset = hasChildren + ? nodeTotalHeight - + item.height - + (hasChildren ? item.expandBtnSize : 0) + : 0 + offset -= hasChildren ? marginY : 0 let _top = totalHeight + offset let _left = item.left item.top += _top // 调整left - item.left = node.left + node.width * ctx.indent + (nodeTotalHeight + totalHeight2) / Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) + item.left = + node.left + + node.width * ctx.indent + + (nodeTotalHeight + totalHeight2) / + Math.tan(degToRad(ctx.mindMap.opt.fishboneDeg)) totalHeight += offset totalHeight2 += nodeTotalHeight // 同步更新后代节点 diff --git a/simple-mind-map/src/parse/toMarkdown.js b/simple-mind-map/src/parse/toMarkdown.js index 9164d7d7..13e48053 100644 --- a/simple-mind-map/src/parse/toMarkdown.js +++ b/simple-mind-map/src/parse/toMarkdown.js @@ -50,4 +50,4 @@ export const transformToMarkdown = root => { true ) return content -} \ No newline at end of file +} diff --git a/simple-mind-map/src/parse/xmind.js b/simple-mind-map/src/parse/xmind.js index cb8b72df..07cfd7c1 100644 --- a/simple-mind-map/src/parse/xmind.js +++ b/simple-mind-map/src/parse/xmind.js @@ -4,7 +4,8 @@ import { getTextFromHtml, imgToDataUrl, parseDataUrl, - getImageSize + getImageSize, + isUndef } from '../utils/index' // 解析.xmind文件 @@ -49,7 +50,7 @@ const transformXmind = async (content, files) => { let walk = async (node, newNode) => { newNode.data = { // 节点内容 - text: node.title + text: isUndef(node.title) ? '' : node.title } // 节点备注 if (node.notes) { @@ -146,9 +147,10 @@ const transformOldXmind = content => { let walk = (node, newNode) => { let nodeElements = node.elements let nodeTitle = getItemByName(nodeElements, 'title') + nodeTitle = nodeTitle && nodeTitle.elements && nodeTitle.elements[0].text newNode.data = { // 节点内容 - text: nodeTitle && nodeTitle.elements && nodeTitle.elements[0].text + text: isUndef(nodeTitle) ? '' : nodeTitle } try { // 节点备注 @@ -189,7 +191,7 @@ const transformOldXmind = content => { if (_children && _children.elements && _children.elements.length > 0) { _children.elements.forEach(item => { if (item.name === 'topics') { - (item.elements || []).forEach(item2 => { + ;(item.elements || []).forEach(item2 => { let newChild = {} newNode.children.push(newChild) walk(item2, newChild) @@ -215,6 +217,7 @@ const transformToXmind = async (data, name) => { let waitLoadImageList = [] let walk = async (node, newNode, isRoot) => { let newData = { + id: node.data.uid, structureClass: 'org.xmind.ui.logic.right', title: getTextFromHtml(node.data.text), // 节点文本 children: { @@ -242,20 +245,20 @@ const transformToXmind = async (data, name) => { } // 图片 if (node.data.image) { + // 处理异步逻辑 + let resolve = null + let promise = new Promise(_resolve => { + resolve = _resolve + }) + waitLoadImageList.push(promise) try { - // 处理异步逻辑 - let resolve = null - let promise = new Promise(_resolve => { - resolve = _resolve - }) - waitLoadImageList.push(promise) let imgName = '' let imgData = node.data.image - // 网络图片要先转换成data:url - if (/^https?:\/\//.test(node.data.image)) { + // base64之外的其他图片要先转换成data:url + if (!/^data:/.test(node.data.image)) { imgData = await imgToDataUrl(node.data.image) } - // 从data:url中解析出图片类型和base64 + // 从data:url中解析出图片类型和ase64 let dataUrlRes = parseDataUrl(imgData) imgName = 'image_' + imageList.length + '.' + dataUrlRes.type imageList.push({ diff --git a/simple-mind-map/src/plugins/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js index dbca5808..51fc51ee 100644 --- a/simple-mind-map/src/plugins/AssociativeLine.js +++ b/simple-mind-map/src/plugins/AssociativeLine.js @@ -105,7 +105,7 @@ class AssociativeLine { this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z') }) } - + // 判断关联线坐标是否变更,有变更则使用变化后的坐标,无则默认坐标 updateAllLinesPos(node, toNode, associativeLinePoint) { associativeLinePoint = associativeLinePoint || {} @@ -149,8 +149,8 @@ class AssociativeLine { ) { nodeToIds.set(cur, data.associativeLineTargets) } - if (data.id) { - idToNode.set(data.id, cur) + if (data.uid) { + idToNode.set(data.uid, cur) } }, () => {}, @@ -158,8 +158,8 @@ class AssociativeLine { 0 ) nodeToIds.forEach((ids, node) => { - ids.forEach((id, index) => { - let toNode = idToNode.get(id) + ids.forEach((uid, index) => { + let toNode = idToNode.get(uid) if (!node || !toNode) return const associativeLinePoint = (node.nodeData.data.associativeLinePoint || [])[index] @@ -234,6 +234,11 @@ class AssociativeLine { controlPoints }) }) + // 双击进入关联线文本编辑状态 + clickPath.dblclick(() => { + if (!this.activeLine) return + this.showEditTextBox(text) + }) // 渲染关联线文字 this.renderText(this.getText(node, toNode), path, text) this.lineList.push([path, clickPath, text, node, toNode]) @@ -252,28 +257,25 @@ class AssociativeLine { }) { let { associativeLineActiveColor } = this.mindMap.themeConfig // 如果当前存在激活节点,那么取消激活节点 - if (this.mindMap.renderer.activeNodeList.length > 0) { - this.clearActiveNodes() - } else { - // 否则清除当前的关联线的激活状态,如果有的话 - this.clearActiveLine() - // 保存当前激活的关联线信息 - this.activeLine = [path, clickPath, text, node, toNode] - // 让不可见的点击线显示 - clickPath.stroke({ color: associativeLineActiveColor }) - // 如果没有输入过关联线文字,那么显示默认文字 - if (!this.getText(node, toNode)) { - this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text) - } - // 渲染控制点和连线 - this.renderControls( - startPoint, - endPoint, - controlPoints[0], - controlPoints[1] - ) - this.mindMap.emit('associative_line_click', path, clickPath, node, toNode) + this.mindMap.execCommand('CLEAR_ACTIVE_NODE') + // 否则清除当前的关联线的激活状态,如果有的话 + this.clearActiveLine() + // 保存当前激活的关联线信息 + this.activeLine = [path, clickPath, text, node, toNode] + // 让不可见的点击线显示 + clickPath.stroke({ color: associativeLineActiveColor }) + // 如果没有输入过关联线文字,那么显示默认文字 + if (!this.getText(node, toNode)) { + this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text) } + // 渲染控制点和连线 + this.renderControls( + startPoint, + endPoint, + controlPoints[0], + controlPoints[1] + ) + this.mindMap.emit('associative_line_click', path, clickPath, node, toNode) } // 移除所有连接线 @@ -362,7 +364,7 @@ class AssociativeLine { if (node.nodeData.data.isActive) { this.mindMap.renderer.setNodeActive(node, false) } - if (node === this.creatingStartNode || this.overlapNode) { + if (node.uid === this.creatingStartNode.uid || this.overlapNode) { return } let { left, top, width, height } = node @@ -379,7 +381,7 @@ class AssociativeLine { // 完成创建连接线 completeCreateLine(node) { - if (this.creatingStartNode === node) return + if (this.creatingStartNode.uid === node.uid) return this.addLine(this.creatingStartNode, node) if (this.overlapNode && this.overlapNode.nodeData.data.isActive) { this.mindMap.renderer.setNodeActive(this.overlapNode, false) @@ -395,21 +397,21 @@ class AssociativeLine { addLine(fromNode, toNode) { if (!fromNode || !toNode) return // 目标节点如果没有id,则生成一个id - let id = toNode.nodeData.data.id - if (!id) { - id = uuid() + let uid = toNode.nodeData.data.uid + if (!uid) { + uid = uuid() this.mindMap.execCommand('SET_NODE_DATA', toNode, { - id + uid }) } // 将目标节点id保存起来 let list = fromNode.nodeData.data.associativeLineTargets || [] // 连线节点是否存在相同的id,存在则阻止添加关联线 - const sameLine = list.some(item => item === id) + const sameLine = list.some(item => item === uid) if (sameLine) { return } - list.push(id) + list.push(uid) // 保存控制点 let [startPoint, endPoint] = computeNodePoints(fromNode, toNode) let controlPoints = computeCubicBezierPathPoints( @@ -433,7 +435,7 @@ class AssociativeLine { ] let associativeLinePoint = fromNode.nodeData.data.associativeLinePoint || [] // 记录关联的起始|结束坐标 - associativeLinePoint[list.length - 1] = [{ startPoint, endPoint }] + associativeLinePoint[list.length - 1] = { startPoint, endPoint } this.mindMap.execCommand('SET_NODE_DATA', fromNode, { associativeLineTargets: list, associativeLineTargetControlOffsets: offsetList, @@ -458,7 +460,7 @@ class AssociativeLine { let newAssociativeLineText = {} if (associativeLineText) { Object.keys(associativeLineText).forEach(item => { - if (item !== toNode.nodeData.data.id) { + if (item !== toNode.nodeData.data.uid) { newAssociativeLineText[item] = associativeLineText[item] } }) @@ -483,13 +485,6 @@ class AssociativeLine { }) } - // 清除当前激活的节点 - clearActiveNodes() { - if (this.mindMap.renderer.activeNodeList.length > 0) { - this.mindMap.execCommand('CLEAR_ACTIVE_NODE') - } - } - // 清除激活的线 clearActiveLine() { if (this.activeLine) { diff --git a/simple-mind-map/src/plugins/Drag.js b/simple-mind-map/src/plugins/Drag.js index fd99c5bf..53d2a1a5 100644 --- a/simple-mind-map/src/plugins/Drag.js +++ b/simple-mind-map/src/plugins/Drag.js @@ -1,4 +1,4 @@ -import { bfsWalk, throttle } from '../utils' +import { bfsWalk, throttle, getTopAncestorsFomNodeList } from '../utils' import Base from '../layouts/Base' // 节点拖动插件 @@ -13,8 +13,14 @@ class Drag extends Base { // 复位 reset() { - // 当前拖拽节点 - this.node = null + // 是否正在跳转中 + this.isDragging = false + // 鼠标按下的节点 + this.mousedownNode = null + // 被拖拽中的节点列表 + this.beingDragNodeList = [] + // 当前画布节点列表 + this.nodeList = [] // 当前重叠节点 this.overlapNode = null // 当前上一个同级节点 @@ -25,16 +31,11 @@ class Drag extends Base { this.drawTransform = null // 克隆节点 this.clone = null - // 连接线 - this.line = null // 同级位置占位符 this.placeholder = null // 鼠标按下位置和节点左上角的偏移量 this.offsetX = 0 this.offsetY = 0 - // 克隆节点左上角的坐标 - this.cloneNodeLeft = 0 - this.cloneNodeTop = 0 // 当前鼠标是否按下 this.isMousedown = false // 拖拽的鼠标位置变量 @@ -51,45 +52,43 @@ class Drag extends Base { bindEvent() { this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this) this.mindMap.on('node_mousedown', (node, e) => { - if (this.mindMap.opt.readonly || node.isGeneralization) { - return - } - if (e.which !== 1 || node.isRoot) { + // 只读模式、不是鼠标左键按下、按下的是概要节点或根节点直接返回 + if ( + this.mindMap.opt.readonly || + e.which !== 1 || + node.isGeneralization || + node.isRoot + ) { return } e.preventDefault() - // 计算鼠标按下的位置距离节点左上角的距离 - this.drawTransform = this.mindMap.draw.transform() - let { scaleX, scaleY, translateX, translateY } = this.drawTransform - let { x, y } = this.mindMap.toPos(e.clientX, e.clientY) - this.offsetX = x - (node.left * scaleX + translateX) - this.offsetY = y - (node.top * scaleY + translateY) - this.node = node this.isMousedown = true + // 记录鼠标按下时的节点 + this.mousedownNode = node + // 记录鼠标按下的坐标 + const { x, y } = this.mindMap.toPos(e.clientX, e.clientY) this.mouseDownX = x this.mouseDownY = y }) this.mindMap.on('mousemove', e => { - if (this.mindMap.opt.readonly) { + if (this.mindMap.opt.readonly || !this.isMousedown) { return } - if (!this.isMousedown) { - return - } - this.mindMap.emit('node_dragging', this.node) e.preventDefault() - let { x, y } = this.mindMap.toPos(e.clientX, e.clientY) + const { x, y } = this.mindMap.toPos(e.clientX, e.clientY) this.mouseMoveX = x this.mouseMoveY = y + // 还没开始移动时鼠标位移过小不认为是拖拽 if ( + !this.isDragging && Math.abs(x - this.mouseDownX) <= this.checkDragOffset && - Math.abs(y - this.mouseDownY) <= this.checkDragOffset && - !this.node.isDrag + Math.abs(y - this.mouseDownY) <= this.checkDragOffset ) { return } - this.mindMap.renderer.clearAllActive() - this.onMove(x, y) + this.mindMap.emit('node_dragging') + this.handleStartMove() + this.onMove(x, y, e) }) this.onMouseup = this.onMouseup.bind(this) this.mindMap.on('node_mouseup', this.onMouseup) @@ -102,27 +101,48 @@ class Drag extends Base { return } this.isMousedown = false - let _nodeIsDrag = this.node.isDrag - this.node.isDrag = false - this.node.show() + // 恢复被拖拽节点的临时设置 + this.beingDragNodeList.forEach(node => { + node.setOpacity(1) + node.showChildren() + node.endDrag() + }) this.removeCloneNode() - let overlapNodeUid = this.overlapNode ? this.overlapNode.nodeData.data.uid : '' + let overlapNodeUid = this.overlapNode + ? this.overlapNode.nodeData.data.uid + : '' let prevNodeUid = this.prevNode ? this.prevNode.nodeData.data.uid : '' let nextNodeUid = this.nextNode ? this.nextNode.nodeData.data.uid : '' // 存在重叠子节点,则移动作为其子节点 if (this.overlapNode) { this.mindMap.renderer.setNodeActive(this.overlapNode, false) - this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode) + this.mindMap.execCommand( + 'MOVE_NODE_TO', + this.beingDragNodeList, + this.overlapNode + ) } else if (this.prevNode) { // 存在前一个相邻节点,作为其下一个兄弟节点 this.mindMap.renderer.setNodeActive(this.prevNode, false) - this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode) + this.mindMap.execCommand( + 'INSERT_AFTER', + this.beingDragNodeList, + this.prevNode + ) } else if (this.nextNode) { // 存在下一个相邻节点,作为其前一个兄弟节点 this.mindMap.renderer.setNodeActive(this.nextNode, false) - this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode) - } else if (_nodeIsDrag && this.mindMap.opt.enableFreeDrag) { - // 自定义位置 + this.mindMap.execCommand( + 'INSERT_BEFORE', + this.beingDragNodeList, + this.nextNode + ) + } else if ( + this.clone && + this.mindMap.opt.enableFreeDrag && + this.beingDragNodeList.length === 1 + ) { + // 如果只拖拽了一个节点,那么设置自定义位置 let { x, y } = this.mindMap.toPos( e.clientX - this.offsetX, e.clientY - this.offsetY @@ -130,11 +150,16 @@ class Drag extends Base { let { scaleX, scaleY, translateX, translateY } = this.drawTransform x = (x - translateX) / scaleX y = (y - translateY) / scaleY - this.node.left = x - this.node.top = y - this.node.customLeft = x - this.node.customTop = y - this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y) + this.mousedownNode.left = x + this.mousedownNode.top = y + this.mousedownNode.customLeft = x + this.mousedownNode.customTop = y + this.mindMap.execCommand( + 'SET_NODE_CUSTOM_POSITION', + this.mousedownNode, + x, + y + ) this.mindMap.render() } this.reset() @@ -145,24 +170,131 @@ class Drag extends Base { }) } + // 拖动中 + onMove(x, y, e) { + if (!this.isMousedown) { + return + } + // 更新克隆节点的位置 + let { scaleX, scaleY, translateX, translateY } = this.drawTransform + let cloneNodeLeft = x - this.offsetX + let cloneNodeTop = y - this.offsetY + x = (cloneNodeLeft - translateX) / scaleX + y = (cloneNodeTop - translateY) / scaleY + let t = this.clone.transform() + this.clone.translate(x - t.translateX, y - t.translateY) + // 检测新位置 + this.checkOverlapNode() + // 如果注册了多选节点插件,那么复用它的边缘自动移动画布功能 + if (this.mindMap.opt.autoMoveWhenMouseInEdgeOnDrag && this.mindMap.select) { + this.drawTransform = this.mindMap.draw.transform() + this.mindMap.select.clearAutoMoveTimer() + this.mindMap.select.onMove(e.clientX, e.clientY) + } + } + + // 开始拖拽时初始化一些数据 + handleStartMove() { + if (!this.isDragging) { + this.isDragging = true + // 鼠标按下的节点 + let node = this.mousedownNode + // 计算鼠标按下的位置距离节点左上角的距离 + this.drawTransform = this.mindMap.draw.transform() + let { scaleX, scaleY, translateX, translateY } = this.drawTransform + this.offsetX = this.mouseDownX - (node.left * scaleX + translateX) + this.offsetY = this.mouseDownY - (node.top * scaleY + translateY) + // 如果鼠标按下的节点是激活节点,那么保存当前所有激活的节点 + if (node.nodeData.data.isActive) { + // 找出这些激活节点中的最顶层节点 + this.beingDragNodeList = getTopAncestorsFomNodeList( + // 过滤掉根节点和概要节点 + this.mindMap.renderer.activeNodeList.filter(item => { + return !item.isRoot && !item.isGeneralization + }) + ) + } else { + // 否则只拖拽按下的节点 + this.beingDragNodeList = [node] + } + // 将节点树转为节点数组 + this.nodeTreeToList() + // 创建克隆节点 + this.createCloneNode() + // 清除当前所有激活的节点 + this.mindMap.renderer.clearAllActive() + } + } + + // 节点由树转换成数组,从子节点到根节点 + nodeTreeToList() { + const list = [] + bfsWalk(this.mindMap.renderer.root, node => { + // 过滤掉当前被拖拽的节点 + if (this.checkIsInBeingDragNodeList(node)) { + return + } + if (!list[node.layerIndex]) { + list[node.layerIndex] = [] + } + list[node.layerIndex].push(node) + }) + this.nodeList = list.reduceRight((res, cur) => { + return [...res, ...cur] + }, []) + } + // 创建克隆节点 createCloneNode() { if (!this.clone) { - // 节点 - this.clone = this.node.group.clone() - this.clone.opacity(0.5) + const { + dragMultiNodeRectConfig, + dragPlaceholderRectFill, + dragOpacityConfig + } = this.mindMap.opt + const { + width: rectWidth, + height: rectHeight, + fill: rectFill + } = dragMultiNodeRectConfig + const node = this.beingDragNodeList[0] + const lineColor = node.style.merge('lineColor', true) + // 如果当前被拖拽的节点数量大于1,那么创建一个矩形示意 + if (this.beingDragNodeList.length > 1) { + this.clone = this.draw + .rect() + .size(rectWidth, rectHeight) + .radius(rectHeight / 2) + .fill({ + color: rectFill || lineColor + }) + this.offsetX = rectWidth / 2 + this.offsetY = rectHeight / 2 + } else { + // 否则克隆当前的节点 + this.clone = node.group.clone() + // 删除展开收起按钮元素 + const expandEl = this.clone.findOne('.smm-expand-btn') + if (expandEl) { + expandEl.remove() + } + this.mindMap.draw.add(this.clone) + } + this.clone.opacity(dragOpacityConfig.cloneNodeOpacity) this.clone.css('z-index', 99999) - this.node.isDrag = true - this.node.hide() - // 连接线 - this.line = this.draw.path() - this.line.opacity(0.5) - this.node.styleLine(this.line, this.node) - // 同级位置占位符 + // 同级位置提示元素 this.placeholder = this.draw.rect().fill({ - color: this.node.style.merge('lineColor', true) + color: dragPlaceholderRectFill || lineColor + }) + // 当前被拖拽的节点的临时设置 + this.beingDragNodeList.forEach(node => { + // 降低透明度 + node.setOpacity(dragOpacityConfig.beingDragNodeOpacity) + // 隐藏连线及下级节点 + node.hideChildren() + // 设置拖拽状态 + node.startDrag() }) - this.mindMap.draw.add(this.clone) } } @@ -172,125 +304,52 @@ class Drag extends Base { return } this.clone.remove() - this.line.remove() this.placeholder.remove() } - // 拖动中 - onMove(x, y) { - if (!this.isMousedown) { - return - } - this.createCloneNode() - let { scaleX, scaleY, translateX, translateY } = this.drawTransform - this.cloneNodeLeft = x - this.offsetX - this.cloneNodeTop = y - this.offsetY - x = (this.cloneNodeLeft - translateX) / scaleX - y = (this.cloneNodeTop - translateY) / scaleY - let t = this.clone.transform() - this.clone.translate(x - t.translateX, y - t.translateY) - // 连接线 - let parent = this.node.parent - this.line.plot( - this.quadraticCurvePath( - parent.left + parent.width / 2, - parent.top + parent.height / 2, - x + this.node.width / 2, - y + this.node.height / 2 - ) - ) - this.checkOverlapNode() - } - // 检测重叠节点 checkOverlapNode() { if (!this.drawTransform || !this.placeholder) { return } - const { nodeDragPlaceholderMaxSize } = this.mindMap.opt - let x = this.mouseMoveX - let y = this.mouseMoveY this.overlapNode = null this.prevNode = null this.nextNode = null this.placeholder.size(0, 0) - bfsWalk(this.mindMap.renderer.root, node => { + this.nodeList.forEach(node => { if (node.nodeData.data.isActive) { this.mindMap.renderer.setNodeActive(node, false) } - if (node === this.node || this.node.isParent(node)) { - return - } if (this.overlapNode || (this.prevNode && this.nextNode)) { return } - let nodeRect = this.getNodeRect(node) - let oneFourthHeight = nodeRect.height / 4 - // 前一个和后一个节点 - let checkList = node.parent ? node.parent.children.filter((item) => { - return item !== this.node - }) : [] - let index = checkList.findIndex((item) => { - return item === node - }) - let prevBrother = null - let nextBrother = null - if (index !== -1) { - if (index - 1 >= 0) { - prevBrother = checkList[index - 1] - } - if (index + 1 <= checkList.length - 1) { - nextBrother = checkList[index + 1] - } - } - // 和前一个兄弟节点的距离 - let prevBrotherOffset = 0 - if (prevBrother) { - let prevNodeRect = this.getNodeRect(prevBrother) - prevBrotherOffset = nodeRect.top - prevNodeRect.bottom - // 间距小于10就当它不存在 - prevBrotherOffset = prevBrotherOffset >= this.minOffset ? prevBrotherOffset / 2 : 0 - } else { - // 没有前一个兄弟节点,那么假设和前一个节点的距离为20 - prevBrotherOffset = this.minOffset - } - // 和后一个兄弟节点的距离 - let nextBrotherOffset = 0 - if (nextBrother) { - let nextNodeRect = this.getNodeRect(nextBrother) - nextBrotherOffset = nextNodeRect.top - nodeRect.bottom - nextBrotherOffset = nextBrotherOffset >= this.minOffset ? nextBrotherOffset / 2 : 0 - } else { - nextBrotherOffset = this.minOffset - } - if (nodeRect.left <= x && nodeRect.right >= x) { - // 检测兄弟节点位置 - if (!this.overlapNode && !this.prevNode && !this.nextNode && !node.isRoot) { - let checkIsPrevNode = nextBrotherOffset > 0 ? // 距离下一个兄弟节点的距离大于0 - y > nodeRect.bottom && y <= (nodeRect.bottom + nextBrotherOffset) : // 那么在当前节点外底部判断 - y >= nodeRect.bottom - oneFourthHeight && y <= nodeRect.bottom // 否则在当前节点内底部1/4区间判断 - let checkIsNextNode = prevBrotherOffset > 0 ? // 距离上一个兄弟节点的距离大于0 - y < nodeRect.top && y >= (nodeRect.top - prevBrotherOffset) : // 那么在当前节点外底部判断 - y >= nodeRect.top && y <= nodeRect.top + oneFourthHeight - if (checkIsPrevNode) { - this.prevNode = node - let size = nextBrotherOffset > 0 ? Math.min(nextBrotherOffset, nodeDragPlaceholderMaxSize) : 5 - this.placeholder.size(node.width, size).move(nodeRect.originLeft, nodeRect.originBottom) - } else if (checkIsNextNode) { - this.nextNode = node - let size = prevBrotherOffset > 0 ? Math.min(prevBrotherOffset, nodeDragPlaceholderMaxSize) : 5 - this.placeholder.size(node.width, size).move(nodeRect.originLeft, nodeRect.originTop - size) - } - } - // 检测是否重叠 - if (!this.overlapNode && !this.prevNode && !this.nextNode) { - if ( - nodeRect.top + (prevBrotherOffset > 0 ? 0 : oneFourthHeight) <= y && - nodeRect.bottom - (nextBrotherOffset > 0 ? 0 : oneFourthHeight) >= y - ) { - this.overlapNode = node - } - } + switch (this.mindMap.opt.layout) { + case 'logicalStructure': + this.handleLogicalStructure(node) + break + case 'mindMap': + this.handleMindMap(node) + break + case 'organizationStructure': + this.handleOrganizationStructure(node) + break + case 'catalogOrganization': + this.handleCatalogOrganization(node) + break + case 'timeline': + this.handleTimeLine(node) + break + case 'timeline2': + this.handleTimeLine2(node) + break + case 'verticalTimeline': + this.handleLogicalStructure(node) + break + case 'fishbone': + this.handleFishbone(node) + break + default: + this.handleLogicalStructure(node) } }) if (this.overlapNode) { @@ -298,6 +357,299 @@ class Drag extends Base { } } + // 垂直方向比较 + // isReverse:是否反向 + handleVerticalCheck(node, checkList, isReverse = false) { + let x = this.mouseMoveX + let y = this.mouseMoveY + let nodeRect = this.getNodeRect(node) + if (isReverse) { + checkList = checkList.reverse() + } + let oneFourthHeight = nodeRect.height / 4 + let { prevBrotherOffset, nextBrotherOffset } = + this.getNodeDistanceToSiblingNode(checkList, node, nodeRect, 'v') + if (nodeRect.left <= x && nodeRect.right >= x) { + // 检测兄弟节点位置 + if ( + !this.overlapNode && + !this.prevNode && + !this.nextNode && + !node.isRoot + ) { + let checkIsPrevNode = + nextBrotherOffset > 0 // 距离下一个兄弟节点的距离大于0 + ? y > nodeRect.bottom && y <= nodeRect.bottom + nextBrotherOffset // 那么在当前节点外底部判断 + : y >= nodeRect.bottom - oneFourthHeight && y <= nodeRect.bottom // 否则在当前节点内底部1/4区间判断 + let checkIsNextNode = + prevBrotherOffset > 0 // 距离上一个兄弟节点的距离大于0 + ? y < nodeRect.top && y >= nodeRect.top - prevBrotherOffset // 那么在当前节点外底部判断 + : y >= nodeRect.top && y <= nodeRect.top + oneFourthHeight + if (checkIsPrevNode) { + if (isReverse) { + this.nextNode = node + } else { + this.prevNode = node + } + let size = this.formatPlaceholderSize(nextBrotherOffset) + this.setPlaceholderRect( + node.width, + size, + nodeRect.originLeft, + nodeRect.originBottom + ) + } else if (checkIsNextNode) { + if (isReverse) { + this.prevNode = node + } else { + this.nextNode = node + } + let size = this.formatPlaceholderSize(prevBrotherOffset) + this.setPlaceholderRect( + node.width, + size, + nodeRect.originLeft, + nodeRect.originTop - size + ) + } + } + // 检测是否重叠 + this.checkIsOverlap({ + node, + dir: 'v', + prevBrotherOffset, + nextBrotherOffset, + size: oneFourthHeight, + pos: y, + nodeRect + }) + } + } + + // 水平方向比较 + handleHorizontalCheck(node, checkList) { + let x = this.mouseMoveX + let y = this.mouseMoveY + let nodeRect = this.getNodeRect(node) + let oneFourthWidth = nodeRect.width / 4 + let { prevBrotherOffset, nextBrotherOffset } = + this.getNodeDistanceToSiblingNode(checkList, node, nodeRect, 'h') + if (nodeRect.top <= y && nodeRect.bottom >= y) { + // 检测兄弟节点位置 + if ( + !this.overlapNode && + !this.prevNode && + !this.nextNode && + !node.isRoot + ) { + let checkIsPrevNode = + nextBrotherOffset > 0 // 距离下一个兄弟节点的距离大于0 + ? x < nodeRect.right + nextBrotherOffset && x >= nodeRect.right // 那么在当前节点外底部判断 + : x <= nodeRect.right && x >= nodeRect.right - oneFourthWidth // 否则在当前节点内底部1/4区间判断 + let checkIsNextNode = + prevBrotherOffset > 0 // 距离上一个兄弟节点的距离大于0 + ? x > nodeRect.left - prevBrotherOffset && x <= nodeRect.left // 那么在当前节点外底部判断 + : x <= nodeRect.left + oneFourthWidth && x >= nodeRect.left + if (checkIsPrevNode) { + this.prevNode = node + let size = this.formatPlaceholderSize(nextBrotherOffset) + this.setPlaceholderRect( + size, + node.height, + nodeRect.originRight, + nodeRect.originTop + ) + } else if (checkIsNextNode) { + this.nextNode = node + let size = this.formatPlaceholderSize(prevBrotherOffset) + this.setPlaceholderRect( + size, + node.height, + nodeRect.originLeft - size, + nodeRect.originTop + ) + } + } + // 检测是否重叠 + this.checkIsOverlap({ + node, + dir: 'h', + prevBrotherOffset, + nextBrotherOffset, + size: oneFourthWidth, + pos: x, + nodeRect + }) + } + } + + // 获取节点距前一个和后一个节点的距离 + getNodeDistanceToSiblingNode(checkList, node, nodeRect, dir) { + let dir1 = dir === 'v' ? 'top' : 'left' + let dir2 = dir === 'v' ? 'bottom' : 'right' + let index = checkList.findIndex(item => { + return item.uid === node.uid + }) + let prevBrother = null + let nextBrother = null + if (index !== -1) { + if (index - 1 >= 0) { + prevBrother = checkList[index - 1] + } + if (index + 1 <= checkList.length - 1) { + nextBrother = checkList[index + 1] + } + } + // 和前一个兄弟节点的距离 + let prevBrotherOffset = 0 + if (prevBrother) { + let prevNodeRect = this.getNodeRect(prevBrother) + prevBrotherOffset = nodeRect[dir1] - prevNodeRect[dir2] + // 间距小于10就当它不存在 + prevBrotherOffset = + prevBrotherOffset >= this.minOffset ? prevBrotherOffset / 2 : 0 + } else { + // 没有前一个兄弟节点,那么假设和前一个节点的距离为20 + prevBrotherOffset = this.minOffset + } + // 和后一个兄弟节点的距离 + let nextBrotherOffset = 0 + if (nextBrother) { + let nextNodeRect = this.getNodeRect(nextBrother) + nextBrotherOffset = nextNodeRect[dir1] - nodeRect[dir2] + nextBrotherOffset = + nextBrotherOffset >= this.minOffset ? nextBrotherOffset / 2 : 0 + } else { + nextBrotherOffset = this.minOffset + } + return { + prevBrotherOffset, + nextBrotherOffset + } + } + + // 处理提示元素的大小 + formatPlaceholderSize(size) { + const { nodeDragPlaceholderMaxSize } = this.mindMap.opt + return size > 0 ? Math.min(size, nodeDragPlaceholderMaxSize) : 5 + } + + // 设置提示元素的大小和位置 + setPlaceholderRect(w, h, x, y) { + this.placeholder.size(w, h).move(x, y) + } + + // 检测是否重叠 + checkIsOverlap({ + node, + dir, + prevBrotherOffset, + nextBrotherOffset, + size, + pos, + nodeRect + }) { + let dir1 = dir === 'v' ? 'top' : 'left' + let dir2 = dir === 'v' ? 'bottom' : 'right' + if (!this.overlapNode && !this.prevNode && !this.nextNode) { + if ( + nodeRect[dir1] + (prevBrotherOffset > 0 ? 0 : size) <= pos && + nodeRect[dir2] - (nextBrotherOffset > 0 ? 0 : size) >= pos + ) { + this.overlapNode = node + } + } + } + + // 处理逻辑结构图 + handleLogicalStructure(node) { + const checkList = this.commonGetNodeCheckList(node) + this.handleVerticalCheck(node, checkList) + } + + // 处理思维导图 + handleMindMap(node) { + const checkList = node.parent + ? node.parent.children.filter(item => { + let sameDir = true + if (node.layerIndex === 1) { + sameDir = item.dir === node.dir + } + return sameDir && !this.checkIsInBeingDragNodeList(item) + }) + : [] + this.handleVerticalCheck(node, checkList) + } + + // 处理组织结构图 + handleOrganizationStructure(node) { + const checkList = this.commonGetNodeCheckList(node) + this.handleHorizontalCheck(node, checkList) + } + + // 处理目录组织图 + handleCatalogOrganization(node) { + const checkList = this.commonGetNodeCheckList(node) + if (node.layerIndex === 1) { + this.handleHorizontalCheck(node, checkList) + } else { + this.handleVerticalCheck(node, checkList) + } + } + + // 处理时间轴 + handleTimeLine(node) { + let checkList = this.commonGetNodeCheckList(node) + if (node.layerIndex === 1) { + this.handleHorizontalCheck(node, checkList) + } else { + this.handleVerticalCheck(node, checkList) + } + } + + // 处理时间轴2 + handleTimeLine2(node) { + let checkList = this.commonGetNodeCheckList(node) + if (node.layerIndex === 1) { + this.handleHorizontalCheck(node, checkList) + } else { + // 处于上方的三级节点需要特殊处理,因为节点排列方向反向了 + if (node.dir === 'top' && node.layerIndex === 2) { + this.handleVerticalCheck(node, checkList, true) + } else { + this.handleVerticalCheck(node, checkList) + } + } + } + + // 处理鱼骨图 + handleFishbone(node) { + let checkList = node.parent + ? node.parent.children.filter(item => { + return item.layerIndex > 1 && !this.checkIsInBeingDragNodeList(item) + }) + : [] + if (node.layerIndex === 1) { + this.handleHorizontalCheck(node, checkList) + } else { + // 处于上方的三级节点需要特殊处理,因为节点排列方向反向了 + if (node.dir === 'top' && node.layerIndex === 2) { + this.handleVerticalCheck(node, checkList, true) + } else { + this.handleVerticalCheck(node, checkList) + } + } + } + + // 获取节点的兄弟节点列表通用方法 + commonGetNodeCheckList(node) { + return node.parent + ? [...node.parent.children].filter(item => { + return !this.checkIsInBeingDragNodeList(item) + }) + : [] + } + // 计算节点的位置尺寸信息 getNodeRect(node) { let { scaleX, scaleY, translateX, translateY } = this.drawTransform @@ -305,6 +657,7 @@ class Drag extends Base { let originLeft = left let originTop = top let originBottom = top + height + let originRight = left + width let right = (left + width) * scaleX + translateX let bottom = (top + height) * scaleY + translateY left = left * scaleX + translateX @@ -318,9 +671,17 @@ class Drag extends Base { bottom, originLeft, originTop, - originBottom + originBottom, + originRight } } + + // 检查某个节点是否在被拖拽节点内 + checkIsInBeingDragNodeList(node) { + return !!this.beingDragNodeList.find(item => { + return item.uid === node.uid || item.isParent(node) + }) + } } Drag.instanceName = 'drag' diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js index aa447540..47bd579e 100644 --- a/simple-mind-map/src/plugins/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -41,7 +41,7 @@ class Export { let task = imageList.map(async item => { let imgUlr = item.attr('href') || item.attr('xlink:href') // 已经是data:URL形式不用转换 - if (/^data:/.test(imgUlr)) { + if (/^data:/.test(imgUlr) || imgUlr === 'none') { return } let imgData = await imgToDataUrl(imgUlr) @@ -58,7 +58,13 @@ class Export { } // svg转png - svgToPng(svgSrc, transparent, checkRotate = () => { return false }) { + svgToPng( + svgSrc, + transparent, + checkRotate = () => { + return false + } + ) { return new Promise((resolve, reject) => { const img = new Image() // 跨域图片需要添加这个属性,否则画布被污染了无法导出图片 @@ -66,7 +72,10 @@ class Export { img.onload = async () => { try { const canvas = document.createElement('canvas') - const dpr = Math.max(window.devicePixelRatio, this.mindMap.opt.minExportImgCanvasScale) + const dpr = Math.max( + window.devicePixelRatio, + this.mindMap.opt.minExportImgCanvasScale + ) const imgWidth = img.width const imgHeight = img.height // 如果宽比高长,那么旋转90度 @@ -185,7 +194,9 @@ class Export { // 覆盖html默认的样式 let foreignObjectList = node.find('foreignObject') if (foreignObjectList.length > 0) { - foreignObjectList[0].add(SVG(``)) + foreignObjectList[0].add( + SVG(``) + ) } str = node.svg() // 使用其他库(html2canvas、dom-to-image-more等)来完成导出 @@ -204,11 +215,7 @@ class Export { // 转换成data:url数据 let svgUrl = await readBlob(blob) // 绘制到canvas上 - let res = await this.svgToPng( - svgUrl, - transparent, - checkRotate - ) + let res = await this.svgToPng(svgUrl, transparent, checkRotate) return res } @@ -219,7 +226,7 @@ class Export { } let img = await this.png('', false, (width, height) => { if (width <= a4Size.width && height && a4Size.height) return false - return (width / height) > 1 + return width / height > 1 }) this.mindMap.doExportPDF.pdf(name, img, useMultiPageExport) } @@ -243,7 +250,9 @@ class Export { if (this.mindMap.richText) { let foreignObjectList = node.find('foreignObject') if (foreignObjectList.length > 0) { - foreignObjectList[0].add(SVG(``)) + foreignObjectList[0].add( + SVG(``) + ) } } node.first().before(SVG(`${name}`)) diff --git a/simple-mind-map/src/plugins/ExportPDF.js b/simple-mind-map/src/plugins/ExportPDF.js index 83f7ee88..5bffffc1 100644 --- a/simple-mind-map/src/plugins/ExportPDF.js +++ b/simple-mind-map/src/plugins/ExportPDF.js @@ -40,7 +40,14 @@ class ExportPDF { w = a4Size.width h = a4Size.width / imageRatio } - pdf.addImage(img, 'PNG', (a4Size.width - w) / 2, (a4Size.height - h) / 2, w, h) + pdf.addImage( + img, + 'PNG', + (a4Size.width - w) / 2, + (a4Size.height - h) / 2, + w, + h + ) pdf.save(name) } image.src = img diff --git a/simple-mind-map/src/plugins/Formula.js b/simple-mind-map/src/plugins/Formula.js new file mode 100644 index 00000000..52a174f5 --- /dev/null +++ b/simple-mind-map/src/plugins/Formula.js @@ -0,0 +1,53 @@ +import katex from 'katex' +import Quill from 'quill' + +// 数学公式支持插件 +// 该插件在富文本模式下可用 +class Formula { + // 构造函数 + constructor(opt) { + this.opt = opt + this.mindMap = opt.mindMap + window.katex = katex + this.extendQuill() + } + + // 修改formula格式工具 + extendQuill() { + const QuillFormula = Quill.import('formats/formula') + + class CustomFormulaBlot extends QuillFormula { + static create(value) { + let node = super.create(value) + if (typeof value === 'string') { + katex.render(value, node, { + throwOnError: false, + errorColor: '#f00', + output: 'mathml' // 增加该配置,默认只输出公式 + }) + node.setAttribute('data-value', value) + } + return node + } + } + + Quill.register('formats/formula', CustomFormulaBlot, true) + } + + // 给指定的节点插入指定公式 + insertFormulaToNode(node, formula) { + let richTextPlugin = this.mindMap.richText + richTextPlugin.showEditText(node) + richTextPlugin.quill.insertEmbed( + richTextPlugin.quill.getLength() - 1, + 'formula', + formula + ) + richTextPlugin.setTextStyleIfNotRichText(richTextPlugin.node) + richTextPlugin.hideEditText([node]) + } +} + +Formula.instanceName = 'formula' + +export default Formula diff --git a/simple-mind-map/src/plugins/KeyboardNavigation.js b/simple-mind-map/src/plugins/KeyboardNavigation.js index 5ed4b593..f3eb694e 100644 --- a/simple-mind-map/src/plugins/KeyboardNavigation.js +++ b/simple-mind-map/src/plugins/KeyboardNavigation.js @@ -94,7 +94,7 @@ class KeyboardNavigation { // 遍历节点树 bfsWalk(this.mindMap.renderer.root, node => { // 跳过当前聚焦的节点 - if (node === currentActiveNode) return + if (node.uid === currentActiveNode.uid) return // 当前遍历到的节点的位置信息 let rect = this.getNodeRect(node) let { left, top, right, bottom } = rect @@ -131,7 +131,7 @@ class KeyboardNavigation { checkNodeDis }) { bfsWalk(this.mindMap.renderer.root, node => { - if (node === currentActiveNode) return + if (node.uid === currentActiveNode.uid) return let rect = this.getNodeRect(node) let { left, top, right, bottom } = rect let match = false @@ -173,7 +173,7 @@ class KeyboardNavigation { let cX = (currentActiveNodeRect.right + currentActiveNodeRect.left) / 2 let cY = (currentActiveNodeRect.bottom + currentActiveNodeRect.top) / 2 bfsWalk(this.mindMap.renderer.root, node => { - if (node === currentActiveNode) return + if (node.uid === currentActiveNode.uid) return let rect = this.getNodeRect(node) let { left, top, right, bottom } = rect // 遍历到的节点的中心点 @@ -232,4 +232,4 @@ class KeyboardNavigation { KeyboardNavigation.instanceName = 'keyboardNavigation' -export default KeyboardNavigation \ No newline at end of file +export default KeyboardNavigation diff --git a/simple-mind-map/src/plugins/MiniMap.js b/simple-mind-map/src/plugins/MiniMap.js index 0127c2dd..fe745700 100644 --- a/simple-mind-map/src/plugins/MiniMap.js +++ b/simple-mind-map/src/plugins/MiniMap.js @@ -1,4 +1,8 @@ -import { isWhite, isTransparent, getVisibleColorFromTheme } from '../utils/index' +import { + isWhite, + isTransparent, + getVisibleColorFromTheme +} from '../utils/index' // 小地图插件 class MiniMap { @@ -78,7 +82,7 @@ class MiniMap { viewBoxStyle.left = miniMapBoxLeft + actWidth } - Object.keys(viewBoxStyle).forEach((key) => { + Object.keys(viewBoxStyle).forEach(key => { viewBoxStyle[key] = viewBoxStyle[key] + 'px' }) @@ -106,7 +110,7 @@ class MiniMap { } let children = svg.children() if (children && children.length > 0) { - children.forEach((node) => { + children.forEach(node => { this.removeNodeContent(node) }) } diff --git a/simple-mind-map/src/plugins/NodeImgAdjust.js b/simple-mind-map/src/plugins/NodeImgAdjust.js index cdde7045..9913ed33 100644 --- a/simple-mind-map/src/plugins/NodeImgAdjust.js +++ b/simple-mind-map/src/plugins/NodeImgAdjust.js @@ -49,7 +49,7 @@ class NodeImgAdjust { // 如果当前正在拖动调整中那么直接返回 if (this.isMousedown || this.isAdjusted || this.mindMap.opt.readonly) return // 如果在当前节点内移动,以及自定义元素已经是显示状态,那么直接返回 - if (this.node === node && this.isShowHandleEl) return + if (this.node && this.node.uid === node.uid && this.isShowHandleEl) return // 更新当前节点信息 this.node = node this.img = img diff --git a/simple-mind-map/src/plugins/Painter.js b/simple-mind-map/src/plugins/Painter.js index b9ef5cd3..5dd0be52 100644 --- a/simple-mind-map/src/plugins/Painter.js +++ b/simple-mind-map/src/plugins/Painter.js @@ -1,4 +1,4 @@ -import { nodeDataNoStylePropList } from '../constants/constant' +import { checkIsNodeStyleDataKey } from '../utils/index' // 格式刷插件 class Painter { @@ -49,13 +49,13 @@ class Painter { !this.isInPainter || !this.painterNode || !node || - node === this.painterNode + node.uid === this.painterNode.uid ) return const style = {} const painterNodeData = this.painterNode.nodeData.data Object.keys(painterNodeData).forEach(key => { - if (!nodeDataNoStylePropList.includes(key)) { + if (checkIsNodeStyleDataKey(key)) { style[key] = painterNodeData[key] } }) diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index 8c158fba..596635c8 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -5,7 +5,8 @@ import { walk, getTextFromHtml, isWhite, - getVisibleColorFromTheme + getVisibleColorFromTheme, + isUndef } from '../utils' import { CONSTANTS } from '../constants/constant' @@ -237,7 +238,10 @@ class RichText { } if (!node.nodeData.data.richText) { // 还不是富文本的情况 - let text = node.nodeData.data.text.split(/\n/gim).join('
') + let text = '' + if (!isUndef(node.nodeData.data.text)) { + text = String(node.nodeData.data.text).split(/\n/gim).join('
') + } let html = `

${text}

` this.textEditNode.innerHTML = this.cacheEditingText || html } else { @@ -249,7 +253,9 @@ class RichText { this.showTextEdit = true // 如果是刚创建的节点,那么默认全选,否则普通激活不全选,除非selectTextOnEnterEditText配置为true // 在selectTextOnEnterEditText时,如果是在keydown事件进入的节点编辑,也不需要全选 - this.focus(isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null) + this.focus( + isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null + ) if (!node.nodeData.data.richText) { // 如果是非富文本的情况,需要手动应用文本样式 this.setTextStyleIfNotRichText(node) @@ -373,7 +379,7 @@ class RichText { } }) // 拦截粘贴,只允许粘贴纯文本 - this.quill.clipboard.addMatcher(Node.TEXT_NODE, (node) => { + this.quill.clipboard.addMatcher(Node.TEXT_NODE, node => { let style = this.getPasteTextStyle() return new Delta().insert(node.data, style) }) @@ -614,7 +620,7 @@ class RichText { // 处理导入数据 handleSetData(data) { let walk = root => { - if (!root.data.richText) { + if (root.data && !root.data.richText) { root.data.richText = true root.data.resetRichText = true } diff --git a/simple-mind-map/src/plugins/Scrollbar.js b/simple-mind-map/src/plugins/Scrollbar.js index c34b9b7a..ab906741 100644 --- a/simple-mind-map/src/plugins/Scrollbar.js +++ b/simple-mind-map/src/plugins/Scrollbar.js @@ -1,4 +1,5 @@ import { throttle } from '../utils/index' +import { CONSTANTS } from '../constants/constant' // 滚动条插件 class Scrollbar { @@ -9,45 +10,13 @@ class Scrollbar { width: 0, // 水平滚动条的容器宽度 height: 0 // 垂直滚动条的容器高度 } + // 思维导图实际高度 + this.chartHeight = 0 + this.chartWidth = 0 this.reset() this.bindEvent() } - // 绑定事件 - bindEvent() { - this.onMousemove = this.onMousemove.bind(this) - this.onMouseup = this.onMouseup.bind(this) - this.onNodeTreeRenderEnd = this.onNodeTreeRenderEnd.bind(this) - this.onViewDataChange = throttle(this.onViewDataChange, 16, this) // 加个节流 - this.mindMap.on('mousemove', this.onMousemove) - this.mindMap.on('mouseup', this.onMouseup) - this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd) - this.mindMap.on('view_data_change', this.onViewDataChange) - } - - // 解绑事件 - unBindEvent() { - this.mindMap.off('mousemove', this.onMousemove) - this.mindMap.off('mouseup', this.onMouseup) - this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd) - this.mindMap.off('view_data_change', this.onViewDataChange) - } - - // 每次渲染后需要更新滚动条 - onNodeTreeRenderEnd() { - this.emitEvent() - } - - // 思维导图视图数据改变需要更新滚动条 - onViewDataChange() { - this.emitEvent() - } - - // 发送滚动条改变事件 - emitEvent() { - this.mindMap.emit('scrollbar_change') - } - // 复位数据 reset() { // 当前拖拽的滚动条类型 @@ -57,13 +26,41 @@ class Scrollbar { x: 0, y: 0 } - this.startViewPos = { - x: 0, - y: 0 - } - // 思维导图实际高度 - this.chartHeight = 0 - this.chartWidth = 0 + // 鼠标按下时,滚动条位置 + this.mousedownScrollbarPos = 0 + } + + // 绑定事件 + bindEvent() { + this.onMousemove = this.onMousemove.bind(this) + this.onMouseup = this.onMouseup.bind(this) + this.updateScrollbar = this.updateScrollbar.bind(this) + this.updateScrollbar = throttle(this.updateScrollbar, 16, this) // 加个节流 + this.mindMap.on('mousemove', this.onMousemove) + this.mindMap.on('mouseup', this.onMouseup) + this.mindMap.on('node_tree_render_end', this.updateScrollbar) + this.mindMap.on('view_data_change', this.updateScrollbar) + } + + // 解绑事件 + unBindEvent() { + this.mindMap.off('mousemove', this.onMousemove) + this.mindMap.off('mouseup', this.onMouseup) + this.mindMap.off('node_tree_render_end', this.updateScrollbar) + this.mindMap.off('view_data_change', this.updateScrollbar) + } + + // 渲染后、数据改变需要更新滚动条 + updateScrollbar() { + // 当前正在拖拽滚动条时不需要更新 + if (this.isMousedown) return + const res = this.calculationScrollbar() + this.emitEvent(res) + } + + // 发送滚动条改变事件 + emitEvent(data) { + this.mindMap.emit('scrollbar_change', data) } // 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度 @@ -78,9 +75,7 @@ class Scrollbar { // 减去画布距离浏览器窗口左上角的距离 const elRect = this.mindMap.elRect rect.x -= elRect.left - rect.x2 -= elRect.left rect.y -= elRect.top - rect.y2 -= elRect.top // 垂直滚动条 const canvasHeight = this.mindMap.height // 画布高度 @@ -129,46 +124,129 @@ class Scrollbar { return res } + // 滚动条鼠标按下事件处理函数 onMousedown(e, type) { e.preventDefault() + e.stopPropagation() this.currentScrollType = type this.isMousedown = true this.mousedownPos = { x: e.clientX, y: e.clientY } - // 保存视图当前的偏移量 - let transformData = this.mindMap.view.getTransformData() - this.startViewPos = { - x: transformData.state.x, - y: transformData.state.y + // 保存滚动条当前的位置 + const styles = window.getComputedStyle(e.target) + if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + this.mousedownScrollbarPos = Number.parseFloat(styles.top) + } else { + this.mousedownScrollbarPos = Number.parseFloat(styles.left) } } + // 鼠标移动事件处理函数 onMousemove(e) { if (!this.isMousedown) { return } - if (this.currentScrollType === 'vertical') { - const oy = e.clientY - this.mousedownPos.y - const oyPercentage = -oy / this.scrollbarWrapSize.height - const oyPx = oyPercentage * this.chartHeight - // 在视图最初偏移量上累加更新量 - this.mindMap.view.translateYTo(oyPx + this.startViewPos.y) + e.preventDefault() + e.stopPropagation() + if (this.currentScrollType === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + const oy = e.clientY - this.mousedownPos.y + this.mousedownScrollbarPos + this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.VERTICAL, oy) } else { - const ox = e.clientX - this.mousedownPos.x - const oxPercentage = -ox / this.scrollbarWrapSize.width - const oxPx = oxPercentage * this.chartWidth - // 在视图最初偏移量上累加更新量 - this.mindMap.view.translateXTo(oxPx + this.startViewPos.x) + const ox = e.clientX - this.mousedownPos.x + this.mousedownScrollbarPos + this.updateMindMapView(CONSTANTS.SCROLL_BAR_DIR.HORIZONTAL, ox) } } + // 鼠标松开事件处理函数 onMouseup() { this.isMousedown = false this.reset() } + // 更新视图 + updateMindMapView(type, offset) { + const scrollbarData = this.calculationScrollbar() + const t = this.mindMap.draw.transform() + const drawRect = this.mindMap.draw.rbox() + const rootRect = this.mindMap.renderer.root.group.rbox() + if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + // 滚动条新位置 + let oy = offset + // 判断是否达到首尾 + if (oy <= 0) { + oy = 0 + } + let max = + ((100 - scrollbarData.vertical.height) / 100) * + this.scrollbarWrapSize.height + if (oy >= max) { + oy = max + } + // 转换成百分比 + const oyPercentage = (oy / this.scrollbarWrapSize.height) * 100 + // 转换成相对于图形高度的距离 + const oyPx = (-oyPercentage / 100) * this.chartHeight + // 节点中心点到图形最上方的距离 + const yOffset = rootRect.y - drawRect.y + // 内边距 + const paddingY = this.mindMap.height / 2 + // 图形新位置 + let chartTop = oyPx + yOffset - paddingY * t.scaleY + paddingY + this.mindMap.view.translateYTo(chartTop) + this.emitEvent({ + horizontal: scrollbarData.horizontal, + vertical: { + top: oyPercentage, + height: scrollbarData.vertical.height + } + }) + } else { + // 滚动条新位置 + let ox = offset + // 判断是否达到首尾 + if (ox <= 0) { + ox = 0 + } + let max = + ((100 - scrollbarData.horizontal.width) / 100) * + this.scrollbarWrapSize.width + if (ox >= max) { + ox = max + } + // 转换成百分比 + const oxPercentage = (ox / this.scrollbarWrapSize.width) * 100 + // 转换成相对于图形高度的距离 + const oxPx = (-oxPercentage / 100) * this.chartWidth + // 节点中心点到图形最左边的距离 + const xOffset = rootRect.x - drawRect.x + // 内边距 + const paddingX = this.mindMap.width / 2 + // 图形新位置 + let chartLeft = oxPx + xOffset - paddingX * t.scaleX + paddingX + this.mindMap.view.translateXTo(chartLeft) + this.emitEvent({ + vertical: scrollbarData.vertical, + horizontal: { + left: oxPercentage, + width: scrollbarData.horizontal.width + } + }) + } + } + + // 滚动条的点击事件 + onClick(e, type) { + let offset = 0 + if (type === CONSTANTS.SCROLL_BAR_DIR.VERTICAL) { + offset = e.clientY - e.currentTarget.getBoundingClientRect().top + } else { + offset = e.clientX - e.currentTarget.getBoundingClientRect().left + } + this.updateMindMapView(type, offset) + } + // 插件被卸载前做的事情 beforePluginDestroy() { this.unBindEvent() diff --git a/simple-mind-map/src/plugins/Search.js b/simple-mind-map/src/plugins/Search.js index c3bf654f..77afa6df 100644 --- a/simple-mind-map/src/plugins/Search.js +++ b/simple-mind-map/src/plugins/Search.js @@ -1,4 +1,9 @@ -import { bfsWalk, getTextFromHtml, isUndef, replaceHtmlText } from '../utils/index' +import { + bfsWalk, + getTextFromHtml, + isUndef, + replaceHtmlText +} from '../utils/index' // 搜索插件 class Search { diff --git a/simple-mind-map/src/plugins/Select.js b/simple-mind-map/src/plugins/Select.js index fe6d801d..c26ebb58 100644 --- a/simple-mind-map/src/plugins/Select.js +++ b/simple-mind-map/src/plugins/Select.js @@ -1,4 +1,4 @@ -import { bfsWalk, throttle } from '../utils' +import { bfsWalk, throttle, checkTwoRectIsOverlap } from '../utils' // 节点选择插件 class Select { @@ -54,8 +54,40 @@ class Select { ) { return } - clearTimeout(this.autoMoveTimer) - this.onMove(x, y) + this.clearAutoMoveTimer() + this.onMove( + e.clientX, + e.clientY, + () => { + this.isSelecting = true + // 绘制矩形 + this.rect.plot([ + [this.mouseDownX, this.mouseDownY], + [this.mouseMoveX, this.mouseDownY], + [this.mouseMoveX, this.mouseMoveY], + [this.mouseDownX, this.mouseMoveY] + ]) + this.checkInNodes() + }, + (dir, step) => { + switch (dir) { + case 'left': + this.mouseDownX += step + break + case 'top': + this.mouseDownY += step + break + case 'right': + this.mouseDownX -= step + break + case 'bottom': + this.mouseDownY -= step + break + default: + break + } + } + ) }) this.mindMap.on('mouseup', () => { if (this.mindMap.opt.readonly) { @@ -78,79 +110,78 @@ class Select { // 如果激活节点改变了,那么触发事件 checkTriggerNodeActiveEvent() { - let isNumChange = this.cacheActiveList.length !== this.mindMap.renderer.activeNodeList.length + let isNumChange = + this.cacheActiveList.length !== + this.mindMap.renderer.activeNodeList.length let isNodeChange = false if (!isNumChange) { - for(let i = 0; i < this.cacheActiveList.length; i++) { + for (let i = 0; i < this.cacheActiveList.length; i++) { let cur = this.cacheActiveList[i] - if (!this.mindMap.renderer.activeNodeList.find((item) => { - return item.nodeData.data.uid === cur.nodeData.data.uid - })){ + if ( + !this.mindMap.renderer.activeNodeList.find(item => { + return item.nodeData.data.uid === cur.nodeData.data.uid + }) + ) { isNodeChange = true break } } } if (isNumChange || isNodeChange) { - this.mindMap.emit( - 'node_active', - null, - [...this.mindMap.renderer.activeNodeList] - ) + this.mindMap.emit('node_active', null, [ + ...this.mindMap.renderer.activeNodeList + ]) } } // 鼠标移动事件 - onMove(x, y) { - this.isSelecting = true - // 绘制矩形 - this.rect.plot([ - [this.mouseDownX, this.mouseDownY], - [this.mouseMoveX, this.mouseDownY], - [this.mouseMoveX, this.mouseMoveY], - [this.mouseDownX, this.mouseMoveY] - ]) - this.checkInNodes() + onMove(x, y, callback = () => {}, handle = () => {}) { + callback() // 检测边缘移动 let step = this.mindMap.opt.selectTranslateStep let limit = this.mindMap.opt.selectTranslateLimit let count = 0 // 左边缘 if (x <= this.mindMap.elRect.left + limit) { - this.mouseDownX += step + handle('left', step) this.mindMap.view.translateX(step) count++ } // 右边缘 if (x >= this.mindMap.elRect.right - limit) { - this.mouseDownX -= step + handle('right', step) this.mindMap.view.translateX(-step) count++ } // 上边缘 if (y <= this.mindMap.elRect.top + limit) { - this.mouseDownY += step + handle('top', step) this.mindMap.view.translateY(step) count++ } // 下边缘 if (y >= this.mindMap.elRect.bottom - limit) { - this.mouseDownY -= step + handle('bottom', step) this.mindMap.view.translateY(-step) count++ } if (count > 0) { - this.startAutoMove(x, y) + this.startAutoMove(x, y, callback, handle) } } // 开启自动移动 - startAutoMove(x, y) { + startAutoMove(x, y, callback, handle) { this.autoMoveTimer = setTimeout(() => { - this.onMove(x, y) + this.onMove(x, y, callback, handle) }, 20) } + // 清除自动移动定时器 + clearAutoMoveTimer() { + clearTimeout(this.autoMoveTimer) + } + // 创建矩形 createRect(x, y) { this.rect = this.mindMap.svg @@ -179,24 +210,19 @@ class Select { left = left * scaleX + translateX top = top * scaleY + translateY if ( - ((left >= minx && left <= maxx) || (right >= minx && right <= maxx)) && - ((top >= miny && top <= maxy) || (bottom >= miny && bottom <= maxy)) + checkTwoRectIsOverlap(minx, maxx, miny, maxy, left, right, top, bottom) ) { - // this.mindMap.batchExecution.push('activeNode' + node.uid, () => { if (node.nodeData.data.isActive) { return } this.mindMap.renderer.setNodeActive(node, true) this.mindMap.renderer.addActiveNode(node) - // }) } else if (node.nodeData.data.isActive) { - // this.mindMap.batchExecution.push('activeNode' + node.uid, () => { if (!node.nodeData.data.isActive) { return } this.mindMap.renderer.setNodeActive(node, false) this.mindMap.renderer.removeActiveNode(node) - // }) } }) } diff --git a/simple-mind-map/src/plugins/Watermark.js b/simple-mind-map/src/plugins/Watermark.js index cd6a3c98..c511be50 100644 --- a/simple-mind-map/src/plugins/Watermark.js +++ b/simple-mind-map/src/plugins/Watermark.js @@ -109,7 +109,10 @@ class Watermark { // 更新水印 updateWatermark(config) { - this.mindMap.opt.watermarkConfig = merge(this.mindMap.opt.watermarkConfig, config) + this.mindMap.opt.watermarkConfig = merge( + this.mindMap.opt.watermarkConfig, + config + ) this.handleConfig(config) this.draw() } @@ -117,4 +120,4 @@ class Watermark { Watermark.instanceName = 'watermark' -export default Watermark \ No newline at end of file +export default Watermark diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineText.js b/simple-mind-map/src/plugins/associativeLine/associativeLineText.js index deb812d9..201077f0 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineText.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineText.js @@ -1,5 +1,9 @@ import { Text } from '@svgdotjs/svg.js' -import { getStrWithBrFromHtml } from '../../utils/index' +import { + getStrWithBrFromHtml, + focusInput, + selectAllInput +} from '../../utils/index' // 创建文字节点 function createText(data) { @@ -36,7 +40,7 @@ function showEditTextBox(g) { this.mindMap.keyCommand.addShortcut('Enter', () => { this.hideEditTextBox() }) - + // 输入框元素没有创建过,则先创建 if (!this.textEditNode) { this.textEditNode = document.createElement('div') this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;` @@ -55,20 +59,27 @@ function showEditTextBox(g) { associativeLineTextFontFamily, associativeLineTextLineHeight } = this.mindMap.themeConfig + let { defaultAssociativeLineText, nodeTextEditZIndex } = this.mindMap.opt let scale = this.mindMap.view.scale let [, , , node, toNode] = this.activeLine - let textLines = ( - this.getText(node, toNode) || this.mindMap.opt.defaultAssociativeLineText - ).split(/\n/gim) + let text = this.getText(node, toNode) + let textLines = (text || defaultAssociativeLineText).split(/\n/gim) this.textEditNode.style.fontFamily = associativeLineTextFontFamily this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px' this.textEditNode.style.lineHeight = textLines.length > 1 ? associativeLineTextLineHeight : 'normal' - this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex + this.textEditNode.style.zIndex = nodeTextEditZIndex this.textEditNode.innerHTML = textLines.join('
') this.textEditNode.style.display = 'block' this.updateTextEditBoxPos(g) this.showTextEdit = true + // 如果是默认文本要全选输入框 + if (text === '' || text === defaultAssociativeLineText) { + selectAllInput(this.textEditNode) + } else { + // 否则聚焦即可 + focusInput(this.textEditNode) + } } // 处理画布缩放 @@ -94,10 +105,13 @@ function hideEditTextBox() { } let [path, , text, node, toNode] = this.activeLine let str = getStrWithBrFromHtml(this.textEditNode.innerHTML) + // 如果是默认文本,那么不保存 + let isDefaultText = str === this.mindMap.opt.defaultAssociativeLineText + str = isDefaultText ? '' : str this.mindMap.execCommand('SET_NODE_DATA', node, { associativeLineText: { ...(node.nodeData.data.associativeLineText || {}), - [toNode.nodeData.data.id]: str + [toNode.nodeData.data.uid]: str } }) this.textEditNode.style.display = 'none' @@ -113,16 +127,14 @@ function getText(node, toNode) { if (!obj) { return '' } - return obj[toNode.nodeData.data.id] || '' + return obj[toNode.nodeData.data.uid] || '' } // 渲染关联线文字 function renderText(str, path, text) { if (!str) return - let { - associativeLineTextFontSize, - associativeLineTextLineHeight - } = this.mindMap.themeConfig + let { associativeLineTextFontSize, associativeLineTextLineHeight } = + this.mindMap.themeConfig text.clear() let textArr = str.split(/\n/gim) textArr.forEach((item, index) => { diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js index b68216e5..ca46b6f8 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js @@ -1,7 +1,7 @@ // 获取目标节点在起始节点的目标数组中的索引 export const getAssociativeLineTargetIndex = (node, toNode) => { return node.nodeData.data.associativeLineTargets.findIndex(item => { - return item === toNode.nodeData.data.id + return item === toNode.nodeData.data.uid }) } diff --git a/simple-mind-map/src/svg/icons.js b/simple-mind-map/src/svg/icons.js index c08ab61b..cafe49d4 100644 --- a/simple-mind-map/src/svg/icons.js +++ b/simple-mind-map/src/svg/icons.js @@ -1,3 +1,5 @@ +import { mergerIconList } from '../utils' + // 超链接图标 const hyperlink = '' @@ -281,12 +283,21 @@ export const nodeIconList = [ // 获取nodeIconList icon内容 const getNodeIconListIcon = (name, extendIconList = []) => { let arr = name.split('_') - let typeData = [...nodeIconList, ...extendIconList].find(item => { + const iconList = mergerIconList([...nodeIconList, ...extendIconList]) + let typeData = iconList.find(item => { return item.type === arr[0] }) - return typeData.list.find(item => { - return item.name === arr[1] - }).icon + if (typeData) { + let typeName = typeData.list.find(item => { + return item.name === arr[1] + }) + if (typeName) { + return typeName.icon + } + return '' + } else { + return '' + } } export default { diff --git a/simple-mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js index 34bd20b6..897a46d7 100644 --- a/simple-mind-map/src/themes/default.js +++ b/simple-mind-map/src/themes/default.js @@ -161,12 +161,14 @@ const nodeSizeIndependenceList = [ 'backgroundSize', 'rootLineKeepSameInCurve' ] -export const checkIsNodeSizeIndependenceConfig = (config) => { +export const checkIsNodeSizeIndependenceConfig = config => { let keys = Object.keys(config) - for(let i = 0; i < keys.length; i++) { - if (!nodeSizeIndependenceList.find((item) => { - return item === keys[i] - })) { + for (let i = 0; i < keys.length; i++) { + if ( + !nodeSizeIndependenceList.find(item => { + return item === keys[i] + }) + ) { return false } } diff --git a/simple-mind-map/src/utils/Lru.js b/simple-mind-map/src/utils/Lru.js index 0518a1fa..4edc0213 100644 --- a/simple-mind-map/src/utils/Lru.js +++ b/simple-mind-map/src/utils/Lru.js @@ -1,39 +1,38 @@ // LRU缓存类 -export default class CRU { - constructor(max) { - this.max = max || 1000 - this.size = 0 - this.pool = new Map() - } +export default class Lru { + constructor(max) { + this.max = max || 1000 + this.size = 0 + this.pool = new Map() + } - add(key, value) { - // 如果该key是否已经存在,则先删除 - this.delete(key) - this.pool.set(key, value) - this.size++ - // 如果数量超出最大值,则删除最早的 - if (this.size > this.max) { - let keys = this.pool.keys() - let last = keys.next() - this.delete(last.value) - } - + add(key, value) { + // 如果该key是否已经存在,则先删除 + this.delete(key) + this.pool.set(key, value) + this.size++ + // 如果数量超出最大值,则删除最早的 + if (this.size > this.max) { + let keys = this.pool.keys() + let last = keys.next() + this.delete(last.value) } + } - delete(key) { - if (this.pool.has(key)) { - this.pool.delete(key) - this.size-- - } + delete(key) { + if (this.pool.has(key)) { + this.pool.delete(key) + this.size-- } + } - has(key) { - return this.pool.has(key) - } + has(key) { + return this.pool.has(key) + } - get(key) { - if (this.pool.has(key)) { - return this.pool.get(key) - } + get(key) { + if (this.pool.has(key)) { + return this.pool.get(key) } -} \ No newline at end of file + } +} diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index 405e2c56..c0918ad0 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -1,5 +1,6 @@ import { v4 as uuidv4 } from 'uuid' - +import { nodeDataNoStylePropList } from '../constants/constant' +import MersenneTwister from './mersenneTwister' // 深度优先遍历树 export const walk = ( root, @@ -169,9 +170,8 @@ export const copyNodeTree = ( keepId = false ) => { tree.data = simpleDeepClone(root.nodeData ? root.nodeData.data : root.data) - // 去除节点id,因为节点id不能重复 - if (tree.data.id && !keepId) delete tree.data.id - if (tree.data.uid) delete tree.data.uid + // 去除节点uid,因为节点uid不能重复 + if (tree.data.uid && !keepId) delete tree.data.uid if (removeActiveState) { tree.data.isActive = false } @@ -636,7 +636,7 @@ export const isMobile = () => { // 获取对象改变了的的属性 export const getObjectChangedProps = (oldObject, newObject) => { const res = {} - Object.keys(newObject).forEach((prop) => { + Object.keys(newObject).forEach(prop => { const oldVal = oldObject[prop] const newVal = newObject[prop] if (getType(oldVal) !== getType(newVal)) { @@ -656,4 +656,170 @@ export const getObjectChangedProps = (oldObject, newObject) => { } }) return res -} \ No newline at end of file +} + +// 判断一个字段是否是节点数据中的样式字段 +export const checkIsNodeStyleDataKey = key => { + // 用户自定义字段 + if (/^_/.test(key)) return false + // 不在节点非样式字段列表里,那么就是样式字段 + if (!nodeDataNoStylePropList.includes(key)) { + return true + } + return false +} + +// 合并图标数组 +// const data = [ +// { type: 'priority', name: '优先级图标', list: [{ name: '1', icon: 'a' }, { name: 2, icon: 'b' }] }, +// { type: 'priority', name: '优先级图标', list: [{ name: '2', icon: 'c' }, { name: 3, icon: 'd' }] }, +// ]; + +// mergerIconList(data) 结果 + +// [ +// { type: 'priority', name: '优先级图标', list: [{ name: '1', icon: 'a' }, { name: 2, icon: 'c' }, { name: 3, icon: 'd' }] }, +// ] +export const mergerIconList = list => { + return list.reduce((result, item) => { + const existingItem = result.find(x => x.type === item.type) + if (existingItem) { + item.list.forEach(newObj => { + const existingObj = existingItem.list.find(x => x.name === newObj.name) + if (existingObj) { + existingObj.icon = newObj.icon + } else { + existingItem.list.push(newObj) + } + }) + } else { + result.push({ ...item }) + } + return result + }, []) +} + +// 从节点实例列表里找出顶层的节点 +export const getTopAncestorsFomNodeList = list => { + let res = [] + list.forEach(node => { + if ( + !list.find(item => { + return item.uid !== node.uid && item.isParent(node) + }) + ) { + res.push(node) + } + }) + return res +} + +// 判断两个矩形是否重叠 +export const checkTwoRectIsOverlap = ( + minx1, + maxx1, + miny1, + maxy1, + minx2, + maxx2, + miny2, + maxy2 +) => { + return maxx1 > minx2 && maxx2 > minx1 && maxy1 > miny2 && maxy2 > miny1 +} + +// 聚焦指定输入框 +export const focusInput = el => { + let selection = window.getSelection() + let range = document.createRange() + range.selectNodeContents(el) + range.collapse() + selection.removeAllRanges() + selection.addRange(range) +} + +// 聚焦全选指定输入框 +export const selectAllInput = el => { + let selection = window.getSelection() + let range = document.createRange() + range.selectNodeContents(el) + selection.removeAllRanges() + selection.addRange(range) +} + +// 给指定的节点列表树数据添加附加数据,会修改原数据 +export const addDataToAppointNodes = (appointNodes, data = {}) => { + const walk = list => { + list.forEach(node => { + node.data = { + ...node.data, + ...data + } + if (node.children && node.children.length > 0) { + walk(node.children) + } + }) + } + walk(appointNodes) + return appointNodes +} + +// 给指定的节点列表树数据添加uid,如果不存在的话,会修改原数据 +export const createUidForAppointNodes = appointNodes => { + const walk = list => { + list.forEach(node => { + if (!node.data) { + node.data = {} + } + if (isUndef(node.data.uid)) { + node.data.uid = createUid() + } + if (node.children && node.children.length > 0) { + walk(node.children) + } + }) + } + walk(appointNodes) + return appointNodes +} + +// 传入一个数据,如果该数据是数组,那么返回该数组,否则返回一个以该数据为成员的数组 +export const formatDataToArray = data => { + if (!data) return [] + return Array.isArray(data) ? data : [data] +} + +// 获取节点在同级里的位置索引 +export const getNodeIndex = node => { + return node.parent + ? node.parent.children.findIndex(item => { + return item.uid === node.uid + }) + : 0 +} + +// 根据内容生成颜色 +export const generateColorByContent = str => { + let hash = 0 + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash) + } + // 这里使用伪随机数的原因是因为 + // 1. 如果字符串的内容差不多,根据hash生产的颜色就比较相近,不好区分,比如v1.1 v1.2,所以需要加入随机数来使得颜色能够区分开 + // 2. 普通的随机数每次数值不一样,就会导致每次新增标签原来的标签颜色就会发生改变,所以加入了这个方法,使得内容不变随机数也不变 + const rng = new MersenneTwister(hash) + const h = rng.genrand_int32() % 360 + return 'hsla(' + h + ', 50%, 50%, 1)' +} + +// html转义 +export const htmlEscape = str => { + ;[ + ['&', '&'], + ['<', '<'], + ['>', '>'] + ].forEach(item => { + str = str.replace(new RegExp(item[0], 'g'), item[1]) + }) + return str +} diff --git a/simple-mind-map/src/utils/mersenneTwister.js b/simple-mind-map/src/utils/mersenneTwister.js new file mode 100644 index 00000000..6d0cd275 --- /dev/null +++ b/simple-mind-map/src/utils/mersenneTwister.js @@ -0,0 +1,65 @@ +/** + * @description 为了保证相同的内容每次生成的随机数都是一样的,我们可以使用一个伪随机数生成器(PRNG),并使用内容的哈希值作为种子。以下是一个使用Mersenne Twister算法的PRNG的实现: + * + * @param {*} seed + */ + +export default function MersenneTwister(seed) { + this.N = 624 + this.M = 397 + this.MATRIX_A = 0x9908b0df + this.UPPER_MASK = 0x80000000 + this.LOWER_MASK = 0x7fffffff + + this.mt = new Array(this.N) + this.mti = this.N + 1 + + this.init_genrand(seed) +} + +MersenneTwister.prototype.init_genrand = function (s) { + this.mt[0] = s >>> 0 + for (this.mti = 1; this.mti < this.N; this.mti++) { + s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30) + this.mt[this.mti] = + ((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + + (s & 0x0000ffff) * 1812433253 + + this.mti + this.mt[this.mti] >>>= 0 + } +} + +MersenneTwister.prototype.genrand_int32 = function () { + var y + var mag01 = new Array(0x0, this.MATRIX_A) + + if (this.mti >= this.N) { + var kk + + if (this.mti == this.N + 1) this.init_genrand(5489) + + for (kk = 0; kk < this.N - this.M; kk++) { + y = (this.mt[kk] & this.UPPER_MASK) | (this.mt[kk + 1] & this.LOWER_MASK) + this.mt[kk] = this.mt[kk + this.M] ^ (y >>> 1) ^ mag01[y & 0x1] + } + + for (; kk < this.N - 1; kk++) { + y = (this.mt[kk] & this.UPPER_MASK) | (this.mt[kk + 1] & this.LOWER_MASK) + this.mt[kk] = this.mt[kk + (this.M - this.N)] ^ (y >>> 1) ^ mag01[y & 0x1] + } + + y = (this.mt[this.N - 1] & this.UPPER_MASK) | (this.mt[0] & this.LOWER_MASK) + this.mt[this.N - 1] = this.mt[this.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1] + + this.mti = 0 + } + + y = this.mt[this.mti++] + + y ^= y >>> 11 + y ^= (y << 7) & 0x9d2c5680 + y ^= (y << 15) & 0xefc60000 + y ^= y >>> 18 + + return y >>> 0 +} diff --git a/simple-mind-map/src/utils/simulateCSSBackgroundInCanvas.js b/simple-mind-map/src/utils/simulateCSSBackgroundInCanvas.js index 5f33b3bb..c11cd215 100644 --- a/simple-mind-map/src/utils/simulateCSSBackgroundInCanvas.js +++ b/simple-mind-map/src/utils/simulateCSSBackgroundInCanvas.js @@ -351,4 +351,4 @@ const drawBackgroundImageToCanvas = ( } } -export default drawBackgroundImageToCanvas \ No newline at end of file +export default drawBackgroundImageToCanvas diff --git a/simple-mind-map/types/index.d.ts b/simple-mind-map/types/index.d.ts new file mode 100644 index 00000000..31567e7e --- /dev/null +++ b/simple-mind-map/types/index.d.ts @@ -0,0 +1,172 @@ +export default MindMap; +declare class MindMap { + /** + * + * @param {defaultOpt} opt + */ + constructor(opt?: { + readonly: boolean; + layout: string; + fishboneDeg: number; + theme: string; + themeConfig: {}; + scaleRatio: number; + mouseScaleCenterUseMousePosition: boolean; + maxTag: number; + expandBtnSize: number; + imgTextMargin: number; + textContentMargin: number; + selectTranslateStep: number; + selectTranslateLimit: number; + customNoteContentShow: any; + enableFreeDrag: boolean; + watermarkConfig: { + text: string; + lineSpacing: number; + textSpacing: number; + angle: number; + textStyle: { + color: string; + opacity: number; + fontSize: number; + }; + }; + textAutoWrapWidth: number; + customHandleMousewheel: any; + mousewheelAction: string; + mousewheelMoveStep: number; + mousewheelZoomActionReverse: boolean; + defaultInsertSecondLevelNodeText: string; + defaultInsertBelowSecondLevelNodeText: string; + expandBtnStyle: { + color: string; + fill: string; + fontSize: number; + strokeColor: string; + }; + expandBtnIcon: { + open: string; + close: string; + }; + expandBtnNumHandler: (num: any) => any; + isShowExpandNum: boolean; + enableShortcutOnlyWhenMouseInSvg: boolean; + initRootNodePosition: any; + exportPaddingX: number; + exportPaddingY: number; + nodeTextEditZIndex: number; + nodeNoteTooltipZIndex: number; + isEndNodeTextEditOnClickOuter: boolean; + maxHistoryCount: number; + alwaysShowExpandBtn: boolean; + iconList: any[]; + maxNodeCacheCount: number; + defaultAssociativeLineText: string; + fitPadding: number; + enableCtrlKeyNodeSelection: boolean; + useLeftKeySelectionRightKeyDrag: boolean; + beforeTextEdit: any; + isUseCustomNodeContent: boolean; + customCreateNodeContent: any; + customInnerElsAppendTo: any; + nodeDragPlaceholderMaxSize: number; + enableAutoEnterTextEditWhenKeydown: boolean; + richTextEditFakeInPlace: boolean; + customHandleClipboardText: any; + disableMouseWheelZoom: boolean; + errorHandler: (code: any, error: any) => void; + resetCss: string; + enableDblclickReset: boolean; + minExportImgCanvasScale: number; + hoverRectColor: string; + hoverRectPadding: number; + selectTextOnEnterEditText: boolean; + deleteNodeActive: boolean; + autoMoveWhenMouseInEdgeOnDrag: boolean; + fit: boolean; + dragMultiNodeRectConfig: { + width: number; + height: number; + fill: string; + }; + dragPlaceholderRectFill: string; + dragOpacityConfig: { + cloneNodeOpacity: number; + beingDragNodeOpacity: number; + }; + tagsColorMap: {}; + }); + opt: any; + el: any; + elRect: any; + width: any; + height: any; + cssEl: HTMLStyleElement; + svg: any; + draw: any; + event: Event; + keyCommand: KeyCommand; + command: Command; + renderer: Render; + view: View; + batchExecution: BatchExecution; + handleOpt(opt: any): any; + addCss(): void; + removeCss(): void; + render(callback: any, source?: string): void; + reRender(callback: any, source?: string): void; + resize(): void; + on(event: any, fn: any): void; + emit(event: any, ...args: any[]): void; + off(event: any, fn: any): void; + initCache(): void; + initTheme(): void; + themeConfig: any; + setTheme(theme: any): void; + getTheme(): any; + setThemeConfig(config: any): void; + getCustomThemeConfig(): any; + getThemeConfig(prop: any): any; + getConfig(prop: any): any; + updateConfig(opt?: {}): void; + getLayout(): any; + setLayout(layout: any): void; + execCommand(...args: any[]): void; + setData(data: any): void; + setFullData(data: any): void; + getData(withConfig: any): any; + export(...args: any[]): Promise; + toPos(x: any, y: any): { + x: number; + y: number; + }; + setMode(mode: any): void; + getSvgData({ paddingX, paddingY }?: { + paddingX?: number; + paddingY?: number; + }): { + svg: any; + svgHTML: any; + rect: any; + origWidth: any; + origHeight: any; + scaleX: any; + scaleY: any; + }; + addPlugin(plugin: any, opt: any): void; + removePlugin(plugin: any): void; + initPlugin(plugin: any): void; + destroy(): void; +} +declare namespace MindMap { + let pluginList: any[]; + function usePlugin(plugin: any, opt?: {}): typeof MindMap; + function hasPlugin(plugin: any): number; + function defineTheme(name: any, config?: {}): Error; +} +import Event from './src/core/event/Event'; +import KeyCommand from './src/core/command/KeyCommand'; +import Command from './src/core/command/Command'; +import Render from './src/core/render/Render'; +import View from './src/core/view/View'; +import BatchExecution from './src/utils/BatchExecution'; diff --git a/simple-mind-map/types/src/constants/constant.d.ts b/simple-mind-map/types/src/constants/constant.d.ts new file mode 100644 index 00000000..bf00d5b9 --- /dev/null +++ b/simple-mind-map/types/src/constants/constant.d.ts @@ -0,0 +1,109 @@ +export const themeList: { + name: string; + value: string; + dark: boolean; +}[]; +export namespace CONSTANTS { + let CHANGE_THEME: string; + let CHANGE_LAYOUT: string; + let SET_DATA: string; + let TRANSFORM_TO_NORMAL_NODE: string; + namespace MODE { + let READONLY: string; + let EDIT: string; + } + namespace LAYOUT { + let LOGICAL_STRUCTURE: string; + let MIND_MAP: string; + let ORGANIZATION_STRUCTURE: string; + let CATALOG_ORGANIZATION: string; + let TIMELINE: string; + let TIMELINE2: string; + let FISHBONE: string; + let VERTICAL_TIMELINE: string; + } + namespace DIR { + let UP: string; + let LEFT: string; + let DOWN: string; + let RIGHT: string; + } + namespace KEY_DIR { + let LEFT_1: string; + export { LEFT_1 as LEFT }; + let UP_1: string; + export { UP_1 as UP }; + let RIGHT_1: string; + export { RIGHT_1 as RIGHT }; + let DOWN_1: string; + export { DOWN_1 as DOWN }; + } + namespace SHAPE { + let RECTANGLE: string; + let DIAMOND: string; + let PARALLELOGRAM: string; + let ROUNDED_RECTANGLE: string; + let OCTAGONAL_RECTANGLE: string; + let OUTER_TRIANGULAR_RECTANGLE: string; + let INNER_TRIANGULAR_RECTANGLE: string; + let ELLIPSE: string; + let CIRCLE: string; + } + namespace MOUSE_WHEEL_ACTION { + let ZOOM: string; + let MOVE: string; + } + namespace INIT_ROOT_NODE_POSITION { + let LEFT_2: string; + export { LEFT_2 as LEFT }; + export let TOP: string; + let RIGHT_2: string; + export { RIGHT_2 as RIGHT }; + export let BOTTOM: string; + export let CENTER: string; + } + namespace LAYOUT_GROW_DIR { + let LEFT_3: string; + export { LEFT_3 as LEFT }; + let TOP_1: string; + export { TOP_1 as TOP }; + let RIGHT_3: string; + export { RIGHT_3 as RIGHT }; + let BOTTOM_1: string; + export { BOTTOM_1 as BOTTOM }; + } + namespace PASTE_TYPE { + let CLIP_BOARD: string; + let CANVAS: string; + } + namespace SCROLL_BAR_DIR { + let VERTICAL: string; + let HORIZONTAL: string; + } +} +export const initRootNodePositionMap: { + [x: string]: number; +}; +export const layoutList: { + name: string; + value: string; +}[]; +export const layoutValueList: string[]; +export const nodeDataNoStylePropList: string[]; +export namespace commonCaches { + let measureCustomNodeContentSizeEl: any; + let measureRichtextNodeTextSizeEl: any; +} +export namespace ERROR_TYPES { + let READ_CLIPBOARD_ERROR: string; + let PARSE_PASTE_DATA_ERROR: string; + let CUSTOM_HANDLE_CLIPBOARD_TEXT_ERROR: string; + let LOAD_CLIPBOARD_IMAGE_ERROR: string; + let BEFORE_TEXT_EDIT_ERROR: string; + let EXPORT_ERROR: string; +} +export namespace a4Size { + let width: number; + let height: number; +} +export const cssContent: "\n /* 鼠标hover和激活时渲染的矩形 */\n .smm-hover-node{\n display: none;\n opacity: 0.6;\n stroke-width: 1;\n }\n\n .smm-node:not(.smm-node-dragging):hover .smm-hover-node{\n display: block;\n }\n\n .smm-node.active .smm-hover-node{\n display: block;\n opacity: 1;\n stroke-width: 2;\n }\n"; diff --git a/simple-mind-map/types/src/constants/defaultOptions.d.ts b/simple-mind-map/types/src/constants/defaultOptions.d.ts new file mode 100644 index 00000000..93dcb638 --- /dev/null +++ b/simple-mind-map/types/src/constants/defaultOptions.d.ts @@ -0,0 +1,95 @@ +export namespace defaultOpt { + let readonly: boolean; + let layout: string; + let fishboneDeg: number; + let theme: string; + let themeConfig: {}; + let scaleRatio: number; + let mouseScaleCenterUseMousePosition: boolean; + let maxTag: number; + let expandBtnSize: number; + let imgTextMargin: number; + let textContentMargin: number; + let selectTranslateStep: number; + let selectTranslateLimit: number; + let customNoteContentShow: any; + let enableFreeDrag: boolean; + namespace watermarkConfig { + let text: string; + let lineSpacing: number; + let textSpacing: number; + let angle: number; + namespace textStyle { + let color: string; + let opacity: number; + let fontSize: number; + } + } + let textAutoWrapWidth: number; + let customHandleMousewheel: any; + let mousewheelAction: string; + let mousewheelMoveStep: number; + let mousewheelZoomActionReverse: boolean; + let defaultInsertSecondLevelNodeText: string; + let defaultInsertBelowSecondLevelNodeText: string; + namespace expandBtnStyle { + let color_1: string; + export { color_1 as color }; + export let fill: string; + let fontSize_1: number; + export { fontSize_1 as fontSize }; + export let strokeColor: string; + } + namespace expandBtnIcon { + let open: string; + let close: string; + } + function expandBtnNumHandler(num: any): any; + let isShowExpandNum: boolean; + let enableShortcutOnlyWhenMouseInSvg: boolean; + let initRootNodePosition: any; + let exportPaddingX: number; + let exportPaddingY: number; + let nodeTextEditZIndex: number; + let nodeNoteTooltipZIndex: number; + let isEndNodeTextEditOnClickOuter: boolean; + let maxHistoryCount: number; + let alwaysShowExpandBtn: boolean; + let iconList: any[]; + let maxNodeCacheCount: number; + let defaultAssociativeLineText: string; + let fitPadding: number; + let enableCtrlKeyNodeSelection: boolean; + let useLeftKeySelectionRightKeyDrag: boolean; + let beforeTextEdit: any; + let isUseCustomNodeContent: boolean; + let customCreateNodeContent: any; + let customInnerElsAppendTo: any; + let nodeDragPlaceholderMaxSize: number; + let enableAutoEnterTextEditWhenKeydown: boolean; + let richTextEditFakeInPlace: boolean; + let customHandleClipboardText: any; + let disableMouseWheelZoom: boolean; + function errorHandler(code: any, error: any): void; + let resetCss: string; + let enableDblclickReset: boolean; + let minExportImgCanvasScale: number; + let hoverRectColor: string; + let hoverRectPadding: number; + let selectTextOnEnterEditText: boolean; + let deleteNodeActive: boolean; + let autoMoveWhenMouseInEdgeOnDrag: boolean; + let fit: boolean; + namespace dragMultiNodeRectConfig { + export let width: number; + export let height: number; + let fill_1: string; + export { fill_1 as fill }; + } + let dragPlaceholderRectFill: string; + namespace dragOpacityConfig { + let cloneNodeOpacity: number; + let beingDragNodeOpacity: number; + } + let tagsColorMap: {}; +} diff --git a/simple-mind-map/types/src/core/command/Command.d.ts b/simple-mind-map/types/src/core/command/Command.d.ts new file mode 100644 index 00000000..8a928b6d --- /dev/null +++ b/simple-mind-map/types/src/core/command/Command.d.ts @@ -0,0 +1,19 @@ +export default Command; +declare class Command { + constructor(opt?: {}); + opt: {}; + mindMap: any; + commands: {}; + history: any[]; + activeHistoryIndex: number; + addHistory(): void; + clearHistory(): void; + registerShortcutKeys(): void; + exec(name: any, ...args: any[]): void; + add(name: any, fn: any): void; + remove(name: any, fn: any): void; + back(step?: number): any; + forward(step?: number): any; + getCopyData(): any; + removeDataUid(data: any): any; +} diff --git a/simple-mind-map/types/src/core/command/KeyCommand.d.ts b/simple-mind-map/types/src/core/command/KeyCommand.d.ts new file mode 100644 index 00000000..4a51eb8d --- /dev/null +++ b/simple-mind-map/types/src/core/command/KeyCommand.d.ts @@ -0,0 +1,26 @@ +export default class KeyCommand { + constructor(opt: any); + opt: any; + mindMap: any; + shortcutMap: {}; + shortcutMapCache: {}; + isPause: boolean; + isInSvg: boolean; + pause(): void; + recovery(): void; + save(): void; + restore(): void; + bindEvent(): void; + checkKey(e: any, key: any): boolean; + getOriginEventCodeArr(e: any): any[]; + hasCombinationKey(e: any): any; + getKeyCodeArr(key: any): any[]; + /** + * Enter + * Tab | Insert + * Shift + a + */ + addShortcut(key: any, fn: any): void; + removeShortcut(key: any, fn: any): void; + getShortcutFn(key: any): any[]; +} diff --git a/simple-mind-map/types/src/core/command/keyMap.d.ts b/simple-mind-map/types/src/core/command/keyMap.d.ts new file mode 100644 index 00000000..48c8dbdf --- /dev/null +++ b/simple-mind-map/types/src/core/command/keyMap.d.ts @@ -0,0 +1,42 @@ +export const keyMap: { + Backspace: number; + Tab: number; + Enter: number; + Shift: number; + Control: number; + Alt: number; + CapsLock: number; + Esc: number; + Spacebar: number; + PageUp: number; + PageDown: number; + End: number; + Home: number; + Insert: number; + Left: number; + Up: number; + Right: number; + Down: number; + Del: number; + NumLock: number; + Cmd: number; + CmdFF: number; + F1: number; + F2: number; + F3: number; + F4: number; + F5: number; + F6: number; + F7: number; + F8: number; + F9: number; + F10: number; + F11: number; + F12: number; + '`': number; + '=': number; + '-': number; + '/': number; + '.': number; +}; +export function isKey(e: any, key: any): boolean; diff --git a/simple-mind-map/types/src/core/event/Event.d.ts b/simple-mind-map/types/src/core/event/Event.d.ts new file mode 100644 index 00000000..d50ad30c --- /dev/null +++ b/simple-mind-map/types/src/core/event/Event.d.ts @@ -0,0 +1,35 @@ +export default Event; +declare class Event { + constructor(opt?: {}); + opt: {}; + mindMap: any; + isLeftMousedown: boolean; + isRightMousedown: boolean; + isMiddleMousedown: boolean; + mousedownPos: { + x: number; + y: number; + }; + mousemovePos: { + x: number; + y: number; + }; + mousemoveOffset: { + x: number; + y: number; + }; + bindFn(): void; + onBodyClick(e: any): void; + onDrawClick(e: any): void; + onMousedown(e: any): void; + onMousemove(e: any): void; + onMouseup(e: any): void; + onMousewheel(e: any): void; + onContextmenu(e: any): void; + onSvgMousedown(e: any): void; + onKeyup(e: any): void; + onMouseenter(e: any): void; + onMouseleave(e: any): void; + bind(): void; + unbind(): void; +} diff --git a/simple-mind-map/types/src/core/render/Render.d.ts b/simple-mind-map/types/src/core/render/Render.d.ts new file mode 100644 index 00000000..346c7709 --- /dev/null +++ b/simple-mind-map/types/src/core/render/Render.d.ts @@ -0,0 +1,95 @@ +export default Render; +declare class Render { + constructor(opt?: {}); + opt: {}; + mindMap: any; + themeConfig: any; + draw: any; + renderTree: any; + reRender: boolean; + isRendering: boolean; + hasWaitRendering: boolean; + nodeCache: {}; + lastNodeCache: {}; + renderSource: string; + activeNodeList: any[]; + root: any; + textEdit: TextEdit; + lastBeingCopyData: any; + beingCopyData: any; + beingPasteText: string; + beingPasteImgSize: number; + currentBeingPasteType: string; + setLayout(): void; + layout: MindMap | CatalogOrganization | OrganizationStructure | Timeline | VerticalTimeline; + bindEvent(): void; + registerCommands(): void; + selectAll(): void; + back(step: any): void; + forward(step: any): void; + insertNode(openEdit?: boolean, appointNodes?: any[], appointData?: any, appointChildren?: any[]): void; + insertMultiNode(appointNodes: any, nodeList: any): void; + insertChildNode(openEdit?: boolean, appointNodes?: any[], appointData?: any, appointChildren?: any[]): void; + insertMultiChildNode(appointNodes: any, childList: any): void; + upNode(): void; + downNode(): void; + insertAfter(node: any, exist: any): void; + insertBefore(node: any, exist: any): void; + moveNodeTo(node: any, toNode: any): void; + removeNode(appointNodes?: any[]): void; + pasteNode(data: any): void; + cutNode(callback: any): void; + setNodeStyle(node: any, prop: any, value: any): void; + setNodeStyles(node: any, style: any): void; + setNodeActive(node: any, active: any): void; + clearAllActive(): void; + setNodeExpand(node: any, expand: any): void; + expandAllNode(): void; + unexpandAllNode(): void; + expandToLevel(level: any): void; + setNodeData(node: any, data: any): void; + setNodeText(node: any, text: any, richText: any, resetRichText: any): void; + setNodeImage(node: any, data: any): void; + setNodeIcon(node: any, icons: any): void; + setNodeHyperlink(node: any, link: any, title?: string): void; + setNodeNote(node: any, note: any): void; + setNodeTag(node: any, tag: any): void; + insertFormula(formula: any, appointNodes?: any[]): void; + addGeneralization(data: any): void; + removeGeneralization(): void; + setNodeCustomPosition(node: any, left?: any, top?: any): void; + resetLayout(): void; + setNodeShape(node: any, shape: any): void; + goTargetNode(node: any, callback?: () => void): void; + registerShortcutKeys(): void; + insertNodeWrap: () => void; + toggleActiveExpand(): void; + removeNodeWrap: () => void; + copy(): void; + cut(): void; + startTextEdit(): void; + endTextEdit(): void; + render(callback: () => void, source: any): void; + clearActive(): void; + addActiveNode(node: any): void; + removeActiveNode(node: any): void; + findActiveNodeIndex(node: any): number; + setCopyDataToClipboard(data: any): void; + paste(): void; + onPaste(): Promise; + insertTo(node: any, exist: any, dir?: string): void; + checkNodeLayerChange(node: any, toNode: any): void; + removeOneNode(node: any): void; + copyNode(): any; + toggleNodeExpand(node: any): void; + setNodeDataRender(node: any, data: any, notRender?: boolean): void; + moveNodeToCenter(node: any): void; + expandToNodeUid(uid: any, callback?: () => void): void; + findNodeByUid(uid: any): any; +} +import TextEdit from './TextEdit'; +import MindMap from '../../layouts/MindMap'; +import CatalogOrganization from '../../layouts/CatalogOrganization'; +import OrganizationStructure from '../../layouts/OrganizationStructure'; +import Timeline from '../../layouts/Timeline'; +import VerticalTimeline from '../../layouts/VerticalTimeline'; diff --git a/simple-mind-map/types/src/core/render/TextEdit.d.ts b/simple-mind-map/types/src/core/render/TextEdit.d.ts new file mode 100644 index 00000000..b0e85633 --- /dev/null +++ b/simple-mind-map/types/src/core/render/TextEdit.d.ts @@ -0,0 +1,17 @@ +export default class TextEdit { + constructor(renderer: any); + renderer: any; + mindMap: any; + currentNode: any; + textEditNode: HTMLDivElement; + showTextEdit: boolean; + cacheEditingText: string; + bindEvent(): void; + show(node: any, e: any, isInserting?: boolean, isFromKeyDown?: boolean): Promise; + onScale(): void; + checkIsAutoEnterTextEditKey(e: any): boolean; + registerTmpShortcut(): void; + showEditTextBox(node: any, rect: any, isInserting: any, isFromKeyDown: any): void; + getEditText(): any; + hideEditTextBox(): any; +} diff --git a/simple-mind-map/types/src/core/render/node/Node.d.ts b/simple-mind-map/types/src/core/render/node/Node.d.ts new file mode 100644 index 00000000..0991864c --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/Node.d.ts @@ -0,0 +1,118 @@ +export default Node; +declare class Node { + constructor(opt?: {}); + nodeData: any; + uid: any; + mindMap: any; + renderer: any; + draw: any; + style: Style; + shapeInstance: Shape; + shapePadding: { + paddingX: number; + paddingY: number; + }; + isRoot: any; + isGeneralization: any; + generalizationBelongNode: any; + layerIndex: any; + width: any; + height: any; + _left: any; + _top: any; + customLeft: any; + customTop: any; + isDrag: boolean; + parent: any; + children: any; + group: any; + shapeNode: any; + hoverNode: any; + _customNodeContent: any; + _imgData: any; + _iconData: any; + _textData: any; + _hyperlinkData: any; + _tagData: any; + _noteData: any; + noteEl: any; + _expandBtn: any; + _lastExpandBtnType: any; + _showExpandBtn: boolean; + _openExpandNode: any; + _closeExpandNode: any; + _fillExpandNode: any; + _lines: any[]; + _generalizationLine: any; + _generalizationNode: any; + _unVisibleRectRegionNode: any; + _isMouseenter: boolean; + _rectInfo: { + imgContentWidth: number; + imgContentHeight: number; + textContentWidth: number; + textContentHeight: number; + }; + _generalizationNodeWidth: number; + _generalizationNodeHeight: number; + textContentItemMargin: any; + blockContentMargin: any; + expandBtnSize: any; + isMultipleChoice: boolean; + needLayout: boolean; + isHide: boolean; + set left(arg: any); + get left(): any; + set top(arg: any); + get top(): any; + reset(): void; + handleData(data: any): any; + createNodeData(): void; + getSize(): boolean; + getNodeRect(): { + width: any; + height: any; + }; + layout(): void; + bindGroupEvent(): void; + active(e: any): void; + update(): void; + getNodePosInClient(_left: any, _top: any): { + left: any; + top: any; + }; + reRender(): boolean; + updateNodeActive(): void; + render(callback?: () => void): void; + remove(): void; + destroy(): void; + hide(): void; + show(): void; + setOpacity(val: any): void; + hideChildren(): void; + showChildren(): void; + startDrag(): void; + endDrag(): void; + renderLine(deep?: boolean): void; + getShape(): any; + hasCustomPosition(): boolean; + ancestorHasCustomPosition(): boolean; + addChildren(node: any): void; + styleLine(line: any, node: any): void; + removeLine(): void; + isParent(node: any): boolean; + isBrother(node: any): any; + getPaddingVale(): { + paddingX: any; + paddingY: any; + }; + getStyle(prop: any, root: any): any; + getSelfStyle(prop: any): any; + getParentSelfStyle(prop: any): any; + getSelfInhertStyle(prop: any): any; + getBorderWidth(): any; + getData(key: any): any; + hasCustomStyle(): boolean; +} +import Style from './Style'; +import Shape from './Shape'; diff --git a/simple-mind-map/types/src/core/render/node/Shape.d.ts b/simple-mind-map/types/src/core/render/node/Shape.d.ts new file mode 100644 index 00000000..6fbf5189 --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/Shape.d.ts @@ -0,0 +1,23 @@ +export default class Shape { + constructor(node: any); + node: any; + getShapePadding(width: any, height: any, paddingX: any, paddingY: any): { + paddingX: number; + paddingY: number; + }; + createShape(): any; + getNodeSize(): { + width: any; + height: any; + }; + createRect(): any; + createDiamond(): any; + createParallelogram(): any; + createRoundedRectangle(): any; + createOctagonalRectangle(): any; + createOuterTriangularRectangle(): any; + createInnerTriangularRectangle(): any; + createEllipse(): any; + createCircle(): any; +} +export const shapeList: string[]; diff --git a/simple-mind-map/types/src/core/render/node/Style.d.ts b/simple-mind-map/types/src/core/render/node/Style.d.ts new file mode 100644 index 00000000..3c78574f --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/Style.d.ts @@ -0,0 +1,36 @@ +export default Style; +declare class Style { + static setBackgroundStyle(el: any, themeConfig: any): void; + static removeBackgroundStyle(el: any): void; + constructor(ctx: any); + ctx: any; + merge(prop: any, root: any): any; + getStyle(prop: any, root: any): any; + getSelfStyle(prop: any): any; + rect(node: any): void; + shape(node: any): void; + text(node: any): void; + createStyleText(): string; + getTextFontStyle(): { + italic: boolean; + bold: any; + fontSize: any; + fontFamily: any; + }; + domText(node: any, fontSizeScale: number, isMultiLine: any): void; + tagText(node: any): void; + tagRect(node: any, text: any, color: any): void; + iconNode(node: any): void; + line(node: any, { width, color, dasharray }?: { + width: any; + color: any; + dasharray: any; + }): void; + generalizationLine(node: any): void; + iconBtn(node: any, node2: any, fillNode: any): void; + hasCustomStyle(): boolean; + hoverNode(node: any): void; +} +declare namespace Style { + let cacheStyle: any; +} diff --git a/simple-mind-map/types/src/core/render/node/nodeCommandWraps.d.ts b/simple-mind-map/types/src/core/render/node/nodeCommandWraps.d.ts new file mode 100644 index 00000000..3c2c2d77 --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/nodeCommandWraps.d.ts @@ -0,0 +1,23 @@ +declare namespace _default { + export { setData }; + export { setText }; + export { setImage }; + export { setIcon }; + export { setHyperlink }; + export { setNote }; + export { setTag }; + export { setShape }; + export { setStyle }; + export { setStyles }; +} +export default _default; +declare function setData(data?: {}): void; +declare function setText(text: any, richText: any, resetRichText: any): void; +declare function setImage(imgData: any): void; +declare function setIcon(icons: any): void; +declare function setHyperlink(link: any, title: any): void; +declare function setNote(note: any): void; +declare function setTag(tag: any): void; +declare function setShape(shape: any): void; +declare function setStyle(prop: any, value: any): void; +declare function setStyles(style: any): void; diff --git a/simple-mind-map/types/src/core/render/node/nodeCreateContents.d.ts b/simple-mind-map/types/src/core/render/node/nodeCreateContents.d.ts new file mode 100644 index 00000000..9f74a320 --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/nodeCreateContents.d.ts @@ -0,0 +1,45 @@ +declare namespace _default { + export { createImgNode }; + export { getImgShowSize }; + export { createIconNode }; + export { createRichTextNode }; + export { createTextNode }; + export { createHyperlinkNode }; + export { createTagNode }; + export { createNoteNode }; + export { measureCustomNodeContentSize }; + export { isUseCustomNodeContent }; +} +export default _default; +declare function createImgNode(): { + node: any; + width: any; + height: any; +}; +declare function getImgShowSize(): any; +declare function createIconNode(): any; +declare function createRichTextNode(): { + node: any; + width: any; + height: any; +}; +declare function createTextNode(): any; +declare function createHyperlinkNode(): { + node: any; + width: any; + height: any; +}; +declare function createTagNode(): any[]; +declare function createNoteNode(): { + node: any; + width: any; + height: any; +}; +declare class createNoteNode { + noteEl: HTMLDivElement; +} +declare function measureCustomNodeContentSize(content: any): { + width: any; + height: any; +}; +declare function isUseCustomNodeContent(): boolean; diff --git a/simple-mind-map/types/src/core/render/node/nodeExpandBtn.d.ts b/simple-mind-map/types/src/core/render/node/nodeExpandBtn.d.ts new file mode 100644 index 00000000..676a54f2 --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/nodeExpandBtn.d.ts @@ -0,0 +1,34 @@ +declare namespace _default { + export { createExpandNodeContent }; + export { updateExpandBtnNode }; + export { updateExpandBtnPos }; + export { renderExpandBtn }; + export { removeExpandBtn }; + export { showExpandBtn }; + export { hideExpandBtn }; + export { sumNode }; +} +export default _default; +declare function createExpandNodeContent(): void; +declare class createExpandNodeContent { + _openExpandNode: any; + _closeExpandNode: any; + _fillExpandNode: any; +} +declare function updateExpandBtnNode(): void; +declare class updateExpandBtnNode { + _lastExpandBtnType: boolean; +} +declare function updateExpandBtnPos(): void; +declare function renderExpandBtn(): void; +declare class renderExpandBtn { + _expandBtn: any; + _showExpandBtn: boolean; +} +declare function removeExpandBtn(): void; +declare class removeExpandBtn { + _showExpandBtn: boolean; +} +declare function showExpandBtn(): void; +declare function hideExpandBtn(): void; +declare function sumNode(data?: any[]): any; diff --git a/simple-mind-map/types/src/core/render/node/nodeExpandBtnPlaceholderRect.d.ts b/simple-mind-map/types/src/core/render/node/nodeExpandBtnPlaceholderRect.d.ts new file mode 100644 index 00000000..ad23173a --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/nodeExpandBtnPlaceholderRect.d.ts @@ -0,0 +1,18 @@ +declare namespace _default { + export { renderExpandBtnPlaceholderRect }; + export { clearExpandBtnPlaceholderRect }; + export { updateExpandBtnPlaceholderRect }; +} +export default _default; +declare function renderExpandBtnPlaceholderRect(): void; +declare class renderExpandBtnPlaceholderRect { + _unVisibleRectRegionNode: any; +} +declare function clearExpandBtnPlaceholderRect(): void; +declare class clearExpandBtnPlaceholderRect { + _unVisibleRectRegionNode: any; +} +declare function updateExpandBtnPlaceholderRect(): void; +declare class updateExpandBtnPlaceholderRect { + needRerenderExpandBtnPlaceholderRect: boolean; +} diff --git a/simple-mind-map/types/src/core/render/node/nodeGeneralization.d.ts b/simple-mind-map/types/src/core/render/node/nodeGeneralization.d.ts new file mode 100644 index 00000000..9382d87d --- /dev/null +++ b/simple-mind-map/types/src/core/render/node/nodeGeneralization.d.ts @@ -0,0 +1,32 @@ +declare namespace _default { + export { checkHasGeneralization }; + export { createGeneralizationNode }; + export { updateGeneralization }; + export { renderGeneralization }; + export { removeGeneralization }; + export { hideGeneralization }; + export { showGeneralization }; +} +export default _default; +declare function checkHasGeneralization(): boolean; +declare function createGeneralizationNode(): void; +declare class createGeneralizationNode { + _generalizationLine: any; + _generalizationNode: Node; + _generalizationNodeWidth: any; + _generalizationNodeHeight: any; +} +declare function updateGeneralization(): void; +declare function renderGeneralization(): void; +declare class renderGeneralization { + _generalizationNodeWidth: number; + _generalizationNodeHeight: number; +} +declare function removeGeneralization(): void; +declare class removeGeneralization { + _generalizationLine: any; + _generalizationNode: any; +} +declare function hideGeneralization(): void; +declare function showGeneralization(): void; +import Node from './Node'; diff --git a/simple-mind-map/types/src/core/view/View.d.ts b/simple-mind-map/types/src/core/view/View.d.ts new file mode 100644 index 00000000..d9e887fe --- /dev/null +++ b/simple-mind-map/types/src/core/view/View.d.ts @@ -0,0 +1,36 @@ +export default View; +declare class View { + constructor(opt?: {}); + opt: {}; + mindMap: any; + scale: number; + sx: number; + sy: number; + x: number; + y: number; + firstDrag: boolean; + bind(): void; + getTransformData(): { + transform: any; + state: { + scale: number; + x: number; + y: number; + sx: number; + sy: number; + }; + }; + setTransformData(viewData: any): void; + translateXY(x: any, y: any): void; + translateX(step: any): void; + translateXTo(x: any): void; + translateY(step: any): void; + translateYTo(y: any): void; + transform(): void; + reset(): void; + narrow(cx: any, cy: any, isTouchPad: any): void; + enlarge(cx: any, cy: any, isTouchPad: any): void; + scaleInCenter(scale: any, cx: any, cy: any): void; + setScale(scale: any, cx: any, cy: any): void; + fit(): void; +} diff --git a/simple-mind-map/types/src/layouts/Base.d.ts b/simple-mind-map/types/src/layouts/Base.d.ts new file mode 100644 index 00000000..bf03b37c --- /dev/null +++ b/simple-mind-map/types/src/layouts/Base.d.ts @@ -0,0 +1,43 @@ +export default Base; +declare class Base { + constructor(renderer: any); + renderer: any; + mindMap: any; + draw: any; + root: any; + lru: Lru; + doLayout(): void; + renderLine(): void; + renderExpandBtn(): void; + renderGeneralization(): void; + cacheNode(uid: any, node: any): void; + checkIsNeedResizeSources(): boolean; + checkIsLayerTypeChange(oldIndex: any, newIndex: any): boolean; + checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(node: any): void; + createNode(data: any, parent: any, isRoot: any, layerIndex: any): any; + formatPosition(value: any, size: any, nodeSize: any): number; + setNodeCenter(node: any): void; + updateChildren(children: any, prop: any, offset: any): void; + updateChildrenPro(children: any, props: any): void; + getNodeAreaWidth(node: any, withGeneralization?: boolean): number; + quadraticCurvePath(x1: any, y1: any, x2: any, y2: any): string; + cubicBezierPath(x1: any, y1: any, x2: any, y2: any): string; + getMarginX(layerIndex: any): any; + getMarginY(layerIndex: any): any; + getNodeWidthWithGeneralization(node: any): number; + getNodeHeightWithGeneralization(node: any): number; + /** + * dir:生长方向,h(水平)、v(垂直) + * isLeft:是否向左生长 + */ + getNodeBoundaries(node: any, dir: any): { + left: any; + right: any; + top: any; + bottom: any; + generalizationLineMargin: any; + generalizationNodeMargin: any; + }; + getNodeActChildrenLength(node: any): any; +} +import Lru from '../utils/Lru'; diff --git a/simple-mind-map/types/src/layouts/CatalogOrganization.d.ts b/simple-mind-map/types/src/layouts/CatalogOrganization.d.ts new file mode 100644 index 00000000..d95995c2 --- /dev/null +++ b/simple-mind-map/types/src/layouts/CatalogOrganization.d.ts @@ -0,0 +1,15 @@ +export default CatalogOrganization; +declare class CatalogOrganization extends Base { + constructor(opt?: {}); + doLayout(callback: any): void; + computedBaseValue(): void; + computedLeftTopValue(): void; + adjustLeftTopValue(): void; + updateBrothersLeft(node: any, addWidth: any): void; + updateBrothersTop(node: any, addHeight: any): void; + renderLine(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/Fishbone.d.ts b/simple-mind-map/types/src/layouts/Fishbone.d.ts new file mode 100644 index 00000000..cc213aa8 --- /dev/null +++ b/simple-mind-map/types/src/layouts/Fishbone.d.ts @@ -0,0 +1,19 @@ +export default Fishbone; +declare class Fishbone extends Base { + constructor(opt?: {}); + indent: number; + childIndent: number; + doLayout(callback: any): void; + computedBaseValue(): void; + computedLeftTopValue(): void; + adjustLeftTopValue(): void; + getNodeAreaHeight(node: any): number; + updateBrothersLeft(node: any): void; + updateBrothersTop(node: any, addHeight: any): void; + checkIsTop(node: any): boolean; + renderLine(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/LogicalStructure.d.ts b/simple-mind-map/types/src/layouts/LogicalStructure.d.ts new file mode 100644 index 00000000..fcb0ea59 --- /dev/null +++ b/simple-mind-map/types/src/layouts/LogicalStructure.d.ts @@ -0,0 +1,17 @@ +export default LogicalStructure; +declare class LogicalStructure extends Base { + constructor(opt?: {}); + doLayout(callback: any): void; + computedBaseValue(): void; + computedTopValue(): void; + adjustTopValue(): void; + updateBrothers(node: any, addHeight: any): void; + renderLine(node: any, lines: any, style: any, lineStyle: any): void; + renderLineStraight(node: any, lines: any, style: any): any[]; + renderLineDirect(node: any, lines: any, style: any): any[]; + renderLineCurve(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/MindMap.d.ts b/simple-mind-map/types/src/layouts/MindMap.d.ts new file mode 100644 index 00000000..492d3b50 --- /dev/null +++ b/simple-mind-map/types/src/layouts/MindMap.d.ts @@ -0,0 +1,17 @@ +export default MindMap; +declare class MindMap extends Base { + constructor(opt?: {}); + doLayout(callback: any): void; + computedBaseValue(): void; + computedTopValue(): void; + adjustTopValue(): void; + updateBrothers(node: any, leftAddHeight: any, rightAddHeight: any): void; + renderLine(node: any, lines: any, style: any, lineStyle: any): void; + renderLineStraight(node: any, lines: any, style: any): any[]; + renderLineDirect(node: any, lines: any, style: any): any[]; + renderLineCurve(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/OrganizationStructure.d.ts b/simple-mind-map/types/src/layouts/OrganizationStructure.d.ts new file mode 100644 index 00000000..ae5961c2 --- /dev/null +++ b/simple-mind-map/types/src/layouts/OrganizationStructure.d.ts @@ -0,0 +1,16 @@ +export default OrganizationStructure; +declare class OrganizationStructure extends Base { + constructor(opt?: {}); + doLayout(callback: any): void; + computedBaseValue(): void; + computedLeftValue(): void; + adjustLeftValue(): void; + updateBrothers(node: any, addWidth: any): void; + renderLine(node: any, lines: any, style: any, lineStyle: any): void; + renderLineDirect(node: any, lines: any, style: any): any[]; + renderLineStraight(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/Timeline.d.ts b/simple-mind-map/types/src/layouts/Timeline.d.ts new file mode 100644 index 00000000..ccc17aa6 --- /dev/null +++ b/simple-mind-map/types/src/layouts/Timeline.d.ts @@ -0,0 +1,17 @@ +export default Timeline; +declare class Timeline extends Base { + constructor(opt: {}, layout: any); + layout: any; + doLayout(callback: any): void; + computedBaseValue(): void; + computedLeftTopValue(): void; + adjustLeftTopValue(): void; + getNodeAreaHeight(node: any): number; + updateBrothersLeft(node: any): void; + updateBrothersTop(node: any, addHeight: any): void; + renderLine(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/VerticalTimeline.d.ts b/simple-mind-map/types/src/layouts/VerticalTimeline.d.ts new file mode 100644 index 00000000..1127a694 --- /dev/null +++ b/simple-mind-map/types/src/layouts/VerticalTimeline.d.ts @@ -0,0 +1,19 @@ +export default VerticalTimeline; +declare class VerticalTimeline extends Base { + constructor(opt: {}, layout: any); + layout: any; + doLayout(callback: any): void; + computedBaseValue(): void; + computedTopValue(): void; + adjustLeftTopValue(): void; + updateBrothers(node: any, addHeight: any): void; + updateBrothersTop(node: any, addHeight: any): void; + renderLine(node: any, lines: any, style: any, lineStyle: any): void; + renderLineStraight(node: any, lines: any, style: any): any[]; + renderLineDirect(node: any, lines: any, style: any): any[]; + renderLineCurve(node: any, lines: any, style: any): any[]; + renderExpandBtn(node: any, btn: any): void; + renderGeneralization(node: any, gLine: any, gNode: any): void; + renderExpandBtnRect(rect: any, expandBtnSize: any, width: any, height: any, node: any): void; +} +import Base from './Base'; diff --git a/simple-mind-map/types/src/layouts/fishboneUtils.d.ts b/simple-mind-map/types/src/layouts/fishboneUtils.d.ts new file mode 100644 index 00000000..59e2ebde --- /dev/null +++ b/simple-mind-map/types/src/layouts/fishboneUtils.d.ts @@ -0,0 +1,77 @@ +declare namespace _default { + namespace top { + function renderExpandBtn({ node, btn, expandBtnSize, translateX, translateY, width, height }: { + node: any; + btn: any; + expandBtnSize: any; + translateX: any; + translateY: any; + width: any; + height: any; + }): void; + function renderLine({ node, line, top, x, lineLength, height, expandBtnSize, maxy, ctx }: { + node: any; + line: any; + top: any; + x: any; + lineLength: any; + height: any; + expandBtnSize: any; + maxy: any; + ctx: any; + }): void; + function computedLeftTopValue({ layerIndex, node, ctx }: { + layerIndex: any; + node: any; + ctx: any; + }): void; + function adjustLeftTopValueBefore({ node, parent, ctx, layerIndex }: { + node: any; + parent: any; + ctx: any; + layerIndex: any; + }): void; + function adjustLeftTopValueAfter({ parent, node, ctx }: { + parent: any; + node: any; + ctx: any; + }): void; + } + namespace bottom { + function renderExpandBtn({ node, btn, expandBtnSize, translateX, translateY, width, height }: { + node: any; + btn: any; + expandBtnSize: any; + translateX: any; + translateY: any; + width: any; + height: any; + }): void; + function renderLine({ node, line, top, x, lineLength, height, miny, ctx }: { + node: any; + line: any; + top: any; + x: any; + lineLength: any; + height: any; + miny: any; + ctx: any; + }): void; + function computedLeftTopValue({ layerIndex, node, ctx }: { + layerIndex: any; + node: any; + ctx: any; + }): void; + function adjustLeftTopValueBefore({ node, ctx, layerIndex }: { + node: any; + ctx: any; + layerIndex: any; + }): void; + function adjustLeftTopValueAfter({ parent, node, ctx }: { + parent: any; + node: any; + ctx: any; + }): void; + } +} +export default _default; diff --git a/simple-mind-map/types/src/svg/btns.d.ts b/simple-mind-map/types/src/svg/btns.d.ts new file mode 100644 index 00000000..ea00ede2 --- /dev/null +++ b/simple-mind-map/types/src/svg/btns.d.ts @@ -0,0 +1,11 @@ +declare namespace _default { + export { open }; + export { close }; + export { remove }; + export { imgAdjust }; +} +export default _default; +declare const open: ""; +declare const close: ""; +declare const remove: ""; +declare const imgAdjust: ""; diff --git a/simple-mind-map/types/src/svg/icons.d.ts b/simple-mind-map/types/src/svg/icons.d.ts new file mode 100644 index 00000000..7dc64759 --- /dev/null +++ b/simple-mind-map/types/src/svg/icons.d.ts @@ -0,0 +1,18 @@ +export const nodeIconList: { + name: string; + type: string; + list: { + name: string; + icon: string; + }[]; +}[]; +declare namespace _default { + export { hyperlink }; + export { note }; + export { nodeIconList }; + export { getNodeIconListIcon }; +} +export default _default; +declare const hyperlink: ""; +declare const note: ""; +declare function getNodeIconListIcon(name: any, extendIconList?: any[]): any; diff --git a/simple-mind-map/types/src/themes/autumn.d.ts b/simple-mind-map/types/src/themes/autumn.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/autumn.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/avocado.d.ts b/simple-mind-map/types/src/themes/avocado.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/avocado.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/blackGold.d.ts b/simple-mind-map/types/src/themes/blackGold.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/blackGold.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/blackHumour.d.ts b/simple-mind-map/types/src/themes/blackHumour.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/blackHumour.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/blueSky.d.ts b/simple-mind-map/types/src/themes/blueSky.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/blueSky.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/brainImpairedPink.d.ts b/simple-mind-map/types/src/themes/brainImpairedPink.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/brainImpairedPink.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/classic.d.ts b/simple-mind-map/types/src/themes/classic.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/classic.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/classic2.d.ts b/simple-mind-map/types/src/themes/classic2.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/classic2.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/classic3.d.ts b/simple-mind-map/types/src/themes/classic3.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/classic3.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/classic4.d.ts b/simple-mind-map/types/src/themes/classic4.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/classic4.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/classicBlue.d.ts b/simple-mind-map/types/src/themes/classicBlue.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/classicBlue.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/classicGreen.d.ts b/simple-mind-map/types/src/themes/classicGreen.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/classicGreen.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/coffee.d.ts b/simple-mind-map/types/src/themes/coffee.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/coffee.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/courseGreen.d.ts b/simple-mind-map/types/src/themes/courseGreen.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/courseGreen.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/dark.d.ts b/simple-mind-map/types/src/themes/dark.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/dark.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/dark2.d.ts b/simple-mind-map/types/src/themes/dark2.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/dark2.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/default.d.ts b/simple-mind-map/types/src/themes/default.d.ts new file mode 100644 index 00000000..5d31dd45 --- /dev/null +++ b/simple-mind-map/types/src/themes/default.d.ts @@ -0,0 +1,143 @@ +declare namespace _default { + let paddingX: number; + let paddingY: number; + let imgMaxWidth: number; + let imgMaxHeight: number; + let iconSize: number; + let lineWidth: number; + let lineColor: string; + let lineDasharray: string; + let lineStyle: string; + let rootLineKeepSameInCurve: boolean; + let generalizationLineWidth: number; + let generalizationLineColor: string; + let generalizationLineMargin: number; + let generalizationNodeMargin: number; + let associativeLineWidth: number; + let associativeLineColor: string; + let associativeLineActiveWidth: number; + let associativeLineActiveColor: string; + let associativeLineTextColor: string; + let associativeLineTextFontSize: number; + let associativeLineTextLineHeight: number; + let associativeLineTextFontFamily: string; + let backgroundColor: string; + let backgroundImage: string; + let backgroundRepeat: string; + let backgroundPosition: string; + let backgroundSize: string; + let nodeUseLineStyle: boolean; + namespace root { + let shape: string; + let fillColor: string; + let fontFamily: string; + let color: string; + let fontSize: number; + let fontWeight: string; + let fontStyle: string; + let lineHeight: number; + let borderColor: string; + let borderWidth: number; + let borderDasharray: string; + let borderRadius: number; + let textDecoration: string; + } + namespace second { + let shape_1: string; + export { shape_1 as shape }; + export let marginX: number; + export let marginY: number; + let fillColor_1: string; + export { fillColor_1 as fillColor }; + let fontFamily_1: string; + export { fontFamily_1 as fontFamily }; + let color_1: string; + export { color_1 as color }; + let fontSize_1: number; + export { fontSize_1 as fontSize }; + let fontWeight_1: string; + export { fontWeight_1 as fontWeight }; + let fontStyle_1: string; + export { fontStyle_1 as fontStyle }; + let lineHeight_1: number; + export { lineHeight_1 as lineHeight }; + let borderColor_1: string; + export { borderColor_1 as borderColor }; + let borderWidth_1: number; + export { borderWidth_1 as borderWidth }; + let borderDasharray_1: string; + export { borderDasharray_1 as borderDasharray }; + let borderRadius_1: number; + export { borderRadius_1 as borderRadius }; + let textDecoration_1: string; + export { textDecoration_1 as textDecoration }; + } + namespace node { + let shape_2: string; + export { shape_2 as shape }; + let marginX_1: number; + export { marginX_1 as marginX }; + let marginY_1: number; + export { marginY_1 as marginY }; + let fillColor_2: string; + export { fillColor_2 as fillColor }; + let fontFamily_2: string; + export { fontFamily_2 as fontFamily }; + let color_2: string; + export { color_2 as color }; + let fontSize_2: number; + export { fontSize_2 as fontSize }; + let fontWeight_2: string; + export { fontWeight_2 as fontWeight }; + let fontStyle_2: string; + export { fontStyle_2 as fontStyle }; + let lineHeight_2: number; + export { lineHeight_2 as lineHeight }; + let borderColor_2: string; + export { borderColor_2 as borderColor }; + let borderWidth_2: number; + export { borderWidth_2 as borderWidth }; + let borderRadius_2: number; + export { borderRadius_2 as borderRadius }; + let borderDasharray_2: string; + export { borderDasharray_2 as borderDasharray }; + let textDecoration_2: string; + export { textDecoration_2 as textDecoration }; + } + namespace generalization { + let shape_3: string; + export { shape_3 as shape }; + let marginX_2: number; + export { marginX_2 as marginX }; + let marginY_2: number; + export { marginY_2 as marginY }; + let fillColor_3: string; + export { fillColor_3 as fillColor }; + let fontFamily_3: string; + export { fontFamily_3 as fontFamily }; + let color_3: string; + export { color_3 as color }; + let fontSize_3: number; + export { fontSize_3 as fontSize }; + let fontWeight_3: string; + export { fontWeight_3 as fontWeight }; + let fontStyle_3: string; + export { fontStyle_3 as fontStyle }; + let lineHeight_3: number; + export { lineHeight_3 as lineHeight }; + let borderColor_3: string; + export { borderColor_3 as borderColor }; + let borderWidth_3: number; + export { borderWidth_3 as borderWidth }; + let borderDasharray_3: string; + export { borderDasharray_3 as borderDasharray }; + let borderRadius_3: number; + export { borderRadius_3 as borderRadius }; + let textDecoration_3: string; + export { textDecoration_3 as textDecoration }; + } +} +export default _default; +export const supportActiveStyle: string[]; +export function checkIsNodeSizeIndependenceConfig(config: any): boolean; +export const lineStyleProps: string[]; diff --git a/simple-mind-map/types/src/themes/earthYellow.d.ts b/simple-mind-map/types/src/themes/earthYellow.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/earthYellow.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/freshGreen.d.ts b/simple-mind-map/types/src/themes/freshGreen.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/freshGreen.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/freshRed.d.ts b/simple-mind-map/types/src/themes/freshRed.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/freshRed.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/gold.d.ts b/simple-mind-map/types/src/themes/gold.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/gold.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/greenLeaf.d.ts b/simple-mind-map/types/src/themes/greenLeaf.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/greenLeaf.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/index.d.ts b/simple-mind-map/types/src/themes/index.d.ts new file mode 100644 index 00000000..7c4ac003 --- /dev/null +++ b/simple-mind-map/types/src/themes/index.d.ts @@ -0,0 +1,67 @@ +declare namespace _default { + export { defaultTheme as default } + export { freshGreen } + export { blueSky } + export { brainImpairedPink } + export { romanticPurple } + export { freshRed } + export { earthYellow } + export { classic } + export { classic2 } + export { classic3 } + export { classic4 } + export { dark } + export { classicGreen } + export { classicBlue } + export { minions } + export { pinkGrape } + export { mint } + export { gold } + export { vitalityOrange } + export { greenLeaf } + export { dark2 } + export { skyGreen } + export { simpleBlack } + export { courseGreen } + export { coffee } + export { redSpirit } + export { blackHumour } + export { lateNightOffice } + export { blackGold } + export { avocado } + export { autumn } + export { orangeJuice } +} +export default _default +import defaultTheme from './default' +import freshGreen from './freshGreen' +import blueSky from './blueSky' +import brainImpairedPink from './brainImpairedPink' +import romanticPurple from './romanticPurple' +import freshRed from './freshRed' +import earthYellow from './earthYellow' +import classic from './classic' +import classic2 from './classic2' +import classic3 from './classic3' +import classic4 from './classic4' +import dark from './dark' +import classicGreen from './classicGreen' +import classicBlue from './classicBlue' +import minions from './minions' +import pinkGrape from './pinkGrape' +import mint from './mint' +import gold from './gold' +import vitalityOrange from './vitalityOrange' +import greenLeaf from './greenLeaf' +import dark2 from './dark2' +import skyGreen from './skyGreen' +import simpleBlack from './simpleBlack' +import courseGreen from './courseGreen' +import coffee from './coffee' +import redSpirit from './redSpirit' +import blackHumour from './blackHumour' +import lateNightOffice from './lateNightOffice' +import blackGold from './blackGold' +import avocado from './avocado' +import autumn from './autumn' +import orangeJuice from './orangeJuice' diff --git a/simple-mind-map/types/src/themes/lateNightOffice.d.ts b/simple-mind-map/types/src/themes/lateNightOffice.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/lateNightOffice.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/minions.d.ts b/simple-mind-map/types/src/themes/minions.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/minions.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/mint.d.ts b/simple-mind-map/types/src/themes/mint.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/mint.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/orangeJuice.d.ts b/simple-mind-map/types/src/themes/orangeJuice.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/orangeJuice.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/pinkGrape.d.ts b/simple-mind-map/types/src/themes/pinkGrape.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/pinkGrape.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/redSpirit.d.ts b/simple-mind-map/types/src/themes/redSpirit.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/redSpirit.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/romanticPurple.d.ts b/simple-mind-map/types/src/themes/romanticPurple.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/romanticPurple.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/simpleBlack.d.ts b/simple-mind-map/types/src/themes/simpleBlack.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/simpleBlack.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/skyGreen.d.ts b/simple-mind-map/types/src/themes/skyGreen.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/skyGreen.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/themes/vitalityOrange.d.ts b/simple-mind-map/types/src/themes/vitalityOrange.d.ts new file mode 100644 index 00000000..c461e215 --- /dev/null +++ b/simple-mind-map/types/src/themes/vitalityOrange.d.ts @@ -0,0 +1,2 @@ +declare const _default: any +export default _default diff --git a/simple-mind-map/types/src/utils/BatchExecution.d.ts b/simple-mind-map/types/src/utils/BatchExecution.d.ts new file mode 100644 index 00000000..453eda55 --- /dev/null +++ b/simple-mind-map/types/src/utils/BatchExecution.d.ts @@ -0,0 +1,8 @@ +export default BatchExecution; +declare class BatchExecution { + has: {}; + queue: any[]; + nextTick: any; + push(name: any, fn: any): void; + flush(): void; +} diff --git a/simple-mind-map/types/src/utils/Lru.d.ts b/simple-mind-map/types/src/utils/Lru.d.ts new file mode 100644 index 00000000..b4bfa4f6 --- /dev/null +++ b/simple-mind-map/types/src/utils/Lru.d.ts @@ -0,0 +1,10 @@ +export default class Lru { + constructor(max: any); + max: any; + size: number; + pool: Map; + add(key: any, value: any): void; + delete(key: any): void; + has(key: any): boolean; + get(key: any): any; +} diff --git a/simple-mind-map/types/src/utils/index.d.ts b/simple-mind-map/types/src/utils/index.d.ts new file mode 100644 index 00000000..615e56b7 --- /dev/null +++ b/simple-mind-map/types/src/utils/index.d.ts @@ -0,0 +1,69 @@ +export function walk(root: any, parent: any, beforeCallback: any, afterCallback: any, isRoot: any, layerIndex?: number, index?: number): void; +export function bfsWalk(root: any, callback: any): void; +export function resizeImgSizeByOriginRatio(width: any, height: any, newWidth: any, newHeight: any): any[]; +export function resizeImgSize(width: any, height: any, maxWidth: any, maxHeight: any): any[]; +export function resizeImg(imgUrl: any, maxWidth: any, maxHeight: any): Promise; +export function getStrWithBrFromHtml(str: any): any; +export function simpleDeepClone(data: any): any; +export function copyRenderTree(tree: any, root: any, removeActiveState?: boolean): any; +export function copyNodeTree(tree: any, root: any, removeActiveState?: boolean, keepId?: boolean): any; +export function imgToDataUrl(src: any): Promise; +export function parseDataUrl(data: any): any; +export function downloadFile(file: any, fileName: any): void; +export function throttle(fn: any, time: number, ctx: any): (...args: any[]) => void; +export function asyncRun(taskList: any, callback?: () => void): void; +export function degToRad(deg: any): number; +export function camelCaseToHyphen(str: any): any; +export function measureText(text: any, { italic, bold, fontSize, fontFamily }: { + italic: any; + bold: any; + fontSize: any; + fontFamily: any; +}): { + width: any; + height: any; +}; +export function joinFontStr({ italic, bold, fontSize, fontFamily }: { + italic: any; + bold: any; + fontSize: any; + fontFamily: any; +}): string; +export function nextTick(fn: any, ctx: any): () => void; +export function checkNodeOuter(mindMap: any, node: any): { + isOuter: boolean; + offsetLeft: number; + offsetTop: number; +}; +export function getTextFromHtml(html: any): any; +export function readBlob(blob: any): Promise; +export function nodeToHTML(node: any): any; +export function getImageSize(src: any): Promise; +export function createUid(): any; +export function loadImage(imgFile: any): Promise; +export function removeHTMLEntities(str: any): any; +export function getType(data: any): any; +export function isUndef(data: any): boolean; +export function removeHtmlStyle(html: any): any; +export function addHtmlStyle(html: any, tag: any, style: any): any; +export function checkIsRichText(str: any): boolean; +export function replaceHtmlText(html: any, searchText: any, replaceText: any): any; +export function isWhite(color: any): boolean; +export function isTransparent(color: any): boolean; +export function getVisibleColorFromTheme(themeConfig: any): any; +export function nodeRichTextToTextWithWrap(html: any): string; +export function textToNodeRichTextWithWrap(html: any): string; +export function isMobile(): boolean; +export function getObjectChangedProps(oldObject: any, newObject: any): {}; +export function checkIsNodeStyleDataKey(key: any): boolean; +export function mergerIconList(list: any): any; +export function getTopAncestorsFomNodeList(list: any): any[]; +export function checkTwoRectIsOverlap(minx1: any, maxx1: any, miny1: any, maxy1: any, minx2: any, maxx2: any, miny2: any, maxy2: any): boolean; +export function focusInput(el: any): void; +export function selectAllInput(el: any): void; +export function addDataToAppointNodes(appointNodes: any, data?: {}): any; +export function createUidForAppointNodes(appointNodes: any): any; +export function formatDataToArray(data: any): any[]; +export function getNodeIndex(node: any): any; +export function generateColorByContent(str: any): string; +export function htmlEscape(str: any): any; diff --git a/simple-mind-map/types/src/utils/mersenneTwister.d.ts b/simple-mind-map/types/src/utils/mersenneTwister.d.ts new file mode 100644 index 00000000..30222088 --- /dev/null +++ b/simple-mind-map/types/src/utils/mersenneTwister.d.ts @@ -0,0 +1,23 @@ +/** + * @description 为了保证相同的内容每次生成的随机数都是一样的,我们可以使用一个伪随机数生成器(PRNG),并使用内容的哈希值作为种子。以下是一个使用Mersenne Twister算法的PRNG的实现: + * + * @param {*} seed + */ +export default function MersenneTwister(seed: any): void; +export default class MersenneTwister { + /** + * @description 为了保证相同的内容每次生成的随机数都是一样的,我们可以使用一个伪随机数生成器(PRNG),并使用内容的哈希值作为种子。以下是一个使用Mersenne Twister算法的PRNG的实现: + * + * @param {*} seed + */ + constructor(seed: any); + N: number; + M: number; + MATRIX_A: number; + UPPER_MASK: number; + LOWER_MASK: number; + mt: any[]; + mti: number; + init_genrand(s: any): void; + genrand_int32(): number; +} diff --git a/web/package-lock.json b/web/package-lock.json index 4446e072..80f9c0e6 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "thoughts", - "version": "0.5.0", + "version": "0.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "thoughts", - "version": "0.5.0", + "version": "0.6.0", "hasInstallScript": true, "dependencies": { "@toast-ui/editor": "^3.1.5", @@ -20833,7 +20833,6 @@ "version": "4.5.19", "dev": true, "requires": { - "@babel/core": "^7.11.0", "@babel/helper-compilation-targets": "^7.9.6", "@babel/helper-module-imports": "^7.8.3", "@babel/plugin-proposal-class-properties": "^7.8.3", @@ -20846,7 +20845,6 @@ "@vue/babel-plugin-jsx": "^1.0.3", "@vue/babel-preset-jsx": "^1.2.4", "babel-plugin-dynamic-import-node": "^2.3.3", - "core-js": "^3.6.5", "core-js-compat": "^3.6.5", "semver": "^6.1.0" } diff --git a/web/package.json b/web/package.json index 571c2e03..3e03ee31 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "thoughts", - "version": "0.6.0", + "version": "0.7.0", "private": true, "description": "一个简洁的思维导图", "author": "街角小林<1013335014@qq.com>", diff --git a/web/src/assets/avatar/乙.jpg b/web/src/assets/avatar/乙.jpg new file mode 100644 index 00000000..d68995ba Binary files /dev/null and b/web/src/assets/avatar/乙.jpg differ diff --git a/web/src/assets/avatar/敏.jpg b/web/src/assets/avatar/敏.jpg new file mode 100644 index 00000000..756c3193 Binary files /dev/null and b/web/src/assets/avatar/敏.jpg differ diff --git a/web/src/assets/avatar/沐风牧草.jpg b/web/src/assets/avatar/沐风牧草.jpg new file mode 100644 index 00000000..201827a1 Binary files /dev/null and b/web/src/assets/avatar/沐风牧草.jpg differ diff --git a/web/src/assets/avatar/蜉蝣撼大叔.jpg b/web/src/assets/avatar/蜉蝣撼大叔.jpg new file mode 100644 index 00000000..24ef2f3d Binary files /dev/null and b/web/src/assets/avatar/蜉蝣撼大叔.jpg differ diff --git a/web/src/assets/icon-font/iconfont.css b/web/src/assets/icon-font/iconfont.css index dc17607d..4eae4d8c 100644 --- a/web/src/assets/icon-font/iconfont.css +++ b/web/src/assets/icon-font/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 2479351 */ - src: url('iconfont.woff2?t=1691822758372') format('woff2'), - url('iconfont.woff?t=1691822758372') format('woff'), - url('iconfont.ttf?t=1691822758372') format('truetype'); + src: url('iconfont.woff2?t=1695365666344') format('woff2'), + url('iconfont.woff?t=1695365666344') format('woff'), + url('iconfont.ttf?t=1695365666344') format('truetype'); } .iconfont { @@ -13,6 +13,10 @@ -moz-osx-font-smoothing: grayscale; } +.icongongshi:before { + content: "\e617"; +} + .icontouming:before { content: "\e60c"; } diff --git a/web/src/assets/icon-font/iconfont.ttf b/web/src/assets/icon-font/iconfont.ttf index 028f19ae..2cbfa9ba 100644 Binary files a/web/src/assets/icon-font/iconfont.ttf and b/web/src/assets/icon-font/iconfont.ttf differ diff --git a/web/src/assets/icon-font/iconfont.woff b/web/src/assets/icon-font/iconfont.woff index 3a255b1f..3b4512b7 100644 Binary files a/web/src/assets/icon-font/iconfont.woff and b/web/src/assets/icon-font/iconfont.woff differ diff --git a/web/src/assets/icon-font/iconfont.woff2 b/web/src/assets/icon-font/iconfont.woff2 index 972819c3..4ce591db 100644 Binary files a/web/src/assets/icon-font/iconfont.woff2 and b/web/src/assets/icon-font/iconfont.woff2 differ diff --git a/web/src/config/constant.js b/web/src/config/constant.js index 60c0bd49..782074a4 100644 --- a/web/src/config/constant.js +++ b/web/src/config/constant.js @@ -1,60 +1,83 @@ // 布局结构图片映射 export const layoutImgMap = { - logicalStructure: require('../assets/img/structures/logicalStructure.png'), - mindMap: require('../assets/img/structures/mindMap.png'), - organizationStructure: require('../assets/img/structures/organizationStructure.png'), - catalogOrganization: require('../assets/img/structures/catalogOrganization.png'), - timeline: require('../assets/img/structures/timeline.png'), - timeline2: require('../assets/img/structures/timeline2.png'), - fishbone: require('../assets/img/structures/fishbone.png'), - verticalTimeline: require('../assets/img/structures/verticalTimeline.png'), + logicalStructure: require('../assets/img/structures/logicalStructure.png'), + mindMap: require('../assets/img/structures/mindMap.png'), + organizationStructure: require('../assets/img/structures/organizationStructure.png'), + catalogOrganization: require('../assets/img/structures/catalogOrganization.png'), + timeline: require('../assets/img/structures/timeline.png'), + timeline2: require('../assets/img/structures/timeline2.png'), + fishbone: require('../assets/img/structures/fishbone.png'), + verticalTimeline: require('../assets/img/structures/verticalTimeline.png') } // 主题图片映射 export const themeMap = { - default: require('../assets/img/themes/default.jpg'), - classic: require('../assets/img/themes/classic.jpg'), - minions: require('../assets/img/themes/minions.jpg'), - pinkGrape: require('../assets/img/themes/pinkGrape.jpg'), - mint: require('../assets/img/themes/mint.jpg'), - gold: require('../assets/img/themes/gold.jpg'), - vitalityOrange: require('../assets/img/themes/vitalityOrange.jpg'), - greenLeaf: require('../assets/img/themes/greenLeaf.jpg'), - dark2: require('../assets/img/themes/dark2.jpg'), - skyGreen: require('../assets/img/themes/skyGreen.jpg'), - classic2: require('../assets/img/themes/classic2.jpg'), - classic3: require('../assets/img/themes/classic3.jpg'), - classic4: require('../assets/img/themes/classic4.jpg'), - classicGreen: require('../assets/img/themes/classicGreen.jpg'), - classicBlue: require('../assets/img/themes/classicBlue.jpg'), - blueSky: require('../assets/img/themes/blueSky.jpg'), - brainImpairedPink: require('../assets/img/themes/brainImpairedPink.jpg'), - dark: require('../assets/img/themes/dark.jpg'), - earthYellow: require('../assets/img/themes/earthYellow.jpg'), - freshGreen: require('../assets/img/themes/freshGreen.jpg'), - freshRed: require('../assets/img/themes/freshRed.jpg'), - romanticPurple: require('../assets/img/themes/romanticPurple.jpg'), - simpleBlack: require('../assets/img/themes/simpleBlack.jpg'), - courseGreen: require('../assets/img/themes/courseGreen.jpg'), - coffee: require('../assets/img/themes/coffee.jpg'), - redSpirit: require('../assets/img/themes/redSpirit.jpg'), - blackHumour: require('../assets/img/themes/blackHumour.jpg'), - lateNightOffice: require('../assets/img/themes/lateNightOffice.jpg'), - blackGold: require('../assets/img/themes/blackGold.jpg'), - autumn: require('../assets/img/themes/autumn.jpg'), - avocado: require('../assets/img/themes/avocado.jpg'), - orangeJuice: require('../assets/img/themes/orangeJuice.jpg'), - oreo: require('../assets/img/themes/oreo.jpg'), - shallowSea: require('../assets/img/themes/shallowSea.jpg'), - lemonBubbles: require('../assets/img/themes/lemonBubbles.jpg'), - rose: require('../assets/img/themes/rose.jpg'), - seaBlueLine: require('../assets/img/themes/seaBlueLine.jpg'), - neonLamp: require('../assets/img/themes/neonLamp.jpg'), - darkNightLceBlade: require('../assets/img/themes/darkNightLceBlade.jpg'), - morandi: require('../assets/img/themes/morandi.jpg'), - classic5: require('../assets/img/themes/classic5.jpg'), - dark3: require('../assets/img/themes/dark3.jpg'), - dark4: require('../assets/img/themes/dark4.jpg'), - cactus: require('../assets/img/themes/cactus.jpg'), + default: require('../assets/img/themes/default.jpg'), + classic: require('../assets/img/themes/classic.jpg'), + minions: require('../assets/img/themes/minions.jpg'), + pinkGrape: require('../assets/img/themes/pinkGrape.jpg'), + mint: require('../assets/img/themes/mint.jpg'), + gold: require('../assets/img/themes/gold.jpg'), + vitalityOrange: require('../assets/img/themes/vitalityOrange.jpg'), + greenLeaf: require('../assets/img/themes/greenLeaf.jpg'), + dark2: require('../assets/img/themes/dark2.jpg'), + skyGreen: require('../assets/img/themes/skyGreen.jpg'), + classic2: require('../assets/img/themes/classic2.jpg'), + classic3: require('../assets/img/themes/classic3.jpg'), + classic4: require('../assets/img/themes/classic4.jpg'), + classicGreen: require('../assets/img/themes/classicGreen.jpg'), + classicBlue: require('../assets/img/themes/classicBlue.jpg'), + blueSky: require('../assets/img/themes/blueSky.jpg'), + brainImpairedPink: require('../assets/img/themes/brainImpairedPink.jpg'), + dark: require('../assets/img/themes/dark.jpg'), + earthYellow: require('../assets/img/themes/earthYellow.jpg'), + freshGreen: require('../assets/img/themes/freshGreen.jpg'), + freshRed: require('../assets/img/themes/freshRed.jpg'), + romanticPurple: require('../assets/img/themes/romanticPurple.jpg'), + simpleBlack: require('../assets/img/themes/simpleBlack.jpg'), + courseGreen: require('../assets/img/themes/courseGreen.jpg'), + coffee: require('../assets/img/themes/coffee.jpg'), + redSpirit: require('../assets/img/themes/redSpirit.jpg'), + blackHumour: require('../assets/img/themes/blackHumour.jpg'), + lateNightOffice: require('../assets/img/themes/lateNightOffice.jpg'), + blackGold: require('../assets/img/themes/blackGold.jpg'), + autumn: require('../assets/img/themes/autumn.jpg'), + avocado: require('../assets/img/themes/avocado.jpg'), + orangeJuice: require('../assets/img/themes/orangeJuice.jpg'), + oreo: require('../assets/img/themes/oreo.jpg'), + shallowSea: require('../assets/img/themes/shallowSea.jpg'), + lemonBubbles: require('../assets/img/themes/lemonBubbles.jpg'), + rose: require('../assets/img/themes/rose.jpg'), + seaBlueLine: require('../assets/img/themes/seaBlueLine.jpg'), + neonLamp: require('../assets/img/themes/neonLamp.jpg'), + darkNightLceBlade: require('../assets/img/themes/darkNightLceBlade.jpg'), + morandi: require('../assets/img/themes/morandi.jpg'), + classic5: require('../assets/img/themes/classic5.jpg'), + dark3: require('../assets/img/themes/dark3.jpg'), + dark4: require('../assets/img/themes/dark4.jpg'), + cactus: require('../assets/img/themes/cactus.jpg') } - \ No newline at end of file + +// 公式列表 +export const formulaList = [ + 'a^2', + 'a_2', + 'a^{2+2}', + 'a_{i,j}', + 'x_2^3', + '\\overbrace{1+2+\\cdots+100}', + '\\sum_{k=1}^N k^2', + '\\lim_{n \\to \\infty}x_n', + '\\int_{-N}^{N} e^x\\, dx', + '\\sqrt{3}', + '\\sqrt[n]{3}', + '\\sin\\theta', + '\\log X', + '\\log_{10}', + '\\log_\\alpha X', + '\\lim_{t\\to n}T', + '\\frac{1}{2}=0.5', + '\\binom{n}{k}', + '\\begin{matrix}x & y \\\\z & v\\end{matrix}', + '\\begin{cases}3x + 5y + z \\\\7x - 2y + 4z \\\\-6x + 3y + 2z\\end{cases}' +] diff --git a/web/src/config/icon.js b/web/src/config/icon.js index 4704486d..3d27cd86 100644 --- a/web/src/config/icon.js +++ b/web/src/config/icon.js @@ -57,7 +57,7 @@ const weekdayList = [ 'data:image/png;base64,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', 'data:image/png;base64,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', 'data:image/png;base64,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', - 'data:image/png;base64,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', + 'data:image/png;base64,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' ] const avatarList = [ diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js index c527b365..65367544 100644 --- a/web/src/lang/en_us.js +++ b/web/src/lang/en_us.js @@ -104,11 +104,13 @@ export default { markdownFile: 'markdown file', tips: 'tips: .smm and .json file can be import', isTransparent: 'Background is transparent', - pngTips: 'tips: Exporting pictures in rich text mode is time-consuming. It is recommended to export to svg format', + pngTips: + 'tips: Exporting pictures in rich text mode is time-consuming. It is recommended to export to svg format', svgTips: 'tips: Exporting pictures in rich text mode is time-consuming', transformingDomToImages: 'Converting nodes: ', notifyTitle: 'Info', - notifyMessage: 'If the download is not triggered, check whether it is blocked by the browser', + notifyMessage: + 'If the download is not triggered, check whether it is blocked by the browser', paddingX: 'Padding x', paddingY: 'Padding y', useMultiPageExport: 'Export multi page' @@ -215,15 +217,19 @@ export default { shortcutKey: 'Shortcut key', associativeLine: 'Associative line', save: 'Save', - painter: 'Painter' + painter: 'Painter', + formula: 'Formula' }, edit: { newFeatureNoticeTitle: 'New feature reminder', - newFeatureNoticeMessage: 'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.' + newFeatureNoticeMessage: + 'This update supports node rich text editing, But there are some defects, The most important impact is that the time to export the image is proportional to the number of nodes, Therefore, if you are more dependent on export requirements, you can use【Base style】-【Other config】-【Enable node rich text editing】Set to turn off rich text editing mode.' }, mouseAction: { - tip1: 'Current: Left click to drag the canvas, right click to box select nodes', - tip2: 'Current: Left click to box select nodes, right click to drag the canvas', + tip1: + 'Current: Left click to drag the canvas, right click to box select nodes', + tip2: + 'Current: Left click to box select nodes, right click to drag the canvas' }, search: { searchPlaceholder: 'Please enter the search content', @@ -231,5 +237,16 @@ export default { replace: 'Replace', replaceAll: 'Replace all', cancel: 'Cancel' + }, + nodeIconSidebar: { + title: 'Icon/Sticker', + icon: 'Icon', + sticker: 'Sticker' + }, + formulaSidebar: { + title: 'Formula', + placeholder: 'Please enter LaText syntax', + confirm: 'Confirm', + common: 'Common formulas' } } diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js index 3383a87e..a13fc37c 100644 --- a/web/src/lang/zh_cn.js +++ b/web/src/lang/zh_cn.js @@ -215,15 +215,17 @@ export default { shortcutKey: '快捷键', associativeLine: '关联线', save: '保存', - painter: '格式刷' + painter: '格式刷', + formula: '公式' }, edit: { newFeatureNoticeTitle: '新特性提醒', - newFeatureNoticeMessage: '本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。' + newFeatureNoticeMessage: + '本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。' }, mouseAction: { tip1: '当前:左键拖动画布,右键框选节点', - tip2: '当前:左键框选节点,右键拖动画布', + tip2: '当前:左键框选节点,右键拖动画布' }, search: { searchPlaceholder: '请输入查找内容', @@ -231,5 +233,16 @@ export default { replace: '替换', replaceAll: '全部替换', cancel: '取消' + }, + nodeIconSidebar: { + title: '图标/贴纸', + icon: '图标', + sticker: '贴纸' + }, + formulaSidebar: { + title: '公式', + placeholder: '请输入 LaText 语法', + confirm: '完成', + common: '常用公式' } } diff --git a/web/src/pages/Doc/catalogList.js b/web/src/pages/Doc/catalogList.js index f98610d9..a774b045 100644 --- a/web/src/pages/Doc/catalogList.js +++ b/web/src/pages/Doc/catalogList.js @@ -35,11 +35,12 @@ let APIList = [ 'search', 'painter', 'scrollbar', + 'formula', 'xmind', 'markdown', 'utils' ] -let helpList = new Array(2).fill(0).map((_, index) => { +let helpList = new Array(5).fill(0).map((_, index) => { return 'help' + (index + 1) }) diff --git a/web/src/pages/Doc/components/Header.vue b/web/src/pages/Doc/components/Header.vue index 59bb265d..251e4bbf 100644 --- a/web/src/pages/Doc/components/Header.vue +++ b/web/src/pages/Doc/components/Header.vue @@ -2,16 +2,24 @@
- + SimpleMindMap
+
{{ index }}
{{ demoName }}
+
+ {{ helpDoc }} +
+
+ {{ devDoc }} +
{{ currentLangName }} @@ -41,6 +49,7 @@ import t from '../i18n' export default { data() { return { + docType: '', lang: '', currentLangName: '', otherLangList: [] @@ -49,7 +58,16 @@ export default { computed: { demoName() { return t('demo', this.lang) - } + }, + helpDoc() { + return t('help', this.lang) + }, + devDoc() { + return t('dev', this.lang) + }, + index() { + return t('index', this.lang) + }, }, watch: { $route() { @@ -61,6 +79,12 @@ export default { }, methods: { init() { + // 当前文档类型 + let docType = /^\/([^\/]+)\//.exec(this.$route.path) + if (docType && docType[1]) { + this.docType = docType[1] + } + // 当前文档语言 let lang = /^\/doc\/([^\/]+)\//.exec(this.$route.path) if (lang && lang[1]) { this.lang = lang[1] @@ -87,6 +111,15 @@ export default { return `/doc/${path}/` }) this.$router.push(url) + }, + + toHelp() { + this.lang = 'zh' + this.$router.replace('/help/zh/') + }, + + toDev() { + this.$router.replace('/doc/zh/') } } } @@ -137,6 +170,10 @@ export default { &:hover { color: #1ea59a; } + + &.active { + color: #1ea59a; + } } .translateBtn { diff --git a/web/src/pages/Doc/en/changelog/index.md b/web/src/pages/Doc/en/changelog/index.md index 19a4a42d..3f9372ec 100644 --- a/web/src/pages/Doc/en/changelog/index.md +++ b/web/src/pages/Doc/en/changelog/index.md @@ -1,5 +1,91 @@ # Changelog +## 0.7.2 + +Fix: + +> 1.Fixed the issue of nodes not being selected when the selection area does not include node boundaries when multiple nodes are selected. +> +> 2.Fix the issue of errors when node text is not a string. +> +> 3.Fix the issue of some text disappearing when edited again when there are<>&characters in the text in non rich text mode. + +New: + +> 1.Supports inserting mathematical formulas. +> +> 2.Supports dragging and moving multiple nodes simultaneously. +> +> 3.Supports copying and cutting multiple nodes simultaneously. +> +> 4.The node label color has been changed to be generated based on the label content, meaning that the same label content will generate the same color. +> +> 5.Optimize the insertion of child nodes: 1. When inserting child nodes into multiple nodes simultaneously, do not enter the editing state; 2. The newly inserted child node automatically enters the active state. +> +> 6.Optimize the insertion of sibling nodes: 1. Support the simultaneous insertion of sibling nodes into multiple nodes; 2. When calling the command to insert sibling nodes on the root node, no child nodes will be created. +> +> 7.Add a command to simultaneously insert multiple peers and multiple child nodes. +> +> 8.Changing the unique identifier of nodes from id to uid mainly affects the associated lines, which may not display properly in previous versions. +> +> 9.Optimize the logic of icon merging and support the expansion of icons under the internal classification of the library. +> +> 10.Associate line: 1. Double click the associate line to enter associate line text editing mode; If the associated line text is the default text, it will not be saved; 3. When there are active nodes, clicking on the associated line can directly activate the associated line. + +Demo: + +> 1.Fixed an issue where multiple nodes in the Zhixi mind map cannot be pasted when copying. +> +> 2.Add a sidebar for formula editing. + +## 0.7.1-fix.2 + +Fix: 1.Fix the issue of plugin registration method chain call reporting errors. + +New: + +> 1.Add a configuration option to adapt to the canvas size when the mind map is first loaded. +> +> 2.Add a command to automatically generate dts declaration files. + +## 0.7.1-fix.1 + +Fix: Fixed the issue of dragging nodes without excluding the child nodes of the dragged nodes. + +## 0.7.1 + +Fix: + +> 1.Fix the issue of unsaved associated line endpoints after changes. +> +> 2.Fix the issue of abnormal canvas scrolling when moving the mouse to the edge of multiple selected nodes when the distance from the top left corner of the canvas to the browser window is not 0. +> +> 3.Fix the issue of importing xmind file errors for nodes with empty titles. +> +> 4.Fix the issue where the exported xmind file prompts for corruption when opened on the latest version of xmind software. +> +> 5.Fix the issue where stickers cannot be displayed when exporting data with stickers in xmind format. +> +> 6.Fix the issue of node right-click event reporting errors when the select plugin is not registered. +> +> 7.There is no issue with removing duplicates in the method of registering plugins. + +New: + +> 1.Reconstruct node drag and drop logic: optimize drag and drop difficulties in some situations, adapt to various structures, and automatically move the canvas when the mouse moves to the edge of the canvas during drag and drop. +> +> 2.Reconstruct the scrollbar plugin to optimize the user experience. +> +> 3.Imperfect resolution of conflicts between logical structure diagrams, mind maps, directory organization diagrams, organization chart summaries, and nodes (the summaries should be rewritten or deleted later). +> +> 4.Activate adjacent nodes after deleting them. +> +> 5.In node data_ The starting field is considered a custom field. + +Demo: + +> 1.The page will display the current core library version number. + ## 0.7.0 Breaking change: Removed the section of node activation style in the theme file, Setting the activation style of nodes is no longer supported, and the activation effect has been changed to a unified node outer border style, while also supporting the mouse hover effect. diff --git a/web/src/pages/Doc/en/changelog/index.vue b/web/src/pages/Doc/en/changelog/index.vue index cdcf02fe..92e3f520 100644 --- a/web/src/pages/Doc/en/changelog/index.vue +++ b/web/src/pages/Doc/en/changelog/index.vue @@ -1,6 +1,63 @@ diff --git a/web/src/pages/Doc/en/node/index.md b/web/src/pages/Doc/en/node/index.md index 60bd459e..c607d23b 100644 --- a/web/src/pages/Doc/en/node/index.md +++ b/web/src/pages/Doc/en/node/index.md @@ -56,6 +56,26 @@ Whether the node is currently being dragged ## Methods +### setOpacity(val) + +> v0.7.2+ + +- `val`: Opacity value,0-1 + +Set node transparency, including connecting lines and child nodes. + +### hideChildren() + +> v0.7.2+ + +Hide subordinate nodes. + +### showChildren() + +> v0.7.2+ + +Display subordinate nodes. + ### hasCustomStyle() > v0.6.2+ diff --git a/web/src/pages/Doc/en/node/index.vue b/web/src/pages/Doc/en/node/index.vue index fc5f1ed9..7a89994f 100644 --- a/web/src/pages/Doc/en/node/index.vue +++ b/web/src/pages/Doc/en/node/index.vue @@ -31,6 +31,24 @@

Whether the node is currently being dragged

Methods

+

setOpacity(val)

+
+

v0.7.2+

+
+
    +
  • val: Opacity value,0-1
  • +
+

Set node transparency, including connecting lines and child nodes.

+

hideChildren()

+
+

v0.7.2+

+
+

Hide subordinate nodes.

+

showChildren()

+
+

v0.7.2+

+
+

Display subordinate nodes.

hasCustomStyle()

v0.6.2+

diff --git a/web/src/pages/Doc/en/scrollbar/index.md b/web/src/pages/Doc/en/scrollbar/index.md index 10974f51..6defb81e 100644 --- a/web/src/pages/Doc/en/scrollbar/index.md +++ b/web/src/pages/Doc/en/scrollbar/index.md @@ -1,8 +1,10 @@ # Scrollbar plugin > v0.7.0+ +> +> V0.7.1+has been refactored, and the following document is a new one. -This plugin is used to help develop the functionality of horizontal and vertical scrollbar. +This plugin is used to help develop the functionality of horizontal and vertical scrollbar. Please refer to the tutorial for detailed usage. ## Register @@ -16,9 +18,24 @@ After registration and instantiation of `MindMap`, the instance can be obtained ## Event -#### scrollbar_change +#### scrollbar_change(data) -Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar. +```js +{ + // Vertical scrollbar + vertical: { + top,// Top value, Percentage value + height// Scrollbar height, Percentage value + }, + // Horizontal scrollbar + horizontal: { + left,// Left value, Percentage value + width// Scrollbar width, Percentage value + } +} +``` + +Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar. Receive a parameter representing the latest scrollbar position and size information, which you can use to update the style of the scrollbar element. ## Method @@ -32,9 +49,9 @@ Set the size of the scroll bar container, which is the width of the container fo ### calculationScrollbar() -> You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element. +> Usually, you do not need to call this method. If the scroll bar is not updated when rendering for the first time, you can manually call this method to obtain the scroll bar data. > -> Generally, it is necessary to monitor scrollbar_change event, and then call it to update the scroll bar. +> You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element. Return value: @@ -53,7 +70,7 @@ Return value: } ``` -Obtain the size and position of the scroll bar, and you can set it to the scroll bar element based on the return value to achieve the effect of rendering and caring about the scroll bar. +Obtain the size and position of the scrollbar. ### onMousedown(e, type) @@ -61,4 +78,12 @@ Obtain the size and position of the scroll bar, and you can set it to the scroll - `type`: The type of scroll bar pressed, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。 -This method needs to be called when the mouse press event of the scrollbar element occurs. \ No newline at end of file +This method needs to be called when the mouse press event of the scrollbar element occurs. + +### onClick(e, type) + +- `e`:The event object for the mouse click event. + +- `type`:The type of scroll bar on click, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。 + +This method needs to be called when the click event of the scrollbar element is triggered. \ No newline at end of file diff --git a/web/src/pages/Doc/en/scrollbar/index.vue b/web/src/pages/Doc/en/scrollbar/index.vue index d5851f33..e40463f6 100644 --- a/web/src/pages/Doc/en/scrollbar/index.vue +++ b/web/src/pages/Doc/en/scrollbar/index.vue @@ -3,8 +3,9 @@

Scrollbar plugin

v0.7.0+

+

V0.7.1+has been refactored, and the following document is a new one.

-

This plugin is used to help develop the functionality of horizontal and vertical scrollbar.

+

This plugin is used to help develop the functionality of horizontal and vertical scrollbar. Please refer to the tutorial for detailed usage.

Register

import MindMap from 'simple-mind-map'
 import Scrollbar from 'simple-mind-map/src/plugins/Scrollbar.js'
@@ -12,8 +13,21 @@ MindMap.usePlugin(Scrollbar)
 

After registration and instantiation of MindMap, the instance can be obtained through mindMap.scrollbar.

Event

-

scrollbar_change

-

Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar.

+

scrollbar_change(data)

+
{
+    // Vertical scrollbar
+    vertical: {
+        top,// Top value, Percentage value
+        height// Scrollbar height, Percentage value
+    },
+    // Horizontal scrollbar
+    horizontal: {
+        left,// Left value, Percentage value
+        width// Scrollbar width, Percentage value
+    }
+}
+
+

Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar. Receive a parameter representing the latest scrollbar position and size information, which you can use to update the style of the scrollbar element.

Method

setScrollBarWrapSize(width, height)

    @@ -27,8 +41,8 @@ MindMap.usePlugin(Scrollbar)

    Set the size of the scroll bar container, which is the width of the container for horizontal scrollbars and the height of the container for vertical scrollbars. When your scrollbar container size changes, you need to call this method again.

    calculationScrollbar()

    +

    Usually, you do not need to call this method. If the scroll bar is not updated when rendering for the first time, you can manually call this method to obtain the scroll bar data.

    You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element.

    -

    Generally, it is necessary to monitor scrollbar_change event, and then call it to update the scroll bar.

    Return value:

    {
    @@ -44,7 +58,7 @@ MindMap.usePlugin(Scrollbar)
         }
     }
     
    -

    Obtain the size and position of the scroll bar, and you can set it to the scroll bar element based on the return value to achieve the effect of rendering and caring about the scroll bar.

    +

    Obtain the size and position of the scrollbar.

    onMousedown(e, type)

    • @@ -55,6 +69,16 @@ MindMap.usePlugin(Scrollbar)

    This method needs to be called when the mouse press event of the scrollbar element occurs.

    +

    onClick(e, type)

    +
      +
    • +

      e:The event object for the mouse click event.

      +
    • +
    • +

      type:The type of scroll bar on click, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。

      +
    • +
    +

    This method needs to be called when the click event of the scrollbar element is triggered.

diff --git a/web/src/pages/Doc/en/start/index.md b/web/src/pages/Doc/en/start/index.md index e76ddf9f..887c30f6 100644 --- a/web/src/pages/Doc/en/start/index.md +++ b/web/src/pages/Doc/en/start/index.md @@ -167,4 +167,14 @@ module. If you need it, you can try using other libraries to parse `xml` to ### Error `Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')` -The reason is that the installed version of `@svgdotjs/svg.js` is too high. You can manually reduce it to the version of `3.0.16`. \ No newline at end of file +The reason is that the installed version of `@svgdotjs/svg.js` is too high. You can manually reduce it to the version of `3.0.16`. + +### TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46 + +The following configurations can be added to the packaging configuration file: + +```js +resolve: { alias: { stream: "stream-browserify" } } +``` + +Different packaging tools may have different specific configurations, with the principle of excluding 'stream' dependencies. \ No newline at end of file diff --git a/web/src/pages/Doc/en/start/index.vue b/web/src/pages/Doc/en/start/index.vue index 6ad429f9..0d3dee1e 100644 --- a/web/src/pages/Doc/en/start/index.vue +++ b/web/src/pages/Doc/en/start/index.vue @@ -116,6 +116,11 @@ module. If you need it, you can try using other libraries to parse xmljson.

Error Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')

The reason is that the installed version of @svgdotjs/svg.js is too high. You can manually reduce it to the version of 3.0.16.

+

TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46

+

The following configurations can be added to the packaging configuration file:

+
resolve: { alias: { stream: "stream-browserify" } }
+
+

Different packaging tools may have different specific configurations, with the principle of excluding 'stream' dependencies.

diff --git a/web/src/pages/Doc/en/translate/index.md b/web/src/pages/Doc/en/translate/index.md index 3255000e..732939a1 100644 --- a/web/src/pages/Doc/en/translate/index.md +++ b/web/src/pages/Doc/en/translate/index.md @@ -1,4 +1,12 @@ -# Participate in translation +# Contribute + +## Participate in development + +If you want to contribute code, you can 'fork' this project and switch to the 'feature' branch for development. After development and testing, you can submit the 'pr' to the 'feature' branch of this project. When submitting, please try to submit functional files as much as possible. Do not submit unnecessary files. + +Before development, it is best to create a new 'issue' to describe the new features you want to add. We can have sufficient communication first, and when submitting a 'pr', please provide a detailed description of the features you are developing. + +## Participate in translation > Thanks for the first version English translation provided by [Emircan ERKUL](https://github.com/emircanerkul). > diff --git a/web/src/pages/Doc/en/translate/index.vue b/web/src/pages/Doc/en/translate/index.vue index 8c07be6b..9df8cc99 100644 --- a/web/src/pages/Doc/en/translate/index.vue +++ b/web/src/pages/Doc/en/translate/index.vue @@ -1,6 +1,10 @@ diff --git a/web/src/pages/Doc/zh/node/index.md b/web/src/pages/Doc/zh/node/index.md index 67706eb7..d3af436f 100644 --- a/web/src/pages/Doc/zh/node/index.md +++ b/web/src/pages/Doc/zh/node/index.md @@ -56,6 +56,26 @@ ## 方法 +### setOpacity(val) + +> v0.7.2+ + +- `val`:透明度,0-1 + +设置节点透明度,包括连接线和下级节点。 + +### hideChildren() + +> v0.7.2+ + +隐藏下级节点。 + +### showChildren() + +> v0.7.2+ + +显示下级节点。 + ### hasCustomStyle() > v0.6.2+ diff --git a/web/src/pages/Doc/zh/node/index.vue b/web/src/pages/Doc/zh/node/index.vue index 8642a410..acbf5a30 100644 --- a/web/src/pages/Doc/zh/node/index.vue +++ b/web/src/pages/Doc/zh/node/index.vue @@ -31,6 +31,24 @@

节点是否正在拖拽中

方法

+

setOpacity(val)

+
+

v0.7.2+

+
+
    +
  • val:透明度,0-1
  • +
+

设置节点透明度,包括连接线和下级节点。

+

hideChildren()

+
+

v0.7.2+

+
+

隐藏下级节点。

+

showChildren()

+
+

v0.7.2+

+
+

显示下级节点。

hasCustomStyle()

v0.6.2+

diff --git a/web/src/pages/Doc/zh/scrollbar/index.md b/web/src/pages/Doc/zh/scrollbar/index.md index 49a20d1e..9b28338c 100644 --- a/web/src/pages/Doc/zh/scrollbar/index.md +++ b/web/src/pages/Doc/zh/scrollbar/index.md @@ -1,8 +1,10 @@ # Scrollbar 插件 > v0.7.0+ +> +> v0.7.1+进行了重构,下面的文档为新文档。 -该插件用于帮助开发水平和垂直滚动条的功能。 +该插件用于帮助开发水平和垂直滚动条的功能。详细使用方式请参考教程。 ## 注册 @@ -16,9 +18,26 @@ MindMap.usePlugin(Scrollbar) ## 事件 -#### scrollbar_change +#### scrollbar_change(data) -当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。 +- `data`:滚动条数据,格式如下: + +```js +{ + // 垂直滚动条 + vertical: { + top,// 垂直滚动条的top值,百分比数值 + height// 垂直滚动条的高度,百分比数值 + }, + // 水平滚动条 + horizontal: { + left,// 水平滚动条的left值,百分比数值 + width// 水平滚动条的宽度,百分比数值 + } +} +``` + +当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。接收一个参数,代表当前最新的滚动条位置和大小信息,你可以使用它来更新滚动条元素的样式。 ## 方法 @@ -32,9 +51,9 @@ MindMap.usePlugin(Scrollbar) ### calculationScrollbar() -> 需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。 +> 通常你不需要调用该方法,如果初次渲染滚动条时滚动条没有更新,那么可以手动调用该方法获取滚动条数据。 > -> 一般需要监听scrollbar_change事件,然后调用该方法更新滚动条。 +> 需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。 返回值: @@ -53,7 +72,7 @@ MindMap.usePlugin(Scrollbar) } ``` -获取滚动条大小和位置,你可以根据返回值来设置到滚动条元素上,达到渲染和关心滚动条的效果。 +获取滚动条大小和位置。 ### onMousedown(e, type) @@ -61,4 +80,12 @@ MindMap.usePlugin(Scrollbar) - `type`:按下的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。 -滚动条元素的鼠标按下事件时需要调用该方法。 \ No newline at end of file +滚动条元素的鼠标按下事件时需要调用该方法。 + +### onClick(e, type) + +- `e`:鼠标点击事件的事件对象。 + +- `type`:鼠标点击的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。 + +滚动条元素的的点击事件时需要调用该方法。 \ No newline at end of file diff --git a/web/src/pages/Doc/zh/scrollbar/index.vue b/web/src/pages/Doc/zh/scrollbar/index.vue index fc06ba0f..7500aaea 100644 --- a/web/src/pages/Doc/zh/scrollbar/index.vue +++ b/web/src/pages/Doc/zh/scrollbar/index.vue @@ -3,8 +3,9 @@

Scrollbar 插件

v0.7.0+

+

v0.7.1+进行了重构,下面的文档为新文档。

-

该插件用于帮助开发水平和垂直滚动条的功能。

+

该插件用于帮助开发水平和垂直滚动条的功能。详细使用方式请参考教程。

注册

import MindMap from 'simple-mind-map'
 import Scrollbar from 'simple-mind-map/src/plugins/Scrollbar.js'
@@ -12,8 +13,24 @@ MindMap.usePlugin(Scrollbar)
 

注册完且实例化MindMap后可通过mindMap.scrollbar获取到该实例。

事件

-

scrollbar_change

-

当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。

+

scrollbar_change(data)

+
    +
  • data:滚动条数据,格式如下:
  • +
+
{
+    // 垂直滚动条
+    vertical: {
+        top,// 垂直滚动条的top值,百分比数值
+        height// 垂直滚动条的高度,百分比数值
+    },
+    // 水平滚动条
+    horizontal: {
+        left,// 水平滚动条的left值,百分比数值
+        width// 水平滚动条的宽度,百分比数值
+    }
+}
+
+

当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。接收一个参数,代表当前最新的滚动条位置和大小信息,你可以使用它来更新滚动条元素的样式。

方法

setScrollBarWrapSize(width, height)

    @@ -27,8 +44,8 @@ MindMap.usePlugin(Scrollbar)

    设置滚动条容器的大小,对于水平滚动条,即容器的宽度,对于垂直滚动条,即容器的高度。当你的滚动条容器尺寸改变时需要再次调用该方法。

    calculationScrollbar()

    +

    通常你不需要调用该方法,如果初次渲染滚动条时滚动条没有更新,那么可以手动调用该方法获取滚动条数据。

    需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。

    -

    一般需要监听scrollbar_change事件,然后调用该方法更新滚动条。

    返回值:

    {
    @@ -44,7 +61,7 @@ MindMap.usePlugin(Scrollbar)
         }
     }
     
    -

    获取滚动条大小和位置,你可以根据返回值来设置到滚动条元素上,达到渲染和关心滚动条的效果。

    +

    获取滚动条大小和位置。

    onMousedown(e, type)

    • @@ -55,6 +72,16 @@ MindMap.usePlugin(Scrollbar)

    滚动条元素的鼠标按下事件时需要调用该方法。

    +

    onClick(e, type)

    +
      +
    • +

      e:鼠标点击事件的事件对象。

      +
    • +
    • +

      type:鼠标点击的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。

      +
    • +
    +

    滚动条元素的的点击事件时需要调用该方法。

    diff --git a/web/src/pages/Doc/zh/start/index.md b/web/src/pages/Doc/zh/start/index.md index e6998a65..4adb7f3f 100644 --- a/web/src/pages/Doc/zh/start/index.md +++ b/web/src/pages/Doc/zh/start/index.md @@ -151,4 +151,14 @@ import MindMap from "simple-mind-map/dist/simpleMindMap.umd.min" ### 2.报错`Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')` -原因为安装的`@svgdotjs/svg.js`版本太高,手动降到`3.0.16`版本即可。 \ No newline at end of file +原因为安装的`@svgdotjs/svg.js`版本太高,手动降到`3.0.16`版本即可。 + +### 3.TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46 + +可以在打包配置文件中增加如下配置: + +```js +resolve: { alias: { stream: "stream-browserify" } } +``` + +不同的打包工具可能具体配置不一样,原理就是排除`stream`依赖。 \ No newline at end of file diff --git a/web/src/pages/Doc/zh/start/index.vue b/web/src/pages/Doc/zh/start/index.vue index 45a59f93..a2fc0c63 100644 --- a/web/src/pages/Doc/zh/start/index.vue +++ b/web/src/pages/Doc/zh/start/index.vue @@ -99,6 +99,11 @@ npm run build

    如果需要二次开发,也就是必须要使用未打包代码的话,如果你不需要解析xmind文件的话,可以去除xmind模块,如果需要的话那么可以尝试换成其他的解析xmljson的库。

    2.报错Getting bbox of element "text" is not possible: TypeError: Cannot read properties of undefined (reading 'apply')

    原因为安装的@svgdotjs/svg.js版本太高,手动降到3.0.16版本即可。

    +

    3.TypeError: Cannot read properties of undefined (reading 'prototype') at sax.js:222:46

    +

    可以在打包配置文件中增加如下配置:

    +
    resolve: { alias: { stream: "stream-browserify" } }
    +
    +

    不同的打包工具可能具体配置不一样,原理就是排除stream依赖。

    diff --git a/web/src/pages/Doc/zh/translate/index.md b/web/src/pages/Doc/zh/translate/index.md index e5fe5d79..3a415abe 100644 --- a/web/src/pages/Doc/zh/translate/index.md +++ b/web/src/pages/Doc/zh/translate/index.md @@ -1,4 +1,12 @@ -# 参与翻译 +# 贡献 + +## 参与开发 + +如果你想贡献代码的话可以`fork`本项目,然后切换到`feature`分支下进行开发,开发并测试完后可以提交`pr`到本项目的`feature`分支,提交时请尽量提交功能相关的文件,非必要的文件请勿提交。 + +在开发前最好通过新建一个`issue`来描述你想要新增的功能,我们可以先进行充分的沟通,在提交`pr`时请详细描述你开发的功能。 + +## 参与翻译 > 感谢[Emircan ERKUL](https://github.com/emircanerkul)提供的第一版英文翻译。 > diff --git a/web/src/pages/Doc/zh/translate/index.vue b/web/src/pages/Doc/zh/translate/index.vue index e4e10473..9507ef9a 100644 --- a/web/src/pages/Doc/zh/translate/index.vue +++ b/web/src/pages/Doc/zh/translate/index.vue @@ -1,6 +1,10 @@ @@ -44,6 +45,7 @@ import SearchPlugin from 'simple-mind-map/src/plugins/Search.js' import { downloadFile, readBlob } from 'simple-mind-map/src/utils/index' import Painter from 'simple-mind-map/src/plugins/Painter.js' import ScrollbarPlugin from 'simple-mind-map/src/plugins/Scrollbar.js' +import Formula from 'simple-mind-map/src/plugins/Formula.js' import OutlineSidebar from './OutlineSidebar' import Style from './Style' import BaseStyle from './BaseStyle' @@ -77,6 +79,7 @@ import { showLoading, hideLoading } from '@/utils/loading' import handleClipboardText from '@/utils/handleClipboardText' import Scrollbar from './Scrollbar.vue' import exampleData from 'simple-mind-map/example/exampleData' +import FormulaSidebar from './FormulaSidebar.vue' // 注册插件 MindMap.usePlugin(MiniMap) @@ -93,6 +96,7 @@ MindMap.usePlugin(MiniMap) .usePlugin(SearchPlugin) .usePlugin(Painter) .usePlugin(ScrollbarPlugin) + .usePlugin(Formula) // 注册自定义主题 customThemeList.forEach(item => { @@ -125,7 +129,8 @@ export default { NodeIconSidebar, NodeIconToolbar, OutlineEdit, - Scrollbar + Scrollbar, + FormulaSidebar }, data() { return { @@ -308,10 +313,10 @@ export default { // 如果url中存在要打开的文件,那么思维导图数据、主题、布局都使用默认的 if (hasFileURL) { root = { - "data": { - "text": "根节点" + data: { + text: '根节点' }, - "children": [] + children: [] } layout = exampleData.layout theme = exampleData.theme @@ -320,6 +325,7 @@ export default { this.mindMap = new MindMap({ el: this.$refs.mindMapContainer, data: root, + fit: false, layout: layout, theme: theme.template, themeConfig: theme.config, @@ -335,11 +341,11 @@ export default { } }, ...(config || {}), - iconList: icon, + iconList: [...icon], useLeftKeySelectionRightKeyDrag: this.useLeftKeySelectionRightKeyDrag, customInnerElsAppendTo: null, enableAutoEnterTextEditWhenKeydown: true, - customHandleClipboardText: handleClipboardText, + customHandleClipboardText: handleClipboardText // isUseCustomNodeContent: true, // 示例1:组件里用到了router、store、i18n等实例化vue组件时需要用到的东西 // customCreateNodeContent: (node) => { @@ -413,20 +419,7 @@ export default { }) }) this.bindSaveEvent() - // setTimeout(() => { - // 动态给指定节点添加子节点 - // this.mindMap.execCommand('INSERT_CHILD_NODE', false, this.mindMap.renderer.root, { - // text: '自定义内容' - // }) - - // 动态给指定节点添加同级节点 - // this.mindMap.execCommand('INSERT_NODE', false, this.mindMap.renderer.root, { - // text: '自定义内容' - // }) - - // 动态删除指定节点 - // this.mindMap.execCommand('REMOVE_NODE', this.mindMap.renderer.root.children[0]) - // }, 5000); + this.testDynamicCreateNodes() // 如果应用被接管,那么抛出事件传递思维导图实例 if (window.takeOverApp) { this.$bus.$emit('app_inited', this.mindMap) @@ -544,6 +537,113 @@ export default { this.setFileName(res) } this.setIsUnSave(false) + }, + + // 测试动态插入节点 + testDynamicCreateNodes() { + return + setTimeout(() => { + // 动态给指定节点添加子节点 + // this.mindMap.execCommand( + // 'INSERT_CHILD_NODE', + // false, + // this.mindMap.renderer.root, + // { + // text: '自定义内容' + // }, + // [ + // { + // data: { + // text: '自定义子节点' + // } + // } + // ] + // ) + // 动态给指定节点添加同级节点 + // this.mindMap.execCommand( + // 'INSERT_NODE', + // false, + // null, + // { + // text: '自定义内容' + // }, + // [ + // { + // data: { + // text: '自定义同级节点' + // }, + // children: [ + // { + // data: { + // text: '自定义同级节点2' + // }, + // children: [] + // } + // ] + // } + // ] + // ) + // 动态插入多个子节点 + // this.mindMap.execCommand('INSERT_MULTI_CHILD_NODE', null, [ + // { + // data: { + // text: '自定义节点1' + // }, + // children: [ + // { + // data: { + // text: '自定义节点1-1' + // }, + // children: [] + // } + // ] + // }, + // { + // data: { + // text: '自定义节点2' + // }, + // children: [ + // { + // data: { + // text: '自定义节点2-1' + // }, + // children: [] + // } + // ] + // } + // ]) + // 动态插入多个同级节点 + // this.mindMap.execCommand('INSERT_MULTI_NODE', null, [ + // { + // data: { + // text: '自定义节点1' + // }, + // children: [ + // { + // data: { + // text: '自定义节点1-1' + // }, + // children: [] + // } + // ] + // }, + // { + // data: { + // text: '自定义节点2' + // }, + // children: [ + // { + // data: { + // text: '自定义节点2-1' + // }, + // children: [] + // } + // ] + // } + // ]) + // 动态删除指定节点 + // this.mindMap.execCommand('REMOVE_NODE', this.mindMap.renderer.root.children[0]) + }, 5000) } } } diff --git a/web/src/pages/Edit/components/FormulaSidebar.vue b/web/src/pages/Edit/components/FormulaSidebar.vue new file mode 100644 index 00000000..114025ef --- /dev/null +++ b/web/src/pages/Edit/components/FormulaSidebar.vue @@ -0,0 +1,201 @@ + + + + + diff --git a/web/src/pages/Edit/components/NavigatorToolbar.vue b/web/src/pages/Edit/components/NavigatorToolbar.vue index a561f32e..b0ddb5fc 100644 --- a/web/src/pages/Edit/components/NavigatorToolbar.vue +++ b/web/src/pages/Edit/components/NavigatorToolbar.vue @@ -80,6 +80,7 @@ 开发文档 官方网站 意见反馈 + 当前:v{{ version }} @@ -94,6 +95,7 @@ import { langList } from '@/config' import i18n from '@/i18n' import { storeLang, getLang } from '@/api' import { mapState, mapMutations } from 'vuex' +import pkg from 'simple-mind-map/package.json' /** * @Author: 王林 @@ -114,6 +116,7 @@ export default { }, data() { return { + version: pkg.version, langList, lang: '', isReadonly: false, diff --git a/web/src/pages/Edit/components/NodeIconSidebar.vue b/web/src/pages/Edit/components/NodeIconSidebar.vue index 168eb970..54b5c36f 100644 --- a/web/src/pages/Edit/components/NodeIconSidebar.vue +++ b/web/src/pages/Edit/components/NodeIconSidebar.vue @@ -1,9 +1,15 @@