diff --git a/index.html b/index.html index c14f7ebd..24207853 100644 --- a/index.html +++ b/index.html @@ -1 +1 @@ -一个简单的web思维导图实现
\ No newline at end of file +一个简单的web思维导图实现
\ No newline at end of file diff --git a/simple-mind-map/full.js b/simple-mind-map/full.js index 80048f10..51ac4c7b 100644 --- a/simple-mind-map/full.js +++ b/simple-mind-map/full.js @@ -1,12 +1,13 @@ import MindMap from './index' -import MiniMap from './src/MiniMap.js' -import Watermark from './src/Watermark.js' -import KeyboardNavigation from './src/KeyboardNavigation.js' -import Export from './src/Export.js' -import Drag from './src/Drag.js' -import Select from './src/Select.js' -import AssociativeLine from './src/AssociativeLine' -import RichText from './src/RichText' +import MiniMap from './src/plugins/MiniMap.js' +import Watermark from './src/plugins/Watermark.js' +import KeyboardNavigation from './src/plugins/KeyboardNavigation.js' +import ExportPDF from './src/plugins/ExportPDF.js' +import Export from './src/plugins/Export.js' +import Drag from './src/plugins/Drag.js' +import Select from './src/plugins/Select.js' +import AssociativeLine from './src/plugins/AssociativeLine' +import RichText from './src/plugins/RichText' import xmind from './src/parse/xmind.js' import markdown from './src/parse/markdown.js' import icons from './src/svg/icons.js' @@ -20,6 +21,7 @@ MindMap .usePlugin(Watermark) .usePlugin(Drag) .usePlugin(KeyboardNavigation) + .usePlugin(ExportPDF) .usePlugin(Export) .usePlugin(Select) .usePlugin(AssociativeLine) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 1b283f4c..3802b389 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -1,13 +1,13 @@ -import View from './src/View' -import Event from './src/Event' -import Render from './src/Render' +import View from './src/core/view/View' +import Event from './src/core/event/Event' +import Render from './src/core/render/Render' import merge from 'deepmerge' import theme from './src/themes' -import Style from './src/Style' -import KeyCommand from './src/KeyCommand' -import Command from './src/Command' -import BatchExecution from './src/BatchExecution' -import { layoutValueList, CONSTANTS } from './src/utils/constant' +import Style from './src/core/render/node/Style' +import KeyCommand from './src/core/command/KeyCommand' +import Command from './src/core/command/Command' +import BatchExecution from './src/utils/BatchExecution' +import { layoutValueList, CONSTANTS } from './src/constants/constant' import { SVG } from '@svgdotjs/svg.js' import { simpleDeepClone } from './src/utils' import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default' @@ -122,7 +122,15 @@ const defaultOpt = { // 节点最大缓存数量 maxNodeCacheCount: 1000, // 关联线默认文字 - defaultAssociativeLineText: '关联' + defaultAssociativeLineText: '关联', + // 思维导图适应画布大小时的内边距 + fitPadding: 50, + // 是否开启按住ctrl键多选节点功能 + enableCtrlKeyNodeSelection: true, + // 设置为左键多选节点,右键拖动画布 + useLeftKeySelectionRightKeyDrag: false, + // 节点即将进入编辑前的回调方法,如果该方法返回true以外的值,那么将取消编辑,函数可以返回一个值,或一个Promise,回调参数为节点实例 + beforeTextEdit: null } // 思维导图 @@ -476,6 +484,21 @@ class MindMap { pluginOpt: plugin.pluginOpt }) } + + // 销毁 + destroy() { + // 移除插件 + [...MindMap.pluginList].forEach((plugin) => { + this[plugin.instanceName] = null + }) + // 解绑事件 + this.event.unbind() + // 移除画布节点 + this.svg.remove() + // 去除给容器元素设置的背景样式 + Style.removeBackgroundStyle(this.el) + this.el = null + } } // 插件列表 diff --git a/simple-mind-map/package-lock.json b/simple-mind-map/package-lock.json index dccb243b..811a8646 100644 --- a/simple-mind-map/package-lock.json +++ b/simple-mind-map/package-lock.json @@ -1,15 +1,14 @@ { "name": "simple-mind-map", - "version": "0.4.7", + "version": "0.6.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.4.7", + "version": "0.6.0", "license": "MIT", "dependencies": { "@svgdotjs/svg.js": "^3.0.16", - "canvg": "^3.0.7", "deepmerge": "^1.5.2", "eventemitter3": "^4.0.7", "html2canvas": "^1.4.1", @@ -160,7 +159,8 @@ "node_modules/@types/raf": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", - "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==" + "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", + "optional": true }, "node_modules/@types/unist": { "version": "2.0.6", @@ -305,6 +305,7 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "optional": true, "dependencies": { "@babel/runtime": "^7.12.5", "@types/raf": "^3.4.0", @@ -381,6 +382,7 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz", "integrity": "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==", "hasInstallScript": true, + "optional": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -1813,7 +1815,8 @@ "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -1908,6 +1911,7 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, "dependencies": { "performance-now": "^2.1.0" } @@ -1982,6 +1986,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true, "engines": { "node": ">= 0.8.15" } @@ -2075,6 +2080,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "optional": true, "engines": { "node": ">=0.1.14" } @@ -2127,6 +2133,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true, "engines": { "node": ">=12.0.0" } @@ -2386,7 +2393,8 @@ "@types/raf": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.0.tgz", - "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==" + "integrity": "sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==", + "optional": true }, "@types/unist": { "version": "2.0.6", @@ -2489,6 +2497,7 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "optional": true, "requires": { "@babel/runtime": "^7.12.5", "@types/raf": "^3.4.0", @@ -2544,7 +2553,8 @@ "core-js": { "version": "3.27.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.1.tgz", - "integrity": "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==" + "integrity": "sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==", + "optional": true }, "core-util-is": { "version": "1.0.3", @@ -3511,7 +3521,8 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true }, "prelude-ls": { "version": "1.2.1", @@ -3576,6 +3587,7 @@ "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, "requires": { "performance-now": "^2.1.0" } @@ -3630,7 +3642,8 @@ "rgbcolor": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==" + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true }, "rimraf": { "version": "3.0.2", @@ -3691,7 +3704,8 @@ "stackblur-canvas": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", - "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==" + "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", + "optional": true }, "string_decoder": { "version": "1.1.1", @@ -3728,7 +3742,8 @@ "svg-pathdata": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==" + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true }, "text-segmentation": { "version": "1.0.3", diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index a5a08080..b53f9a7b 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.5.11", + "version": "0.6.0", "description": "一个简单的web在线思维导图", "authors": [ { @@ -25,7 +25,6 @@ "__main": "./dist/simpleMindMap.umd.min.js", "dependencies": { "@svgdotjs/svg.js": "^3.0.16", - "canvg": "^3.0.7", "deepmerge": "^1.5.2", "eventemitter3": "^4.0.7", "html2canvas": "^1.4.1", diff --git a/simple-mind-map/src/utils/constant.js b/simple-mind-map/src/constants/constant.js similarity index 100% rename from simple-mind-map/src/utils/constant.js rename to simple-mind-map/src/constants/constant.js diff --git a/simple-mind-map/src/Command.js b/simple-mind-map/src/core/command/Command.js similarity index 98% rename from simple-mind-map/src/Command.js rename to simple-mind-map/src/core/command/Command.js index b1c35c57..2a5f3115 100644 --- a/simple-mind-map/src/Command.js +++ b/simple-mind-map/src/core/command/Command.js @@ -1,4 +1,4 @@ -import { copyRenderTree, simpleDeepClone, nextTick } from './utils' +import { copyRenderTree, simpleDeepClone, nextTick } from '../../utils' // 命令类 class Command { diff --git a/simple-mind-map/src/KeyCommand.js b/simple-mind-map/src/core/command/KeyCommand.js similarity index 98% rename from simple-mind-map/src/KeyCommand.js rename to simple-mind-map/src/core/command/KeyCommand.js index 579e219a..cc7a772b 100644 --- a/simple-mind-map/src/KeyCommand.js +++ b/simple-mind-map/src/core/command/KeyCommand.js @@ -1,4 +1,4 @@ -import { keyMap } from './utils/keyMap' +import { keyMap } from './keyMap' // 快捷按键、命令处理类 export default class KeyCommand { // 构造函数 diff --git a/simple-mind-map/src/utils/keyMap.js b/simple-mind-map/src/core/command/keyMap.js similarity index 100% rename from simple-mind-map/src/utils/keyMap.js rename to simple-mind-map/src/core/command/keyMap.js diff --git a/simple-mind-map/src/Event.js b/simple-mind-map/src/core/event/Event.js similarity index 92% rename from simple-mind-map/src/Event.js rename to simple-mind-map/src/core/event/Event.js index daac439f..67d7fb59 100644 --- a/simple-mind-map/src/Event.js +++ b/simple-mind-map/src/core/event/Event.js @@ -1,5 +1,5 @@ import EventEmitter from 'eventemitter3' -import { CONSTANTS } from './utils/constant' +import { CONSTANTS } from '../../constants/constant' // 事件类 class Event extends EventEmitter { @@ -9,6 +9,7 @@ class Event extends EventEmitter { this.opt = opt this.mindMap = opt.mindMap this.isLeftMousedown = false + this.isRightMousedown = false this.mousedownPos = { x: 0, y: 0 @@ -89,6 +90,8 @@ class Event extends EventEmitter { // 鼠标左键 if (e.which === 1) { this.isLeftMousedown = true + } else if (e.which === 3) { + this.isRightMousedown = true } this.mousedownPos.x = e.clientX this.mousedownPos.y = e.clientY @@ -97,12 +100,17 @@ class Event extends EventEmitter { // 鼠标移动事件 onMousemove(e) { + let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt this.mousemovePos.x = e.clientX this.mousemovePos.y = e.clientY this.mousemoveOffset.x = e.clientX - this.mousedownPos.x this.mousemoveOffset.y = e.clientY - this.mousedownPos.y this.emit('mousemove', e, this) - if (this.isLeftMousedown) { + if ( + useLeftKeySelectionRightKeyDrag + ? this.isRightMousedown + : this.isLeftMousedown + ) { e.preventDefault() this.emit('drag', e, this) } @@ -111,6 +119,7 @@ class Event extends EventEmitter { // 鼠标松开事件 onMouseup(e) { this.isLeftMousedown = false + this.isRightMousedown = false this.emit('mouseup', e, this) } diff --git a/simple-mind-map/src/Render.js b/simple-mind-map/src/core/render/Render.js similarity index 96% rename from simple-mind-map/src/Render.js rename to simple-mind-map/src/core/render/Render.js index 92ea717e..b2e3464a 100644 --- a/simple-mind-map/src/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -1,15 +1,15 @@ import merge from 'deepmerge' -import LogicalStructure from './layouts/LogicalStructure' -import MindMap from './layouts/MindMap' -import CatalogOrganization from './layouts/CatalogOrganization' -import OrganizationStructure from './layouts/OrganizationStructure' -import Timeline from './layouts/Timeline' -import Fishbone from './layouts/Fishbone' +import LogicalStructure from '../../layouts/LogicalStructure' +import MindMap from '../../layouts/MindMap' +import CatalogOrganization from '../../layouts/CatalogOrganization' +import OrganizationStructure from '../../layouts/OrganizationStructure' +import Timeline from '../../layouts/Timeline' +import Fishbone from '../../layouts/Fishbone' import TextEdit from './TextEdit' -import { copyNodeTree, simpleDeepClone, walk } from './utils' -import { shapeList } from './Shape' -import { lineStyleProps } from './themes/default' -import { CONSTANTS } from './utils/constant' +import { copyNodeTree, simpleDeepClone, walk } from '../../utils' +import { shapeList } from './node/Shape' +import { lineStyleProps } from '../../themes/default' +import { CONSTANTS } from '../../constants/constant' // 布局列表 const layouts = { @@ -79,9 +79,15 @@ class Render { // 绑定事件 bindEvent() { // 点击事件 - this.mindMap.on('draw_click', () => { + this.mindMap.on('draw_click', (e) => { // 清除激活状态 - if (this.activeNodeList.length > 0) { + let isTrueClick = true + let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt + if (useLeftKeySelectionRightKeyDrag) { + let mousedownPos = this.mindMap.event.mousedownPos + isTrueClick = Math.abs(e.clientX - mousedownPos.x) <= 5 && Math.abs(e.clientY - mousedownPos.y) <= 5 + } + if (isTrueClick && this.activeNodeList.length > 0) { this.mindMap.execCommand('CLEAR_ACTIVE_NODE') } }) diff --git a/simple-mind-map/src/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js similarity index 94% rename from simple-mind-map/src/TextEdit.js rename to simple-mind-map/src/core/render/TextEdit.js index 80d7e90b..888ef7e3 100644 --- a/simple-mind-map/src/TextEdit.js +++ b/simple-mind-map/src/core/render/TextEdit.js @@ -1,4 +1,4 @@ -import { getStrWithBrFromHtml, checkNodeOuter } from './utils' +import { getStrWithBrFromHtml, checkNodeOuter } from '../../utils' // 节点文字编辑类 export default class TextEdit { @@ -65,7 +65,16 @@ export default class TextEdit { } // 显示文本编辑框 - show(node) { + async show(node) { + if (typeof this.mindMap.opt.beforeTextEdit === 'function') { + let isShow = false + try { + isShow = await this.mindMap.opt.beforeTextEdit(node) + } catch (error) { + isShow = false + } + if (!isShow) return + } this.currentNode = node let { offsetLeft, offsetTop } = checkNodeOuter(this.mindMap, node) this.mindMap.view.translateXY(offsetLeft, offsetTop) diff --git a/simple-mind-map/src/Node.js b/simple-mind-map/src/core/render/node/Node.js similarity index 98% rename from simple-mind-map/src/Node.js rename to simple-mind-map/src/core/render/node/Node.js index 789c00d5..d92c9f72 100644 --- a/simple-mind-map/src/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -1,12 +1,12 @@ import Style from './Style' import Shape from './Shape' -import { asyncRun } from './utils' +import { asyncRun } from '../../../utils' import { G, Rect } from '@svgdotjs/svg.js' -import nodeGeneralizationMethods from './utils/nodeGeneralization' -import nodeExpandBtnMethods from './utils/nodeExpandBtn' -import nodeCommandWrapsMethods from './utils/nodeCommandWraps' -import nodeCreateContentsMethods from './utils/nodeCreateContents' -import { CONSTANTS } from './utils/constant' +import nodeGeneralizationMethods from './nodeGeneralization' +import nodeExpandBtnMethods from './nodeExpandBtn' +import nodeCommandWrapsMethods from './nodeCommandWraps' +import nodeCreateContentsMethods from './nodeCreateContents' +import { CONSTANTS } from '../../../constants/constant' // 节点类 class Node { @@ -343,12 +343,12 @@ class Node { bindGroupEvent() { // 单击事件,选中节点 this.group.on('click', e => { + this.mindMap.emit('node_click', this, e) if (this.isMultipleChoice) { e.stopPropagation() this.isMultipleChoice = false return } - this.mindMap.emit('node_click', this, e) this.active(e) }) this.group.on('mousedown', e => { @@ -359,7 +359,7 @@ class Node { e.stopPropagation() } // 多选和取消多选 - if (e.ctrlKey) { + if (e.ctrlKey && this.mindMap.opt.enableCtrlKeyNodeSelection) { this.isMultipleChoice = true let isActive = this.nodeData.data.isActive if (!isActive) diff --git a/simple-mind-map/src/Shape.js b/simple-mind-map/src/core/render/node/Shape.js similarity index 99% rename from simple-mind-map/src/Shape.js rename to simple-mind-map/src/core/render/node/Shape.js index b0df207b..d53d8881 100644 --- a/simple-mind-map/src/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -1,5 +1,5 @@ import { Rect, Polygon, Path } from '@svgdotjs/svg.js' -import { CONSTANTS } from './utils/constant' +import { CONSTANTS } from '../../../constants/constant' // 节点形状类 export default class Shape { diff --git a/simple-mind-map/src/Style.js b/simple-mind-map/src/core/render/node/Style.js similarity index 88% rename from simple-mind-map/src/Style.js rename to simple-mind-map/src/core/render/node/Style.js index 6f10526e..3ffa9a82 100644 --- a/simple-mind-map/src/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -1,10 +1,20 @@ -import { tagColorList } from './utils/constant' +import { tagColorList } from '../../../constants/constant' const rootProp = ['paddingX', 'paddingY'] +const backgroundStyleProps = ['backgroundColor', 'backgroundImage', 'backgroundRepeat', 'backgroundPosition', 'backgroundSize'] // 样式类 class Style { // 设置背景样式 static setBackgroundStyle(el, themeConfig) { + // 缓存容器元素原本的样式 + if (!Style.cacheStyle) { + Style.cacheStyle = {} + let style = window.getComputedStyle(el) + backgroundStyleProps.forEach((prop) => { + Style.cacheStyle[prop] = style[prop] + }) + } + // 设置新样式 let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig el.style.backgroundColor = backgroundColor if (backgroundImage) { @@ -17,6 +27,14 @@ class Style { } } + // 移除背景样式 + static removeBackgroundStyle(el) { + backgroundStyleProps.forEach((prop) => { + el.style[prop] = Style.cacheStyle[prop] + }) + Style.cacheStyle = null + } + // 构造函数 constructor(ctx) { this.ctx = ctx @@ -192,4 +210,6 @@ class Style { } } +Style.cacheStyle = null + export default Style diff --git a/simple-mind-map/src/utils/nodeCommandWraps.js b/simple-mind-map/src/core/render/node/nodeCommandWraps.js similarity index 100% rename from simple-mind-map/src/utils/nodeCommandWraps.js rename to simple-mind-map/src/core/render/node/nodeCommandWraps.js diff --git a/simple-mind-map/src/utils/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js similarity index 98% rename from simple-mind-map/src/utils/nodeCreateContents.js rename to simple-mind-map/src/core/render/node/nodeCreateContents.js index 866a4db3..c5231e71 100644 --- a/simple-mind-map/src/utils/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -1,7 +1,7 @@ -import { measureText, resizeImgSize, getTextFromHtml } from '../utils' +import { measureText, resizeImgSize, getTextFromHtml } from '../../../utils' import { Image, SVG, A, G, Rect, Text, ForeignObject } from '@svgdotjs/svg.js' -import iconsSvg from '../svg/icons' -import { CONSTANTS } from './constant' +import iconsSvg from '../../../svg/icons' +import { CONSTANTS } from '../../../constants/constant' // 创建图片节点 function createImgNode() { diff --git a/simple-mind-map/src/utils/nodeExpandBtn.js b/simple-mind-map/src/core/render/node/nodeExpandBtn.js similarity index 98% rename from simple-mind-map/src/utils/nodeExpandBtn.js rename to simple-mind-map/src/core/render/node/nodeExpandBtn.js index 650417b5..5b2675cd 100644 --- a/simple-mind-map/src/utils/nodeExpandBtn.js +++ b/simple-mind-map/src/core/render/node/nodeExpandBtn.js @@ -1,4 +1,4 @@ -import btnsSvg from '../svg/btns' +import btnsSvg from '../../../svg/btns' import { SVG, Circle, G } from '@svgdotjs/svg.js' // 创建展开收起按钮的内容节点 diff --git a/simple-mind-map/src/utils/nodeGeneralization.js b/simple-mind-map/src/core/render/node/nodeGeneralization.js similarity index 99% rename from simple-mind-map/src/utils/nodeGeneralization.js rename to simple-mind-map/src/core/render/node/nodeGeneralization.js index 6521de97..2a2a585d 100644 --- a/simple-mind-map/src/utils/nodeGeneralization.js +++ b/simple-mind-map/src/core/render/node/nodeGeneralization.js @@ -1,4 +1,4 @@ -import Node from '../Node' +import Node from './Node' // 检查是否存在概要 function checkHasGeneralization () { diff --git a/simple-mind-map/src/View.js b/simple-mind-map/src/core/view/View.js similarity index 69% rename from simple-mind-map/src/View.js rename to simple-mind-map/src/core/view/View.js index ffe2599f..fa464f37 100644 --- a/simple-mind-map/src/View.js +++ b/simple-mind-map/src/core/view/View.js @@ -1,4 +1,4 @@ -import { CONSTANTS } from './utils/constant' +import { CONSTANTS } from '../../constants/constant' // 视图操作类 class View { @@ -28,6 +28,9 @@ class View { this.mindMap.keyCommand.addShortcut('Control+Enter', () => { this.reset() }) + this.mindMap.keyCommand.addShortcut('Control+i', () => { + this.fit() + }) this.mindMap.svg.on('dblclick', () => { this.reset() }) @@ -57,10 +60,15 @@ class View { }) // 放大缩小视图 this.mindMap.event.on('mousewheel', (e, dir) => { - if (this.mindMap.opt.customHandleMousewheel && typeof this.mindMap.opt.customHandleMousewheel === 'function') { + if ( + this.mindMap.opt.customHandleMousewheel && + typeof this.mindMap.opt.customHandleMousewheel === 'function' + ) { return this.mindMap.opt.customHandleMousewheel(e) } - if (this.mindMap.opt.mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM) { + if ( + this.mindMap.opt.mousewheelAction === CONSTANTS.MOUSE_WHEEL_ACTION.ZOOM + ) { switch (dir) { // 鼠标滚轮,向上和向左,都是缩小 case CONSTANTS.DIR.UP: @@ -74,7 +82,7 @@ class View { break } } else { - switch (dir){ + switch (dir) { // 上移 case CONSTANTS.DIR.DOWN: this.translateY(-this.mindMap.opt.mousewheelMoveStep) @@ -201,6 +209,56 @@ class View { this.transform() this.mindMap.emit('scale', this.scale) } + + // 适应画布大小 + fit() { + let { fitPadding } = this.mindMap.opt + let draw = this.mindMap.draw + let origTransform = draw.transform() + let rect = draw.rbox() + let drawWidth = rect.width / origTransform.scaleX + let drawHeight = rect.height / origTransform.scaleY + let drawRatio = drawWidth / drawHeight + let { width: elWidth, height: elHeight } = + this.mindMap.el.getBoundingClientRect() + elWidth = elWidth - fitPadding * 2 + elHeight = elHeight - fitPadding * 2 + let elRatio = elWidth / elHeight + let newScale = 0 + let flag = '' + if (drawWidth <= elWidth && drawHeight <= elHeight) { + newScale = 1 + flag = 1 + } else { + let newWidth = 0 + let newHeight = 0 + if (drawRatio > elRatio) { + newWidth = elWidth + newHeight = elWidth / drawRatio + flag = 2 + } else { + newHeight = elHeight + newWidth = elHeight * drawRatio + flag = 3 + } + newScale = newWidth / drawWidth + } + this.setScale(newScale) + let newRect = draw.rbox() + let newX = 0 + let newY = 0 + if (flag === 1) { + newX = -newRect.x + fitPadding + (elWidth - newRect.width) / 2 + newY = -newRect.y + fitPadding + (elHeight - newRect.height) / 2 + } else if (flag === 2) { + newX = -newRect.x + fitPadding + newY = -newRect.y + fitPadding + (elHeight - newRect.height) / 2 + } else if (flag === 3) { + newX = -newRect.x + fitPadding + (elWidth - newRect.width) / 2 + newY = -newRect.y + fitPadding + } + this.translateXY(newX, newY) + } } export default View diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index e51b72d5..701fffbf 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -1,5 +1,5 @@ -import Node from '../Node' -import { CONSTANTS, initRootNodePositionMap } from '../utils/constant' +import Node from '../core/render/node/Node' +import { CONSTANTS, initRootNodePositionMap } from '../constants/constant' import Lru from '../utils/Lru' // 布局基类 diff --git a/simple-mind-map/src/layouts/Fishbone.js b/simple-mind-map/src/layouts/Fishbone.js index 58e70932..e8581852 100644 --- a/simple-mind-map/src/layouts/Fishbone.js +++ b/simple-mind-map/src/layouts/Fishbone.js @@ -1,6 +1,6 @@ import Base from './Base' import { walk, asyncRun, degToRad } from '../utils' -import { CONSTANTS } from '../utils/constant' +import { CONSTANTS } from '../constants/constant' import utils from './fishboneUtils' // 鱼骨图 diff --git a/simple-mind-map/src/layouts/Timeline.js b/simple-mind-map/src/layouts/Timeline.js index 0e426cf6..60a41db2 100644 --- a/simple-mind-map/src/layouts/Timeline.js +++ b/simple-mind-map/src/layouts/Timeline.js @@ -1,6 +1,6 @@ import Base from './Base' import { walk, asyncRun } from '../utils' -import { CONSTANTS } from '../utils/constant' +import { CONSTANTS } from '../constants/constant' // 时间轴 class Timeline extends Base { diff --git a/simple-mind-map/src/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js similarity index 96% rename from simple-mind-map/src/AssociativeLine.js rename to simple-mind-map/src/plugins/AssociativeLine.js index c365a8de..28223265 100644 --- a/simple-mind-map/src/AssociativeLine.js +++ b/simple-mind-map/src/plugins/AssociativeLine.js @@ -1,4 +1,4 @@ -import { walk, bfsWalk, throttle } from './utils/' +import { walk, bfsWalk, throttle } from '../utils' import { v4 as uuid } from 'uuid' import { getAssociativeLineTargetIndex, @@ -7,9 +7,9 @@ import { getNodePoint, computeNodePoints, getNodeLinePath -} from './utils/associativeLineUtils' -import associativeLineControlsMethods from './utils/associativeLineControls' -import associativeLineTextMethods from './utils/associativeLineText' +} from './associativeLine/associativeLineUtils' +import associativeLineControlsMethods from './associativeLine/associativeLineControls' +import associativeLineTextMethods from './associativeLine/associativeLineText' // 关联线类 class AssociativeLine { @@ -91,12 +91,7 @@ class AssociativeLine { this.mindMap.on('node_dragging', this.onNodeDragging.bind(this)) this.mindMap.on('node_dragend', this.onNodeDragend.bind(this)) // 拖拽控制点 - window.addEventListener('mousemove', e => { - this.onControlPointMousemove(e) - }) - window.addEventListener('mouseup', e => { - this.onControlPointMouseup(e) - }) + this.mindMap.on('mouseup', this.onControlPointMouseup.bind(this)) // 缩放事件 this.mindMap.on('scale', this.onScale) } @@ -266,12 +261,13 @@ class AssociativeLine { // 鼠标移动事件 onMousemove(e) { - if (!this.isCreatingLine) return + this.onControlPointMousemove(e) this.updateCreatingLine(e) } // 更新创建过程中的连接线 updateCreatingLine(e) { + if (!this.isCreatingLine) return let { x, y } = this.getTransformedEventPos(e) let startPoint = getNodePoint(this.creatingStartNode) let offsetX = x > startPoint.x ? -10 : 10 diff --git a/simple-mind-map/src/Drag.js b/simple-mind-map/src/plugins/Drag.js similarity index 99% rename from simple-mind-map/src/Drag.js rename to simple-mind-map/src/plugins/Drag.js index ffadf0c5..d85ea667 100644 --- a/simple-mind-map/src/Drag.js +++ b/simple-mind-map/src/plugins/Drag.js @@ -1,5 +1,5 @@ -import { bfsWalk, throttle } from './utils' -import Base from './layouts/Base' +import { bfsWalk, throttle } from '../utils' +import Base from '../layouts/Base' // 节点拖动类 diff --git a/simple-mind-map/src/Export.js b/simple-mind-map/src/plugins/Export.js similarity index 84% rename from simple-mind-map/src/Export.js rename to simple-mind-map/src/plugins/Export.js index cbf3a639..5186ffe8 100644 --- a/simple-mind-map/src/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -1,9 +1,7 @@ -import { imgToDataUrl, downloadFile, readBlob } from './utils' -import JsPDF from 'jspdf' +import { imgToDataUrl, downloadFile, readBlob } from '../utils' import { SVG } from '@svgdotjs/svg.js' -import drawBackgroundImageToCanvas from './utils/simulateCSSBackgroundInCanvas' -import { transformToMarkdown } from './parse/toMarkdown' -const URL = window.URL || window.webkitURL || window +import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas' +import { transformToMarkdown } from '../parse/toMarkdown' // 导出类 class Export { @@ -175,34 +173,11 @@ class Export { // 导出为pdf async pdf(name) { - let img = await this.png() - let pdf = new JsPDF('', 'pt', 'a4') - let a4Width = 595 - let a4Height = 841 - let a4Ratio = a4Width / a4Height - let image = new Image() - image.onload = () => { - let imageWidth = image.width - let imageHeight = image.height - let imageRatio = imageWidth / imageHeight - let w, h - if (imageWidth <= a4Width && imageHeight <= a4Height) { - // 使用图片原始宽高 - w = imageWidth - h = imageHeight - } else if (a4Ratio > imageRatio) { - // 以a4Height为高度,缩放图片宽度 - w = imageRatio * a4Height - h = a4Height - } else { - // 以a4Width为宽度,缩放图片高度 - w = a4Width - h = a4Width / imageRatio - } - pdf.addImage(img, 'PNG', (a4Width - w) / 2, (a4Height - h) / 2, w, h) - pdf.save(name) + if (!this.mindMap.doExportPDF) { + throw new Error('请注册ExportPDF插件') } - image.src = img + let img = await this.png() + this.mindMap.doExportPDF.pdf(name, img) } // 导出为svg diff --git a/simple-mind-map/src/plugins/ExportPDF.js b/simple-mind-map/src/plugins/ExportPDF.js new file mode 100644 index 00000000..2fcc254d --- /dev/null +++ b/simple-mind-map/src/plugins/ExportPDF.js @@ -0,0 +1,44 @@ +import JsPDF from 'jspdf' + +// 导出PDF类,需要通过Export插件使用 +class ExportPDF { + // 构造函数 + constructor(opt) { + this.mindMap = opt.mindMap + } + + // 导出为pdf + pdf(name, img) { + let pdf = new JsPDF('', 'pt', 'a4') + let a4Width = 595 + let a4Height = 841 + let a4Ratio = a4Width / a4Height + let image = new Image() + image.onload = () => { + let imageWidth = image.width + let imageHeight = image.height + let imageRatio = imageWidth / imageHeight + let w, h + if (imageWidth <= a4Width && imageHeight <= a4Height) { + // 使用图片原始宽高 + w = imageWidth + h = imageHeight + } else if (a4Ratio > imageRatio) { + // 以a4Height为高度,缩放图片宽度 + w = imageRatio * a4Height + h = a4Height + } else { + // 以a4Width为宽度,缩放图片高度 + w = a4Width + h = a4Width / imageRatio + } + pdf.addImage(img, 'PNG', (a4Width - w) / 2, (a4Height - h) / 2, w, h) + pdf.save(name) + } + image.src = img + } +} + +ExportPDF.instanceName = 'doExportPDF' + +export default ExportPDF diff --git a/simple-mind-map/src/KeyboardNavigation.js b/simple-mind-map/src/plugins/KeyboardNavigation.js similarity index 98% rename from simple-mind-map/src/KeyboardNavigation.js rename to simple-mind-map/src/plugins/KeyboardNavigation.js index c5adf041..cdbf95f2 100644 --- a/simple-mind-map/src/KeyboardNavigation.js +++ b/simple-mind-map/src/plugins/KeyboardNavigation.js @@ -1,5 +1,5 @@ -import { bfsWalk } from './utils' -import { CONSTANTS } from './utils/constant' +import { bfsWalk } from '../utils' +import { CONSTANTS } from '../constants/constant' // 键盘导航类 class KeyboardNavigation { diff --git a/simple-mind-map/src/MiniMap.js b/simple-mind-map/src/plugins/MiniMap.js similarity index 100% rename from simple-mind-map/src/MiniMap.js rename to simple-mind-map/src/plugins/MiniMap.js diff --git a/simple-mind-map/src/RichText.js b/simple-mind-map/src/plugins/RichText.js similarity index 94% rename from simple-mind-map/src/RichText.js rename to simple-mind-map/src/plugins/RichText.js index 227f701e..a4b846d3 100644 --- a/simple-mind-map/src/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -1,8 +1,8 @@ import Quill from 'quill' import 'quill/dist/quill.snow.css' import html2canvas from 'html2canvas' -import { walk, getTextFromHtml } from './utils' -import { CONSTANTS } from './utils/constant' +import { walk, getTextFromHtml } from '../utils' +import { CONSTANTS } from '../constants/constant' let extended = false @@ -41,6 +41,7 @@ class RichText { this.node = null this.styleEl = null this.cacheEditingText = '' + this.lostStyle = false this.initOpt() this.extendQuill() this.appendCss() @@ -59,6 +60,7 @@ class RichText { padding: 0; height: auto; line-height: normal; + -webkit-user-select: text; } .ql-container { @@ -281,6 +283,20 @@ class RichText { ) } }) + this.quill.on('text-change', () => { + let contents = this.quill.getContents() + let len = contents.ops.length + // 如果编辑过程中删除所有字符,那么会丢失主题的样式 + if (len <= 0 || (len === 1 && contents.ops[0].insert === '\n')) { + this.lostStyle = true + // 需要删除节点的样式数据 + this.syncFormatToNodeConfig(null, true) + } else if (this.lostStyle) { + // 如果处于样式丢失状态,那么需要进行格式化加回样式 + this.setTextStyleIfNotRichText(this.node) + this.lostStyle = false + } + }) } // 选中全部 diff --git a/simple-mind-map/src/Select.js b/simple-mind-map/src/plugins/Select.js similarity index 83% rename from simple-mind-map/src/Select.js rename to simple-mind-map/src/plugins/Select.js index a8c847c9..b2a58c14 100644 --- a/simple-mind-map/src/Select.js +++ b/simple-mind-map/src/plugins/Select.js @@ -1,4 +1,4 @@ -import { bfsWalk, throttle } from './utils' +import { bfsWalk, throttle } from '../utils' // 选择节点类 @@ -22,9 +22,14 @@ class Select { if (this.mindMap.opt.readonly) { return } - if (!e.ctrlKey && e.which !== 3) { + let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt + if ( + !e.ctrlKey && + (useLeftKeySelectionRightKeyDrag ? e.which !== 1 : e.which !== 3) + ) { return } + e.preventDefault() this.isMousedown = true let { x, y } = this.mindMap.toPos(e.clientX, e.clientY) this.mouseDownX = x @@ -146,25 +151,24 @@ class Select { let bottom = (top + height) * scaleY + translateY 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) - ) { + if ( + ((left >= minx && left <= maxx) || (right >= minx && right <= maxx)) && + ((top >= miny && top <= maxy) || (bottom >= miny && bottom <= maxy)) + ) { // this.mindMap.batchExecution.push('activeNode' + node.uid, () => { - if (node.nodeData.data.isActive) { - return - } - this.mindMap.renderer.setNodeActive(node, true) - this.mindMap.renderer.addActiveNode(node) + 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) + 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/TouchEvent.js b/simple-mind-map/src/plugins/TouchEvent.js new file mode 100644 index 00000000..135d6ece --- /dev/null +++ b/simple-mind-map/src/plugins/TouchEvent.js @@ -0,0 +1,122 @@ +// 手势事件支持类 + +class TouchEvent { + // 构造函数 + constructor({ mindMap }) { + this.mindMap = mindMap + this.touchesNum = 0 + this.singleTouchstartEvent = null + this.clickNum = 0 + this.doubleTouchmoveDistance = 0 + this.bindEvent() + } + + // 绑定事件 + bindEvent() { + this.onTouchstart = this.onTouchstart.bind(this) + this.onTouchmove = this.onTouchmove.bind(this) + this.onTouchcancel = this.onTouchcancel.bind(this) + this.onTouchend = this.onTouchend.bind(this) + window.addEventListener('touchstart', this.onTouchstart) + window.addEventListener('touchmove', this.onTouchmove) + window.addEventListener('touchcancel', this.onTouchcancel) + window.addEventListener('touchend', this.onTouchend) + } + + // 解绑事件 + unBindEvent() { + window.removeEventListener('touchstart', this.onTouchstart) + window.removeEventListener('touchmove', this.onTouchmove) + window.removeEventListener('touchcancel', this.onTouchcancel) + window.removeEventListener('touchend', this.onTouchend) + } + + // 手指按下事件 + onTouchstart(e) { + this.touchesNum = e.touches.length + if (this.touchesNum === 1) { + let touch = e.touches[0] + this.singleTouchstartEvent = touch + this.dispatchMouseEvent('mousedown', touch.target, touch) + } + } + + // 手指移动事件 + onTouchmove(e) { + let len = e.touches.length + if (len === 1) { + let touch = e.touches[0] + this.dispatchMouseEvent('mousemove', touch.target, touch) + } else if (len === 2) { + let touch1 = e.touches[0] + let touch2 = e.touches[1] + let distance = Math.sqrt( + Math.pow(touch1.clientX - touch2.clientX, 2) + + Math.pow(touch1.clientY - touch2.clientY, 2) + ) + if (distance > this.doubleTouchmoveDistance) { + // 放大 + this.mindMap.view.enlarge() + } else { + // 缩小 + this.mindMap.view.narrow() + } + this.doubleTouchmoveDistance = distance + } + } + + // 手指取消事件 + onTouchcancel(e) {} + + // 手指松开事件 + onTouchend(e) { + this.dispatchMouseEvent('mouseup', e.target) + if (this.touchesNum === 1) { + // 模拟双击事件 + this.clickNum++ + setTimeout(() => { + this.clickNum = 0 + }, 300) + let ev = this.singleTouchstartEvent + if (this.clickNum > 1) { + this.clickNum = 0 + this.dispatchMouseEvent('dblclick', ev.target, ev) + } else { + this.dispatchMouseEvent('click', ev.target, ev) + } + } + this.touchesNum = 0 + this.singleTouchstartEvent = null + this.doubleTouchmoveDistance = 0 + } + + // 发送鼠标事件 + dispatchMouseEvent(eventName, target, e) { + let opt = {} + if (e) { + opt = { + screenX: e.screenX, + screenY: e.screenY, + clientX: e.clientX, + clientY: e.clientY, + which: 1 + } + } + let event = new MouseEvent(eventName, { + view: window, + bubbles: true, + cancelable: true, + ...opt + }) + target.dispatchEvent(event) + } + + // 插件被移除前做的事情 + beforePluginRemove() { + this.unBindEvent() + } +} + +TouchEvent.instanceName = 'touchEvent' + +export default TouchEvent diff --git a/simple-mind-map/src/Watermark.js b/simple-mind-map/src/plugins/Watermark.js similarity index 98% rename from simple-mind-map/src/Watermark.js rename to simple-mind-map/src/plugins/Watermark.js index 83bec10d..7d2ddaca 100644 --- a/simple-mind-map/src/Watermark.js +++ b/simple-mind-map/src/plugins/Watermark.js @@ -1,5 +1,5 @@ import { Text, G } from '@svgdotjs/svg.js' -import { degToRad, camelCaseToHyphen } from './utils' +import { degToRad, camelCaseToHyphen } from '../utils' import merge from 'deepmerge' // 水印类 diff --git a/simple-mind-map/src/utils/associativeLineControls.js b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js similarity index 100% rename from simple-mind-map/src/utils/associativeLineControls.js rename to simple-mind-map/src/plugins/associativeLine/associativeLineControls.js diff --git a/simple-mind-map/src/utils/associativeLineText.js b/simple-mind-map/src/plugins/associativeLine/associativeLineText.js similarity index 98% rename from simple-mind-map/src/utils/associativeLineText.js rename to simple-mind-map/src/plugins/associativeLine/associativeLineText.js index f761ab13..9d0722bb 100644 --- a/simple-mind-map/src/utils/associativeLineText.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineText.js @@ -1,5 +1,5 @@ import { Text } from '@svgdotjs/svg.js' -import { getStrWithBrFromHtml } from './index' +import { getStrWithBrFromHtml } from '../../utils/index' // 创建文字节点 function createText(data) { diff --git a/simple-mind-map/src/utils/associativeLineUtils.js b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js similarity index 100% rename from simple-mind-map/src/utils/associativeLineUtils.js rename to simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js diff --git a/simple-mind-map/src/BatchExecution.js b/simple-mind-map/src/utils/BatchExecution.js similarity index 94% rename from simple-mind-map/src/BatchExecution.js rename to simple-mind-map/src/utils/BatchExecution.js index 4a92fbdc..fcdfb735 100644 --- a/simple-mind-map/src/BatchExecution.js +++ b/simple-mind-map/src/utils/BatchExecution.js @@ -1,4 +1,4 @@ -import { nextTick } from './utils' +import { nextTick } from '.' // 批量执行 class BatchExecution { diff --git a/web/package-lock.json b/web/package-lock.json index d89d26ed..6b3fc039 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -33,6 +33,7 @@ "markdown-it": "^13.0.1", "markdown-it-checkbox": "^1.1.0", "prettier": "^1.19.1", + "vconsole": "^3.15.1", "vue-template-compiler": "^2.6.11", "webpack": "^4.44.2" } @@ -5168,6 +5169,18 @@ "node": ">=0.10.0" } }, + "node_modules/copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/copy-webpack-plugin": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", @@ -10351,6 +10364,12 @@ "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true }, + "node_modules/mutation-observer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", + "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==", + "dev": true + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -15185,6 +15204,18 @@ "node": ">= 0.8" } }, + "node_modules/vconsole": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", + "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.17.2", + "copy-text-to-clipboard": "^3.0.1", + "core-js": "^3.11.0", + "mutation-observer": "^1.0.3" + } + }, "node_modules/vendors": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", @@ -20525,6 +20556,12 @@ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", "dev": true }, + "copy-text-to-clipboard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", + "dev": true + }, "copy-webpack-plugin": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", @@ -24567,6 +24604,12 @@ "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==", "dev": true }, + "mutation-observer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz", + "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==", + "dev": true + }, "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -28566,6 +28609,18 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "vconsole": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz", + "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.17.2", + "copy-text-to-clipboard": "^3.0.1", + "core-js": "^3.11.0", + "mutation-observer": "^1.0.3" + } + }, "vendors": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", diff --git a/web/package.json b/web/package.json index cc9d1cbd..4e94f77e 100644 --- a/web/package.json +++ b/web/package.json @@ -37,6 +37,7 @@ "markdown-it": "^13.0.1", "markdown-it-checkbox": "^1.1.0", "prettier": "^1.19.1", + "vconsole": "^3.15.1", "vue-template-compiler": "^2.6.11", "webpack": "^4.44.2" }, diff --git a/web/public/index.html b/web/public/index.html index 10932fe8..294e768a 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -3,7 +3,7 @@ - + 一个简单的web思维导图实现 diff --git a/web/src/assets/icon-font/iconfont.css b/web/src/assets/icon-font/iconfont.css index 5193dd1c..32a38dce 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=1679621707211') format('woff2'), - url('iconfont.woff?t=1679621707211') format('woff'), - url('iconfont.ttf?t=1679621707211') format('truetype'); + src: url('iconfont.woff2?t=1686298427624') format('woff2'), + url('iconfont.woff?t=1686298427624') format('woff'), + url('iconfont.ttf?t=1686298427624') format('truetype'); } .iconfont { @@ -13,6 +13,14 @@ -moz-osx-font-smoothing: grayscale; } +.iconmouseR:before { + content: "\e6bd"; +} + +.iconmouseL:before { + content: "\e6c0"; +} + .iconwenjian:before { content: "\e607"; } diff --git a/web/src/assets/icon-font/iconfont.ttf b/web/src/assets/icon-font/iconfont.ttf index 2311cfcd..9ab744d3 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 1bdad3ba..a4e57c87 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 f3c1c366..487c6d0d 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/en.js b/web/src/config/en.js index f813eb63..e0a8136d 100644 --- a/web/src/config/en.js +++ b/web/src/config/en.js @@ -291,6 +291,11 @@ export const shortcutKeyList = [ icon: 'icondingwei', name: 'Reset', value: 'Ctrl + Enter' + }, + { + icon: 'iconquanping1', + name: 'fit canvas', + value: 'Ctrl + i' } ] } diff --git a/web/src/config/zh.js b/web/src/config/zh.js index 07080b96..db06f4c5 100644 --- a/web/src/config/zh.js +++ b/web/src/config/zh.js @@ -351,6 +351,11 @@ export const shortcutKeyList = [ icon: 'icondingwei', name: '恢复默认', value: 'Ctrl + Enter' + }, + { + icon: 'iconquanping1', + name: '适应画布', + value: 'Ctrl + i' } ] } diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js index f66a24ea..7ea75351 100644 --- a/web/src/lang/en_us.js +++ b/web/src/lang/en_us.js @@ -69,7 +69,8 @@ export default { level4: 'Level4', level5: 'Level5', level6: 'Level6', - zenMode: 'Zen mode' + zenMode: 'Zen mode', + fitCanvas: 'Fit canvas' }, count: { words: 'Words', @@ -201,5 +202,9 @@ export default { 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.' + }, + 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', } } diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js index 407866ef..858de591 100644 --- a/web/src/lang/zh_cn.js +++ b/web/src/lang/zh_cn.js @@ -69,7 +69,8 @@ export default { level4: '四级主题', level5: '五级主题', level6: '六级主题', - zenMode: '禅模式' + zenMode: '禅模式', + fitCanvas: '适应画布' }, count: { words: '字数', @@ -201,5 +202,9 @@ export default { edit: { newFeatureNoticeTitle: '新特性提醒', newFeatureNoticeMessage: '本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。' + }, + mouseAction: { + tip1: '当前:左键拖动画布,右键框选节点', + tip2: '当前:左键框选节点,右键拖动画布', } } diff --git a/web/src/main.js b/web/src/main.js index 6729eda9..346c5f02 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -8,6 +8,8 @@ import '@/assets/icon-font/iconfont.css' import 'viewerjs/dist/viewer.css' import VueViewer from 'v-viewer' import i18n from './i18n' +// import VConsole from 'vconsole' +// const vConsole = new VConsole() Vue.config.productionTip = false Vue.prototype.$bus = new Vue() diff --git a/web/src/pages/Doc/catalogList.js b/web/src/pages/Doc/catalogList.js index 4282f0ab..cdc0ec6f 100644 --- a/web/src/pages/Doc/catalogList.js +++ b/web/src/pages/Doc/catalogList.js @@ -30,6 +30,7 @@ let APIList = [ 'miniMap', 'watermark', 'associativeLine', + 'touchEvent', 'xmind', 'markdown', 'utils' diff --git a/web/src/pages/Doc/en/associativeLine/index.md b/web/src/pages/Doc/en/associativeLine/index.md index 9a726858..06685ee1 100644 --- a/web/src/pages/Doc/en/associativeLine/index.md +++ b/web/src/pages/Doc/en/associativeLine/index.md @@ -12,7 +12,8 @@ This plugin is used to support the addition of associative lines. ```js import MindMap from 'simple-mind-map' -import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js' +import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js' +// import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js' Use this path for versions below v0.6.0 MindMap.usePlugin(AssociativeLine) ``` diff --git a/web/src/pages/Doc/en/associativeLine/index.vue b/web/src/pages/Doc/en/associativeLine/index.vue index 4981edcc..bc39c6cf 100644 --- a/web/src/pages/Doc/en/associativeLine/index.vue +++ b/web/src/pages/Doc/en/associativeLine/index.vue @@ -13,7 +13,8 @@

This plugin is used to support the addition of associative lines.

Register

import MindMap from 'simple-mind-map'
-import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js'
+import AssociativeLine from 'simple-mind-map/src/plugins/AssociativeLine.js'
+// import AssociativeLine from 'simple-mind-map/src/AssociativeLine.js' Use this path for versions below v0.6.0
 
 MindMap.usePlugin(AssociativeLine)
 
diff --git a/web/src/pages/Doc/en/changelog/index.md b/web/src/pages/Doc/en/changelog/index.md index 42236e0b..12e99159 100644 --- a/web/src/pages/Doc/en/changelog/index.md +++ b/web/src/pages/Doc/en/changelog/index.md @@ -1,5 +1,13 @@ # Changelog +## 0.6.0 + +Breaking change: Adjusted the directory structure of the simple-mind-map source code, Main impact: 1. The introduction path of the plugin needs to be modified. The constant file path needs to be modified. + +New: 1.Supports one click zoom to fit the canvas function. 2.Press and hold the Ctrl key to activate the multi selection function on demand through configuration. 3.Support setting to left click to select multiple nodes and right click to drag the canvas. 4. Support controlling whether nodes are allowed to be edited. 5.Add a method for destroying mind maps. 6.Added touch event support plugin. + +Fix: 1.Fix the issue where holding down the Ctrl key to select multiple nodes does not trigger the click event for the node. 2.Fixed the issue of node style loss when clearing a node and then entering text. + ## 0.5.11 New: Supports associative text editing. diff --git a/web/src/pages/Doc/en/changelog/index.vue b/web/src/pages/Doc/en/changelog/index.vue index 6ae87866..3274fcf9 100644 --- a/web/src/pages/Doc/en/changelog/index.vue +++ b/web/src/pages/Doc/en/changelog/index.vue @@ -1,6 +1,10 @@