Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63d73a73aa | ||
|
|
00f9258a4d | ||
|
|
7087b43d39 | ||
|
|
945a78b7b1 | ||
|
|
55acc19ab8 | ||
|
|
410f0be05e | ||
|
|
2d7c091071 | ||
|
|
9bf58a54ce | ||
|
|
231adeea44 | ||
|
|
0ea618af39 | ||
|
|
f66297ff99 | ||
|
|
3113bf2e1f | ||
|
|
8c114dac02 | ||
|
|
06dba79272 | ||
|
|
8441986ca7 | ||
|
|
c8b50908e1 | ||
|
|
7e11fde892 | ||
|
|
3d9f3bd7a8 | ||
|
|
46e11649b0 | ||
|
|
161ef7590c | ||
|
|
ca660a3c74 | ||
|
|
7a24872331 | ||
|
|
eb4ea9fb3a | ||
|
|
64228c02ff | ||
|
|
f184a5d948 | ||
|
|
0bf5b2d6f7 | ||
|
|
74a52ea386 | ||
|
|
9914eef5b9 | ||
|
|
f547f741f2 | ||
|
|
c26149fa42 | ||
|
|
b2aa3648eb | ||
|
|
b997604ab2 | ||
|
|
c6929b82ad | ||
|
|
b40da53aef | ||
|
|
1e5dfd97e1 | ||
|
|
97bcc22abd | ||
|
|
bd655839cb | ||
|
|
a53a4e8e1d | ||
|
|
eb01646747 | ||
|
|
d27eee0fae | ||
|
|
92ee241ed8 |
@@ -55,4 +55,8 @@ const mindMap = new MindMap({
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
MIT
|
||||
|
||||
# 微信交流群
|
||||
|
||||

|
||||
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 254 KiB |
@@ -1 +1 @@
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="./dist/logo.png"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d0a3179.8bac0e6a.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.3848b480.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.0c0ab348.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.89baf4c9.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.ba566b9e.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.d57feb19.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.9058553b.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.ff912e01.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.988f0b69.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.b87008fb.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.d53f93f5.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.ef38347c.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.fe3d1357.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.dc5d667f.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.7c0e8c3b.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.7110cb06.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.789c08d8.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.773ee0f5.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.29899f8c.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.4f180262.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.7b20d38b.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.314fd603.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.cd1baa18.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.62e1ccbf.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.af35b5ce.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.ffe557f3.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.6b617b98.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.66cba965.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.25884d5e.js" rel="prefetch"><link href="dist/js/chunk-2d216004.3416e02c.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.2d06497a.js" rel="prefetch"><link href="dist/js/chunk-2d217907.96994ac6.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.d30a47fe.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.5aa1632c.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.d813ba33.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.a6908b68.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.1790fe42.css" rel="preload" as="style"><link href="dist/js/app.2f5edb91.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.7f3b8358.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.1790fe42.css" rel="stylesheet"><link href="dist/css/app.a6908b68.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.7f3b8358.js"></script><script src="dist/js/app.2f5edb91.js"></script></body></html>
|
||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="./dist/logo.png"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d0a3179.05d77cdf.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.61258a02.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.040c6f5c.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.87f48f42.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.ae972b36.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.b094b87c.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.34207f33.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.ade5a7e0.js" rel="prefetch"><link href="dist/js/chunk-2d0ba309.c9a9ab22.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.db6065c6.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.1ffa155d.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.e44018ef.js" rel="prefetch"><link href="dist/js/chunk-2d0c14fc.17d4f60a.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.e63d640f.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.2803233f.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.77611624.js" rel="prefetch"><link href="dist/js/chunk-2d0c20be.57f5b62e.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.8f26961c.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.6c0d2c1e.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.8277bd5a.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.32d1bf7e.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.836132f8.js" rel="prefetch"><link href="dist/js/chunk-2d0ddf37.7b4a470a.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.00dad103.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.8750dc1f.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.4f35fbc4.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.a4640577.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.bd5197f5.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.586c30fb.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.c7c6517f.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.0e33676c.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.4cf834ac.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.e60ccf9b.js" rel="prefetch"><link href="dist/js/chunk-2d216004.fde7548f.js" rel="prefetch"><link href="dist/js/chunk-2d217907.3772894a.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.5947204c.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.0bdd83ab.js" rel="prefetch"><link href="dist/js/chunk-2d22bd06.1447b6d2.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.4fa18681.js" rel="prefetch"><link href="dist/js/chunk-2d238428.61fffbf5.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.8cc0c770.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.c097b26d.css" rel="preload" as="style"><link href="dist/js/app.d14e7a91.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.524ee6e1.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.c097b26d.css" rel="stylesheet"><link href="dist/css/app.8cc0c770.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.524ee6e1.js"></script><script src="dist/js/app.d14e7a91.js"></script></body></html>
|
||||
BIN
qrcode.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
@@ -900,6 +900,17 @@ const data5 = {
|
||||
}
|
||||
}
|
||||
|
||||
// 富文本数据v0.4.0+,需要使用RichText插件才支持富文本编辑
|
||||
const richTextData = {
|
||||
"root": {
|
||||
"data": {
|
||||
"text": "<a href='http://lxqnsys.com/' target='_blank'>理想去年实验室</a>",
|
||||
"richText": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
}
|
||||
|
||||
const rootData = {
|
||||
"root": {
|
||||
"data": {
|
||||
|
||||
@@ -31,6 +31,12 @@
|
||||
"isActive": false
|
||||
},
|
||||
"children": []
|
||||
}, {
|
||||
"data": {
|
||||
"text": "<a href='http://lxqnsys.com/' target='_blank'>理想去年实验室</a>",
|
||||
"richText": true
|
||||
},
|
||||
"children": []
|
||||
}]
|
||||
}]
|
||||
},
|
||||
|
||||
20
simple-mind-map/full.js
Normal file
@@ -0,0 +1,20 @@
|
||||
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 xmind from './src/parse/xmind.js'
|
||||
|
||||
MindMap.xmind = xmind
|
||||
|
||||
MindMap
|
||||
.usePlugin(MiniMap)
|
||||
.usePlugin(Watermark)
|
||||
.usePlugin(Drag)
|
||||
.usePlugin(KeyboardNavigation)
|
||||
.usePlugin(Export)
|
||||
.usePlugin(Select)
|
||||
|
||||
export default MindMap
|
||||
@@ -7,16 +7,9 @@ import Style from './src/Style'
|
||||
import KeyCommand from './src/KeyCommand'
|
||||
import Command from './src/Command'
|
||||
import BatchExecution from './src/BatchExecution'
|
||||
import Export from './src/Export'
|
||||
import Select from './src/Select'
|
||||
import Drag from './src/Drag'
|
||||
import MiniMap from './src/MiniMap'
|
||||
import Watermark from './src/Watermark'
|
||||
import { layoutValueList } from './src/utils/constant'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import xmind from './src/parse/xmind'
|
||||
import { simpleDeepClone } from './src/utils'
|
||||
import KeyboardNavigation from './src/KeyboardNavigation'
|
||||
import defaultTheme from './src/themes/default'
|
||||
|
||||
// 默认选项配置
|
||||
@@ -66,7 +59,16 @@ const defaultOpt = {
|
||||
opacity: 0.5,
|
||||
fontSize: 14
|
||||
}
|
||||
}
|
||||
},
|
||||
// 达到该宽度文本自动换行
|
||||
textAutoWrapWidth: 500,
|
||||
// 自定义鼠标滚轮事件处理
|
||||
// 可以传一个函数,回调参数为事件对象
|
||||
customHandleMousewheel: null,
|
||||
// 鼠标滚动的行为,如果customHandleMousewheel传了自定义函数,这个属性不生效
|
||||
mousewheelAction: 'zoom',// zoom(放大缩小)、move(上下移动)
|
||||
// 当mousewheelAction设为move时,可以通过该属性控制鼠标滚动一下视图移动的步长,单位px
|
||||
mousewheelMoveStep: 100
|
||||
}
|
||||
|
||||
// 思维导图
|
||||
@@ -120,39 +122,14 @@ class MindMap {
|
||||
draw: this.draw
|
||||
})
|
||||
|
||||
// 小地图类
|
||||
this.miniMap = new MiniMap({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 导出类
|
||||
this.doExport = new Export({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 选择类
|
||||
this.select = new Select({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 拖动类
|
||||
this.drag = new Drag({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 键盘导航类
|
||||
this.keyboardNavigation = new KeyboardNavigation({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 水印类
|
||||
this.watermark = new Watermark({
|
||||
mindMap: this
|
||||
})
|
||||
|
||||
// 批量执行类
|
||||
this.batchExecution = new BatchExecution()
|
||||
|
||||
// 注册插件
|
||||
MindMap.pluginList.forEach((plugin) => {
|
||||
this.initPlugin(plugin)
|
||||
})
|
||||
|
||||
// 初始渲染
|
||||
this.reRender()
|
||||
setTimeout(() => {
|
||||
@@ -172,21 +149,21 @@ class MindMap {
|
||||
}
|
||||
|
||||
// 渲染,部分渲染
|
||||
render() {
|
||||
render(callback) {
|
||||
this.batchExecution.push('render', () => {
|
||||
this.initTheme()
|
||||
this.renderer.reRender = false
|
||||
this.renderer.render()
|
||||
this.renderer.render(callback)
|
||||
})
|
||||
}
|
||||
|
||||
// 重新渲染
|
||||
reRender() {
|
||||
reRender(callback) {
|
||||
this.batchExecution.push('render', () => {
|
||||
this.draw.clear()
|
||||
this.initTheme()
|
||||
this.renderer.reRender = true
|
||||
this.renderer.render()
|
||||
this.renderer.render(callback)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -355,9 +332,98 @@ class MindMap {
|
||||
}
|
||||
this.emit('mode_change', mode)
|
||||
}
|
||||
|
||||
// 获取svg数据
|
||||
getSvgData() {
|
||||
const svg = this.svg
|
||||
const draw = this.draw
|
||||
// 保存原始信息
|
||||
const origWidth = svg.width()
|
||||
const origHeight = svg.height()
|
||||
const origTransform = draw.transform()
|
||||
const elRect = this.el.getBoundingClientRect()
|
||||
// 去除放大缩小的变换效果
|
||||
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
let clone = svg.clone()
|
||||
// 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题
|
||||
if ((rect.width > origWidth || rect.height > origHeight) && this.watermark && this.watermark.hasWatermark()) {
|
||||
this.width = rect.width
|
||||
this.height = rect.height
|
||||
this.watermark.draw()
|
||||
clone = svg.clone()
|
||||
this.width = origWidth
|
||||
this.height = origHeight
|
||||
this.watermark.draw()
|
||||
}
|
||||
// 恢复原先的大小和变换信息
|
||||
svg.size(origWidth, origHeight)
|
||||
draw.transform(origTransform)
|
||||
|
||||
return {
|
||||
svg: clone, // 思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组)
|
||||
svgHTML: clone.svg(), // svg字符串
|
||||
rect: {
|
||||
...rect, // 思维导图图形未缩放时的位置尺寸等信息
|
||||
ratio: rect.width / rect.height // 思维导图图形的宽高比
|
||||
},
|
||||
origWidth, // 画布宽度
|
||||
origHeight, // 画布高度
|
||||
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
|
||||
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值
|
||||
}
|
||||
}
|
||||
|
||||
// 添加插件
|
||||
addPlugin(plugin, opt) {
|
||||
let index = MindMap.hasPlugin(plugin)
|
||||
if (index === -1) {
|
||||
MindMap.usePlugin(plugin, opt)
|
||||
this.initPlugin(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
// 移除插件
|
||||
removePlugin(plugin) {
|
||||
let index = MindMap.hasPlugin(plugin)
|
||||
if (index !== -1) {
|
||||
MindMap.pluginList.splice(index, 1)
|
||||
if (this[plugin.instanceName]) {
|
||||
if (this[plugin.instanceName].beforePluginRemove) {
|
||||
this[plugin.instanceName].beforePluginRemove()
|
||||
}
|
||||
delete this[plugin.instanceName]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化插件
|
||||
initPlugin(plugin) {
|
||||
this[plugin.instanceName] = new plugin({
|
||||
mindMap: this,
|
||||
pluginOpt: plugin.pluginOpt
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MindMap.xmind = xmind
|
||||
// 插件列表
|
||||
MindMap.pluginList = []
|
||||
MindMap.usePlugin = (plugin, opt = {}) => {
|
||||
plugin.pluginOpt = opt
|
||||
MindMap.pluginList.push(plugin)
|
||||
return MindMap
|
||||
}
|
||||
MindMap.hasPlugin = (plugin) => {
|
||||
return MindMap.pluginList.findIndex((item) => {
|
||||
return item === plugin
|
||||
})
|
||||
}
|
||||
|
||||
// 定义新主题
|
||||
MindMap.defineTheme = (name, config = {}) => {
|
||||
|
||||
451
simple-mind-map/package-lock.json
generated
@@ -1,19 +1,21 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.2.21",
|
||||
"version": "0.4.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "0.2.21",
|
||||
"version": "0.4.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",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"quill": "^1.3.6",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -225,7 +227,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
@@ -251,6 +252,18 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -294,6 +307,14 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -351,7 +372,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
@@ -373,6 +393,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/deep-equal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
|
||||
"dependencies": {
|
||||
"is-arguments": "^1.0.4",
|
||||
"is-date-object": "^1.0.1",
|
||||
"is-regex": "^1.0.4",
|
||||
"object-is": "^1.0.1",
|
||||
"object-keys": "^1.1.1",
|
||||
"regexp.prototype.flags": "^1.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -387,6 +423,21 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/define-properties": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
|
||||
"dependencies": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -586,12 +637,22 @@
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-diff": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
|
||||
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
@@ -671,6 +732,32 @@
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/functions-have-names": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
@@ -724,6 +811,17 @@
|
||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -733,11 +831,46 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
@@ -800,6 +933,35 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-date-object": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||
"dependencies": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@@ -830,6 +992,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-regex": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
|
||||
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
@@ -969,6 +1146,29 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -1030,6 +1230,11 @@
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"node_modules/parchment": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
|
||||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -1132,6 +1337,37 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/quill": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
|
||||
"integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
|
||||
"dependencies": {
|
||||
"clone": "^2.1.1",
|
||||
"deep-equal": "^1.0.1",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"extend": "^3.0.1",
|
||||
"parchment": "^1.1.4",
|
||||
"quill-delta": "^3.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-delta": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
|
||||
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
|
||||
"dependencies": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"extend": "^3.0.2",
|
||||
"fast-diff": "1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/quill/node_modules/eventemitter3": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
|
||||
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
|
||||
},
|
||||
"node_modules/raf": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||
@@ -1159,6 +1395,22 @@
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
|
||||
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"functions-have-names": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
@@ -1336,7 +1588,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
@@ -1389,7 +1640,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
@@ -1593,8 +1843,7 @@
|
||||
"base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"optional": true
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
@@ -1611,6 +1860,15 @@
|
||||
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
|
||||
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g=="
|
||||
},
|
||||
"call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -1642,6 +1900,11 @@
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -1688,7 +1951,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
@@ -1702,6 +1964,19 @@
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
|
||||
"requires": {
|
||||
"is-arguments": "^1.0.4",
|
||||
"is-date-object": "^1.0.1",
|
||||
"is-regex": "^1.0.4",
|
||||
"object-is": "^1.0.1",
|
||||
"object-keys": "^1.1.1",
|
||||
"regexp.prototype.flags": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -1713,6 +1988,15 @@
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
|
||||
"integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ=="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
|
||||
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
|
||||
"requires": {
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"object-keys": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -1860,12 +2144,22 @@
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-diff": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
|
||||
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
@@ -1933,6 +2227,26 @@
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"functions-have-names": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
@@ -1971,17 +2285,45 @@
|
||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-property-descriptors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
|
||||
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
|
||||
"requires": {
|
||||
"get-intrinsic": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||
},
|
||||
"has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"requires": {
|
||||
"has-symbols": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
@@ -2029,6 +2371,23 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-date-object": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||
"requires": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@@ -2050,6 +2409,15 @@
|
||||
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-regex": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
|
||||
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
@@ -2168,6 +2536,20 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||
"dev": true
|
||||
},
|
||||
"object-is": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -2214,6 +2596,11 @@
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"parchment": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
|
||||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -2275,6 +2662,36 @@
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true
|
||||
},
|
||||
"quill": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
|
||||
"integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
|
||||
"requires": {
|
||||
"clone": "^2.1.1",
|
||||
"deep-equal": "^1.0.1",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"extend": "^3.0.1",
|
||||
"parchment": "^1.1.4",
|
||||
"quill-delta": "^3.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"eventemitter3": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
|
||||
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"quill-delta": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
|
||||
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
|
||||
"requires": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"extend": "^3.0.2",
|
||||
"fast-diff": "1.1.2"
|
||||
}
|
||||
},
|
||||
"raf": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||
@@ -2302,6 +2719,16 @@
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
|
||||
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"functions-have-names": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"regexpp": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
|
||||
@@ -2419,7 +2846,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
@@ -2463,7 +2889,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.2.24",
|
||||
"version": "0.4.3",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
@@ -22,14 +22,16 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"module": "index.js",
|
||||
"main": "./dist/simpleMindMap.umd.min.js",
|
||||
"__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",
|
||||
"jspdf": "^2.5.1",
|
||||
"jszip": "^3.10.1",
|
||||
"quill": "^1.3.6",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -72,6 +72,9 @@ class Command {
|
||||
|
||||
// 添加回退数据
|
||||
addHistory() {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
let data = this.getCopyData()
|
||||
this.history.push(simpleDeepClone(data))
|
||||
this.activeHistoryIndex = this.history.length - 1
|
||||
@@ -85,6 +88,9 @@ class Command {
|
||||
|
||||
// 回退
|
||||
back(step = 1) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
if (this.activeHistoryIndex - step >= 0) {
|
||||
this.activeHistoryIndex -= step
|
||||
this.mindMap.emit(
|
||||
@@ -92,17 +98,24 @@ class Command {
|
||||
this.activeHistoryIndex,
|
||||
this.history.length
|
||||
)
|
||||
return simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
let data = simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
this.mindMap.emit('data_change', data)
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
// 前进
|
||||
forward(step = 1) {
|
||||
if (this.mindMap.opt.readonly) {
|
||||
return
|
||||
}
|
||||
let len = this.history.length
|
||||
if (this.activeHistoryIndex + step <= len - 1) {
|
||||
this.activeHistoryIndex += step
|
||||
this.mindMap.emit('back_forward', this.activeHistoryIndex)
|
||||
return simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
let data = simpleDeepClone(this.history[this.activeHistoryIndex])
|
||||
this.mindMap.emit('data_change', data)
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -260,4 +260,6 @@ class Drag extends Base {
|
||||
}
|
||||
}
|
||||
|
||||
Drag.instanceName = 'drag'
|
||||
|
||||
export default Drag
|
||||
|
||||
@@ -43,12 +43,7 @@ class Event extends EventEmitter {
|
||||
this.mindMap.svg.on('mousedown', this.onSvgMousedown)
|
||||
window.addEventListener('mousemove', this.onMousemove)
|
||||
window.addEventListener('mouseup', this.onMouseup)
|
||||
// 兼容火狐浏览器
|
||||
if (window.navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
|
||||
this.mindMap.el.addEventListener('DOMMouseScroll', this.onMousewheel)
|
||||
} else {
|
||||
this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
|
||||
}
|
||||
this.mindMap.el.addEventListener('wheel', this.onMousewheel)
|
||||
this.mindMap.svg.on('contextmenu', this.onContextmenu)
|
||||
window.addEventListener('keyup', this.onKeyup)
|
||||
}
|
||||
@@ -59,7 +54,7 @@ class Event extends EventEmitter {
|
||||
this.mindMap.el.removeEventListener('mousedown', this.onMousedown)
|
||||
window.removeEventListener('mousemove', this.onMousemove)
|
||||
window.removeEventListener('mouseup', this.onMouseup)
|
||||
this.mindMap.el.removeEventListener('mousewheel', this.onMousewheel)
|
||||
this.mindMap.el.removeEventListener('wheel', this.onMousewheel)
|
||||
this.mindMap.svg.off('contextmenu', this.onContextmenu)
|
||||
window.removeEventListener('keyup', this.onKeyup)
|
||||
}
|
||||
@@ -110,10 +105,16 @@ class Event extends EventEmitter {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
let dir
|
||||
if ((e.wheelDeltaY || e.detail) > 0) {
|
||||
dir = 'up'
|
||||
// 解决mac触控板双指缩放方向相反的问题
|
||||
if (e.ctrlKey) {
|
||||
if (e.deltaY > 0) dir = 'up'
|
||||
if (e.deltaY < 0) dir = 'down'
|
||||
} else {
|
||||
dir = 'down'
|
||||
if ((e.wheelDeltaY || e.detail) > 0) {
|
||||
dir = 'up'
|
||||
} else {
|
||||
dir = 'down'
|
||||
}
|
||||
}
|
||||
this.emit('mousewheel', e, dir, this)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { imgToDataUrl, downloadFile } from './utils'
|
||||
import JsPDF from 'jspdf'
|
||||
import { SVG } from '@svgdotjs/svg.js'
|
||||
import drawBackgroundImageToCanvas from './utils/simulateCSSBackgroundInCanvas'
|
||||
const URL = window.URL || window.webkitURL || window
|
||||
|
||||
// 导出类
|
||||
@@ -25,8 +26,8 @@ class Export {
|
||||
}
|
||||
|
||||
// 获取svg数据
|
||||
async getSvgData() {
|
||||
let { svg, svgHTML } = this.mindMap.miniMap.getMiniMap()
|
||||
async getSvgData(domToImage) {
|
||||
let { svg, svgHTML } = this.mindMap.getSvgData()
|
||||
// 把图片的url转换成data:url类型,否则导出会丢失图片
|
||||
let imageList = svg.find('image')
|
||||
let task = imageList.map(async item => {
|
||||
@@ -35,9 +36,17 @@ class Export {
|
||||
item.attr('href', imgData)
|
||||
})
|
||||
await Promise.all(task)
|
||||
// 如果开启了富文本编辑,需要把svg中的dom元素转换成图片
|
||||
let nodeWithDomToImg = null
|
||||
if (domToImage && this.mindMap.richText) {
|
||||
let res = await this.mindMap.richText.handleSvgDomElements(svg)
|
||||
nodeWithDomToImg = res.svg
|
||||
svgHTML = res.svgHTML
|
||||
}
|
||||
return {
|
||||
node: svg,
|
||||
str: svgHTML
|
||||
str: svgHTML,
|
||||
nodeWithDomToImg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +94,9 @@ class Export {
|
||||
let {
|
||||
backgroundColor = '#fff',
|
||||
backgroundImage,
|
||||
backgroundRepeat = 'repeat'
|
||||
backgroundRepeat = 'no-repeat',
|
||||
backgroundPosition = 'center center',
|
||||
backgroundSize = 'cover',
|
||||
} = this.mindMap.themeConfig
|
||||
// 背景颜色
|
||||
ctx.save()
|
||||
@@ -96,19 +107,18 @@ class Export {
|
||||
// 背景图片
|
||||
if (backgroundImage && backgroundImage !== 'none') {
|
||||
ctx.save()
|
||||
let img = new Image()
|
||||
img.src = backgroundImage
|
||||
img.onload = () => {
|
||||
let pat = ctx.createPattern(img, backgroundRepeat)
|
||||
ctx.rect(0, 0, width, height)
|
||||
ctx.fillStyle = pat
|
||||
ctx.fill()
|
||||
drawBackgroundImageToCanvas(ctx, width, height, backgroundImage, {
|
||||
backgroundRepeat,
|
||||
backgroundPosition,
|
||||
backgroundSize
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
img.onerror = e => {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
@@ -121,7 +131,7 @@ class Export {
|
||||
* 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换
|
||||
*/
|
||||
async png() {
|
||||
let { str } = await this.getSvgData()
|
||||
let { str } = await this.getSvgData(true)
|
||||
// 转换成blob数据
|
||||
let blob = new Blob([str], {
|
||||
type: 'image/svg+xml'
|
||||
@@ -189,8 +199,21 @@ class Export {
|
||||
}
|
||||
|
||||
// 导出为svg
|
||||
async svg(name) {
|
||||
let { node } = await this.getSvgData()
|
||||
// domToImage:是否将svg中的dom节点转换成图片的形式
|
||||
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
|
||||
async svg(name, domToImage = false, plusCssText) {
|
||||
let { node, nodeWithDomToImg } = await this.getSvgData(domToImage)
|
||||
// 开启了节点富文本编辑
|
||||
if (this.mindMap.richText) {
|
||||
if (domToImage) {
|
||||
node = nodeWithDomToImg
|
||||
} else if (plusCssText) {
|
||||
let foreignObjectList = node.find('foreignObject')
|
||||
if (foreignObjectList.length > 0) {
|
||||
foreignObjectList[0].add(SVG(`<style>${plusCssText}</style>`))
|
||||
}
|
||||
}
|
||||
}
|
||||
node.first().before(SVG(`<title>${name}</title>`))
|
||||
await this.drawBackgroundToSvg(node)
|
||||
let str = node.svg()
|
||||
@@ -215,4 +238,6 @@ class Export {
|
||||
}
|
||||
}
|
||||
|
||||
Export.instanceName = 'doExport'
|
||||
|
||||
export default Export
|
||||
|
||||
@@ -2,7 +2,7 @@ import { isKey } from './utils/keyMap'
|
||||
import { bfsWalk } from './utils'
|
||||
|
||||
// 键盘导航类
|
||||
export default class KeyboardNavigation {
|
||||
class KeyboardNavigation {
|
||||
// 构造函数
|
||||
constructor(opt) {
|
||||
this.opt = opt
|
||||
@@ -224,3 +224,7 @@ export default class KeyboardNavigation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardNavigation.instanceName = 'keyboardNavigation'
|
||||
|
||||
export default KeyboardNavigation
|
||||
@@ -14,43 +14,6 @@ class MiniMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取小地图相关数据
|
||||
getMiniMap() {
|
||||
const svg = this.mindMap.svg
|
||||
const draw = this.mindMap.draw
|
||||
// 保存原始信息
|
||||
const origWidth = svg.width()
|
||||
const origHeight = svg.height()
|
||||
const origTransform = draw.transform()
|
||||
const elRect = this.mindMap.el.getBoundingClientRect()
|
||||
// 去除放大缩小的变换效果
|
||||
draw.scale(1 / origTransform.scaleX, 1 / origTransform.scaleY)
|
||||
// 获取变换后的位置尺寸信息,其实是getBoundingClientRect方法的包装方法
|
||||
const rect = draw.rbox()
|
||||
// 将svg设置为实际内容的宽高
|
||||
svg.size(rect.width, rect.height)
|
||||
// 把实际内容变换
|
||||
draw.translate(-rect.x + elRect.left, -rect.y + elRect.top)
|
||||
// 克隆一份数据
|
||||
const clone = svg.clone()
|
||||
// 恢复原先的大小和变换信息
|
||||
svg.size(origWidth, origHeight)
|
||||
draw.transform(origTransform)
|
||||
|
||||
return {
|
||||
svg: clone, // 思维导图图形的整体svg元素,包括:svg(画布容器)、g(实际的思维导图组)
|
||||
svgHTML: clone.svg(), // svg字符串
|
||||
rect: {
|
||||
...rect, // 思维导图图形未缩放时的位置尺寸等信息
|
||||
ratio: rect.width / rect.height // 思维导图图形的宽高比
|
||||
},
|
||||
origWidth, // 画布宽度
|
||||
origHeight, // 画布高度
|
||||
scaleX: origTransform.scaleX, // 思维导图图形的水平缩放值
|
||||
scaleY: origTransform.scaleY // 思维导图图形的垂直缩放值
|
||||
}
|
||||
}
|
||||
|
||||
// 计算小地图的渲染数据
|
||||
/**
|
||||
* boxWidth:小地图容器的宽度
|
||||
@@ -58,7 +21,7 @@ class MiniMap {
|
||||
*/
|
||||
calculationMiniMap(boxWidth, boxHeight) {
|
||||
let { svgHTML, rect, origWidth, origHeight, scaleX, scaleY } =
|
||||
this.getMiniMap()
|
||||
this.mindMap.getSvgData()
|
||||
// 计算数据
|
||||
let boxRatio = boxWidth / boxHeight
|
||||
let actWidth = 0
|
||||
@@ -144,4 +107,6 @@ class MiniMap {
|
||||
}
|
||||
}
|
||||
|
||||
MiniMap.instanceName = 'miniMap'
|
||||
|
||||
export default MiniMap
|
||||
|
||||
443
simple-mind-map/src/RichText.js
Normal file
@@ -0,0 +1,443 @@
|
||||
import Quill from 'quill'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import './css/quill.css'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { Image as SvgImage } from '@svgdotjs/svg.js'
|
||||
import { walk } from './utils'
|
||||
|
||||
let extended = false
|
||||
|
||||
// 扩展quill的字体列表
|
||||
let fontFamilyList = [
|
||||
'宋体, SimSun, Songti SC',
|
||||
'微软雅黑, Microsoft YaHei',
|
||||
'楷体, 楷体_GB2312, SimKai, STKaiti',
|
||||
'黑体, SimHei, Heiti SC',
|
||||
'隶书, SimLi',
|
||||
'andale mono',
|
||||
'arial, helvetica, sans-serif',
|
||||
'arial black, avant garde',
|
||||
'comic sans ms',
|
||||
'impact, chicago',
|
||||
'times new roman',
|
||||
'sans-serif',
|
||||
'serif'
|
||||
]
|
||||
|
||||
// 扩展quill的字号列表
|
||||
let fontSizeList = new Array(100).fill(0).map((_, index) => {
|
||||
return index + 'px'
|
||||
})
|
||||
|
||||
// 节点支持富文本编辑功能
|
||||
class RichText {
|
||||
constructor({ mindMap, pluginOpt }) {
|
||||
this.mindMap = mindMap
|
||||
this.pluginOpt = pluginOpt
|
||||
this.textEditNode = null
|
||||
this.showTextEdit = false
|
||||
this.quill = null
|
||||
this.range = null
|
||||
this.lastRange = null
|
||||
this.node = null
|
||||
this.initOpt()
|
||||
this.extendQuill()
|
||||
}
|
||||
|
||||
// 处理选项参数
|
||||
initOpt() {
|
||||
if (
|
||||
this.pluginOpt.fontFamilyList &&
|
||||
Array.isArray(this.pluginOpt.fontFamilyList)
|
||||
) {
|
||||
fontFamilyList = this.pluginOpt.fontFamilyList
|
||||
}
|
||||
if (
|
||||
this.pluginOpt.fontSizeList &&
|
||||
Array.isArray(this.pluginOpt.fontSizeList)
|
||||
) {
|
||||
fontSizeList = this.pluginOpt.fontSizeList
|
||||
}
|
||||
}
|
||||
|
||||
// 扩展quill编辑器
|
||||
extendQuill() {
|
||||
if (extended) {
|
||||
return
|
||||
}
|
||||
extended = true
|
||||
|
||||
// 扩展quill的字体列表
|
||||
const FontAttributor = Quill.import('attributors/class/font')
|
||||
FontAttributor.whitelist = fontFamilyList
|
||||
Quill.register(FontAttributor, true)
|
||||
|
||||
const FontStyle = Quill.import('attributors/style/font')
|
||||
FontStyle.whitelist = fontFamilyList
|
||||
Quill.register(FontStyle, true)
|
||||
|
||||
// 扩展quill的字号列表
|
||||
const SizeAttributor = Quill.import('attributors/class/size')
|
||||
SizeAttributor.whitelist = fontSizeList
|
||||
Quill.register(SizeAttributor, true)
|
||||
|
||||
const SizeStyle = Quill.import('attributors/style/size')
|
||||
SizeStyle.whitelist = fontSizeList
|
||||
Quill.register(SizeStyle, true)
|
||||
}
|
||||
|
||||
// 显示文本编辑控件
|
||||
showEditText(node, rect) {
|
||||
if (this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
this.node = node
|
||||
if (!rect) rect = node._textData.node.node.getBoundingClientRect()
|
||||
this.mindMap.emit('before_show_text_edit')
|
||||
this.mindMap.renderer.textEdit.registerTmpShortcut()
|
||||
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);outline: none; word-break: break-all;`
|
||||
document.body.appendChild(this.textEditNode)
|
||||
}
|
||||
// 原始宽高
|
||||
let g = node._textData.node
|
||||
let originWidth = g.attr('data-width')
|
||||
let originHeight = g.attr('data-height')
|
||||
this.textEditNode.style.minWidth = originWidth + 'px'
|
||||
this.textEditNode.style.minHeight = originHeight + 'px'
|
||||
this.textEditNode.style.left =
|
||||
rect.left + (rect.width - originWidth) / 2 + 'px'
|
||||
this.textEditNode.style.top =
|
||||
rect.top + (rect.height - originHeight) / 2 + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth + 'px'
|
||||
this.textEditNode.style.transform = `scale(${rect.width / originWidth}, ${
|
||||
rect.height / originHeight
|
||||
})`
|
||||
if (!node.nodeData.data.richText) {
|
||||
// 还不是富文本的情况
|
||||
let text = node.nodeData.data.text.split(/\n/gim).join('<br>')
|
||||
let html = `<p>${text}</p>`
|
||||
this.textEditNode.innerHTML = html
|
||||
} else {
|
||||
this.textEditNode.innerHTML = node.nodeData.data.text
|
||||
}
|
||||
this.initQuillEditor()
|
||||
document.querySelector('.ql-editor').style.minHeight = originHeight + 'px'
|
||||
this.showTextEdit = true
|
||||
this.selectAll()
|
||||
if (!node.nodeData.data.richText) {
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
this.setTextStyleIfNotRichText(node)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
setTextStyleIfNotRichText(node) {
|
||||
let style = {
|
||||
font: node.style.merge('fontFamily'),
|
||||
color: node.style.merge('color'),
|
||||
italic: node.style.merge('fontStyle') === 'italic',
|
||||
bold: node.style.merge('fontWeight') === 'bold',
|
||||
size: node.style.merge('fontSize') + 'px',
|
||||
underline: node.style.merge('textDecoration') === 'underline',
|
||||
strike: node.style.merge('textDecoration') === 'line-through'
|
||||
}
|
||||
this.formatText(style)
|
||||
}
|
||||
|
||||
// 隐藏文本编辑控件,即完成编辑
|
||||
hideEditText() {
|
||||
if (!this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
let html = this.quill.container.firstChild.innerHTML
|
||||
// 去除最后的空行
|
||||
html = html.replace(/<p><br><\/p>$/, '')
|
||||
this.mindMap.renderer.activeNodeList.forEach(node => {
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', node, html, true)
|
||||
if (node.isGeneralization) {
|
||||
// 概要节点
|
||||
node.generalizationBelongNode.updateGeneralization()
|
||||
}
|
||||
this.mindMap.render()
|
||||
})
|
||||
this.mindMap.emit(
|
||||
'hide_text_edit',
|
||||
this.textEditNode,
|
||||
this.mindMap.renderer.activeNodeList
|
||||
)
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.showTextEdit = false
|
||||
this.mindMap.emit('rich_text_selection_change', false)
|
||||
this.node = null
|
||||
}
|
||||
|
||||
// 初始化Quill富文本编辑器
|
||||
initQuillEditor() {
|
||||
this.quill = new Quill(this.textEditNode, {
|
||||
modules: {
|
||||
toolbar: false,
|
||||
keyboard: {
|
||||
bindings: {
|
||||
enter: {
|
||||
key: 13,
|
||||
handler: function () {
|
||||
// 覆盖默认的回车键换行
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
theme: 'snow'
|
||||
})
|
||||
this.quill.on('selection-change', range => {
|
||||
this.lastRange = this.range
|
||||
this.range = null
|
||||
if (range) {
|
||||
let bounds = this.quill.getBounds(range.index, range.length)
|
||||
let rect = this.textEditNode.getBoundingClientRect()
|
||||
let rectInfo = {
|
||||
left: bounds.left + rect.left,
|
||||
top: bounds.top + rect.top,
|
||||
right: bounds.right + rect.left,
|
||||
bottom: bounds.bottom + rect.top,
|
||||
width: bounds.width
|
||||
}
|
||||
let formatInfo = this.quill.getFormat(range.index, range.length)
|
||||
let hasRange = false
|
||||
if (range.length == 0) {
|
||||
hasRange = false
|
||||
} else {
|
||||
this.range = range
|
||||
hasRange = true
|
||||
}
|
||||
this.mindMap.emit(
|
||||
'rich_text_selection_change',
|
||||
hasRange,
|
||||
rectInfo,
|
||||
formatInfo
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 选中全部
|
||||
selectAll() {
|
||||
this.quill.setSelection(0, this.quill.getLength())
|
||||
}
|
||||
|
||||
// 格式化当前选中的文本
|
||||
formatText(config = {}, clear = false) {
|
||||
if (!this.range && !this.lastRange) return
|
||||
this.syncFormatToNodeConfig(config, clear)
|
||||
let rangeLost = !this.range
|
||||
let range = rangeLost ? this.lastRange : this.range
|
||||
clear ? this.quill.removeFormat(range.index, range.length) : this.quill.formatText(range.index, range.length, config)
|
||||
if (rangeLost) {
|
||||
this.quill.setSelection(this.lastRange.index, this.lastRange.length)
|
||||
}
|
||||
}
|
||||
|
||||
// 清除当前选中文本的样式
|
||||
removeFormat() {
|
||||
this.formatText({}, true)
|
||||
}
|
||||
|
||||
// 格式化指定范围的文本
|
||||
formatRangeText(range, config = {}) {
|
||||
if (!range) return
|
||||
this.syncFormatToNodeConfig(config)
|
||||
this.quill.formatText(range.index, range.length, config)
|
||||
}
|
||||
|
||||
// 格式化所有文本
|
||||
formatAllText(config = {}) {
|
||||
this.syncFormatToNodeConfig(config)
|
||||
this.quill.formatText(0, this.quill.getLength(), config)
|
||||
}
|
||||
|
||||
// 同步格式化到节点样式配置
|
||||
syncFormatToNodeConfig(config, clear) {
|
||||
if (!this.node) return
|
||||
if (clear) {
|
||||
// 清除文本样式
|
||||
['fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'textDecoration', 'color'].forEach((prop) => {
|
||||
delete this.node.nodeData.data[prop]
|
||||
})
|
||||
} else {
|
||||
let data = this.richTextStyleToNormalStyle(config)
|
||||
this.mindMap.renderer.setNodeData(this.node, data)
|
||||
}
|
||||
}
|
||||
|
||||
// 将普通节点样式对象转换成富文本样式对象
|
||||
normalStyleToRichTextStyle(style) {
|
||||
let config = {}
|
||||
Object.keys(style).forEach(prop => {
|
||||
let value = style[prop]
|
||||
switch (prop) {
|
||||
case 'fontFamily':
|
||||
config.font = value
|
||||
break
|
||||
case 'fontSize':
|
||||
config.size = value + 'px'
|
||||
break
|
||||
case 'fontWeight':
|
||||
config.bold = value === 'bold'
|
||||
break
|
||||
case 'fontStyle':
|
||||
config.italic = value === 'italic'
|
||||
break
|
||||
case 'textDecoration':
|
||||
config.underline = value === 'underline'
|
||||
config.strike = value === 'line-through'
|
||||
break
|
||||
case 'color':
|
||||
config.color = value
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
return config
|
||||
}
|
||||
|
||||
// 将富文本样式对象转换成普通节点样式对象
|
||||
richTextStyleToNormalStyle(config) {
|
||||
let data = {}
|
||||
Object.keys(config).forEach(prop => {
|
||||
let value = config[prop]
|
||||
switch (prop) {
|
||||
case 'font':
|
||||
data.fontFamily = value
|
||||
break
|
||||
case 'size':
|
||||
data.fontSize = parseFloat(value)
|
||||
break
|
||||
case 'bold':
|
||||
data.fontWeight = value ? 'bold' : 'normal'
|
||||
break
|
||||
case 'italic':
|
||||
data.fontStyle = value ? 'italic' : 'normal'
|
||||
break
|
||||
case 'underline':
|
||||
data.textDecoration = value ? 'underline' : 'none'
|
||||
break
|
||||
case 'strike':
|
||||
data.textDecoration = value ? 'line-through' : 'none'
|
||||
break
|
||||
case 'color':
|
||||
data.color = value
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// 将svg中嵌入的dom元素转换成图片
|
||||
async _handleSvgDomElements(svg) {
|
||||
svg = svg.clone()
|
||||
let foreignObjectList = svg.find('foreignObject')
|
||||
let task = foreignObjectList.map(async item => {
|
||||
let clone = item.first().node.cloneNode(true)
|
||||
let div = document.createElement('div')
|
||||
div.style.cssText = `position: fixed; left: -999999px;`
|
||||
div.appendChild(clone)
|
||||
this.mindMap.el.appendChild(div)
|
||||
let canvas = await html2canvas(clone, {
|
||||
backgroundColor: null
|
||||
})
|
||||
this.mindMap.el.removeChild(div)
|
||||
let imgNode = new SvgImage()
|
||||
.load(canvas.toDataURL())
|
||||
.size(canvas.width, canvas.height)
|
||||
item.replace(imgNode)
|
||||
})
|
||||
await Promise.all(task)
|
||||
return {
|
||||
svg: svg,
|
||||
svgHTML: svg.svg()
|
||||
}
|
||||
}
|
||||
|
||||
// 将svg中嵌入的dom元素转换成图片
|
||||
handleSvgDomElements(svg) {
|
||||
return new Promise((resolve, reject) => {
|
||||
svg = svg.clone()
|
||||
let foreignObjectList = svg.find('foreignObject')
|
||||
let index = 0
|
||||
let len = foreignObjectList.length
|
||||
let transform = async () => {
|
||||
this.mindMap.emit('transforming-dom-to-images', index, len)
|
||||
try {
|
||||
let item = foreignObjectList[index++]
|
||||
let parent = item.parent()
|
||||
let clone = item.first().node.cloneNode(true)
|
||||
let div = document.createElement('div')
|
||||
div.style.cssText = `position: fixed; left: -999999px;`
|
||||
div.appendChild(clone)
|
||||
this.mindMap.el.appendChild(div)
|
||||
let canvas = await html2canvas(clone, {
|
||||
backgroundColor: null
|
||||
})
|
||||
// 优先使用原始宽高,因为当设备的window.devicePixelRatio不为1时,html2canvas输出的图片会更大
|
||||
let imgNodeWidth = parent.attr('data-width') || canvas.width
|
||||
let imgNodeHeight = parent.attr('data-height') || canvas.height
|
||||
this.mindMap.el.removeChild(div)
|
||||
let imgNode = new SvgImage()
|
||||
.load(canvas.toDataURL())
|
||||
.size(imgNodeWidth, imgNodeHeight)
|
||||
.x((parent ? parent.attr('data-offsetx') : 0) || 0)
|
||||
item.replace(imgNode)
|
||||
if (index <= len - 1) {
|
||||
setTimeout(() => {
|
||||
transform()
|
||||
}, 0)
|
||||
} else {
|
||||
resolve({
|
||||
svg: svg,
|
||||
svgHTML: svg.svg()
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
if (len > 0) transform()
|
||||
})
|
||||
}
|
||||
|
||||
// 将所有节点转换成非富文本节点
|
||||
transformAllNodesToNormalNode() {
|
||||
let div = document.createElement('div')
|
||||
walk(
|
||||
this.mindMap.renderer.renderTree,
|
||||
null,
|
||||
node => {
|
||||
if (node.data.richText) {
|
||||
node.data.richText = false
|
||||
div.innerHTML = node.data.text
|
||||
node.data.text = div.textContent
|
||||
}
|
||||
},
|
||||
null,
|
||||
true,
|
||||
0,
|
||||
0
|
||||
)
|
||||
this.mindMap.reRender()
|
||||
}
|
||||
|
||||
// 插件被移除前做的事情
|
||||
beforePluginRemove() {
|
||||
this.transformAllNodesToNormalNode()
|
||||
}
|
||||
}
|
||||
|
||||
RichText.instanceName = 'richText'
|
||||
|
||||
export default RichText
|
||||
@@ -167,4 +167,6 @@ class Select {
|
||||
}
|
||||
}
|
||||
|
||||
Select.instanceName = 'select'
|
||||
|
||||
export default Select
|
||||
|
||||
@@ -1,167 +1,200 @@
|
||||
import { tagColorList } from './utils/constant'
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
|
||||
// 样式类
|
||||
|
||||
class Style {
|
||||
// 设置背景样式
|
||||
|
||||
static setBackgroundStyle(el, themeConfig) {
|
||||
let { backgroundColor, backgroundImage, backgroundRepeat } = themeConfig
|
||||
el.style.backgroundColor = backgroundColor
|
||||
if (backgroundImage) {
|
||||
el.style.backgroundImage = `url(${backgroundImage})`
|
||||
el.style.backgroundRepeat = backgroundRepeat
|
||||
}
|
||||
}
|
||||
|
||||
// 构造函数
|
||||
|
||||
constructor(ctx, themeConfig) {
|
||||
this.ctx = ctx
|
||||
this.themeConfig = themeConfig
|
||||
}
|
||||
|
||||
// 更新主题配置
|
||||
|
||||
updateThemeConfig(themeConfig) {
|
||||
this.themeConfig = themeConfig
|
||||
}
|
||||
|
||||
// 合并样式
|
||||
|
||||
merge(prop, root, isActive) {
|
||||
// 三级及以下节点
|
||||
let defaultConfig = this.themeConfig.node
|
||||
if (root || rootProp.includes(prop)) {
|
||||
// 直接使用最外层样式
|
||||
defaultConfig = this.themeConfig
|
||||
} else if (this.ctx.isGeneralization) {
|
||||
// 概要节点
|
||||
defaultConfig = this.themeConfig.generalization
|
||||
} else if (this.ctx.layerIndex === 0) {
|
||||
// 根节点
|
||||
defaultConfig = this.themeConfig.root
|
||||
} else if (this.ctx.layerIndex === 1) {
|
||||
// 二级节点
|
||||
defaultConfig = this.themeConfig.second
|
||||
}
|
||||
// 激活状态
|
||||
if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) {
|
||||
if (
|
||||
this.ctx.nodeData.data.activeStyle &&
|
||||
this.ctx.nodeData.data.activeStyle[prop] !== undefined
|
||||
) {
|
||||
return this.ctx.nodeData.data.activeStyle[prop]
|
||||
} else if (defaultConfig.active && defaultConfig.active[prop]) {
|
||||
return defaultConfig.active[prop]
|
||||
}
|
||||
}
|
||||
// 优先使用节点本身的样式
|
||||
return this.getSelfStyle(prop) !== undefined
|
||||
? this.getSelfStyle(prop)
|
||||
: defaultConfig[prop]
|
||||
}
|
||||
|
||||
// 获取某个样式值
|
||||
|
||||
getStyle(prop, root, isActive) {
|
||||
return this.merge(prop, root, isActive)
|
||||
}
|
||||
|
||||
// 获取自身自定义样式
|
||||
|
||||
getSelfStyle(prop) {
|
||||
return this.ctx.nodeData.data[prop]
|
||||
}
|
||||
|
||||
// 矩形
|
||||
|
||||
rect(node) {
|
||||
this.shape(node)
|
||||
node.radius(this.merge('borderRadius'))
|
||||
}
|
||||
|
||||
// 矩形外的其他形状
|
||||
|
||||
shape(node) {
|
||||
node.fill({
|
||||
color: this.merge('fillColor')
|
||||
})
|
||||
// 节点使用横线样式,不需要渲染非激活状态的边框样式
|
||||
if (
|
||||
!this.ctx.isRoot &&
|
||||
!this.ctx.isGeneralization &&
|
||||
this.themeConfig.nodeUseLineStyle &&
|
||||
!this.ctx.nodeData.data.isActive
|
||||
) {
|
||||
return
|
||||
}
|
||||
node.stroke({
|
||||
color: this.merge('borderColor'),
|
||||
width: this.merge('borderWidth'),
|
||||
dasharray: this.merge('borderDasharray')
|
||||
})
|
||||
}
|
||||
|
||||
// 文字
|
||||
|
||||
text(node) {
|
||||
node
|
||||
.fill({
|
||||
color: this.merge('color')
|
||||
})
|
||||
.css({
|
||||
'font-family': this.merge('fontFamily'),
|
||||
'font-size': this.merge('fontSize'),
|
||||
'font-weight': this.merge('fontWeight'),
|
||||
'font-style': this.merge('fontStyle'),
|
||||
'text-decoration': this.merge('textDecoration')
|
||||
})
|
||||
}
|
||||
|
||||
// html文字节点
|
||||
|
||||
domText(node, fontSizeScale = 1) {
|
||||
node.style.fontFamily = this.merge('fontFamily')
|
||||
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
|
||||
node.style.fontWeight = this.merge('fontWeight') || 'normal'
|
||||
}
|
||||
|
||||
// 标签文字
|
||||
|
||||
tagText(node, index) {
|
||||
node
|
||||
.fill({
|
||||
color: tagColorList[index].color
|
||||
})
|
||||
.css({
|
||||
'font-size': '12px'
|
||||
})
|
||||
}
|
||||
|
||||
// 标签矩形
|
||||
|
||||
tagRect(node, index) {
|
||||
node.fill({
|
||||
color: tagColorList[index].background
|
||||
})
|
||||
}
|
||||
|
||||
// 内置图标
|
||||
|
||||
iconNode(node) {
|
||||
node.attr({
|
||||
fill: this.merge('color')
|
||||
})
|
||||
}
|
||||
|
||||
// 连线
|
||||
|
||||
line(node, { width, color, dasharray } = {}) {
|
||||
node.stroke({ width, color, dasharray }).fill({ color: 'none' })
|
||||
}
|
||||
|
||||
// 概要连线
|
||||
|
||||
generalizationLine(node) {
|
||||
import { tagColorList } from './utils/constant'
|
||||
const rootProp = ['paddingX', 'paddingY']
|
||||
|
||||
// 样式类
|
||||
|
||||
class Style {
|
||||
// 设置背景样式
|
||||
|
||||
static setBackgroundStyle(el, themeConfig) {
|
||||
let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig
|
||||
el.style.backgroundColor = backgroundColor
|
||||
if (backgroundImage) {
|
||||
el.style.backgroundImage = `url(${backgroundImage})`
|
||||
el.style.backgroundRepeat = backgroundRepeat
|
||||
el.style.backgroundPosition = backgroundPosition
|
||||
el.style.backgroundSize = backgroundSize
|
||||
} else {
|
||||
el.style.backgroundImage = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
// 构造函数
|
||||
|
||||
constructor(ctx, themeConfig) {
|
||||
this.ctx = ctx
|
||||
this.themeConfig = themeConfig
|
||||
}
|
||||
|
||||
// 更新主题配置
|
||||
|
||||
updateThemeConfig(themeConfig) {
|
||||
this.themeConfig = themeConfig
|
||||
}
|
||||
|
||||
// 合并样式
|
||||
|
||||
merge(prop, root, isActive) {
|
||||
// 三级及以下节点
|
||||
let defaultConfig = this.themeConfig.node
|
||||
if (root || rootProp.includes(prop)) {
|
||||
// 直接使用最外层样式
|
||||
defaultConfig = this.themeConfig
|
||||
} else if (this.ctx.isGeneralization) {
|
||||
// 概要节点
|
||||
defaultConfig = this.themeConfig.generalization
|
||||
} else if (this.ctx.layerIndex === 0) {
|
||||
// 根节点
|
||||
defaultConfig = this.themeConfig.root
|
||||
} else if (this.ctx.layerIndex === 1) {
|
||||
// 二级节点
|
||||
defaultConfig = this.themeConfig.second
|
||||
}
|
||||
// 激活状态
|
||||
if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) {
|
||||
if (
|
||||
this.ctx.nodeData.data.activeStyle &&
|
||||
this.ctx.nodeData.data.activeStyle[prop] !== undefined
|
||||
) {
|
||||
return this.ctx.nodeData.data.activeStyle[prop]
|
||||
} else if (defaultConfig.active && defaultConfig.active[prop]) {
|
||||
return defaultConfig.active[prop]
|
||||
}
|
||||
}
|
||||
// 优先使用节点本身的样式
|
||||
return this.getSelfStyle(prop) !== undefined
|
||||
? this.getSelfStyle(prop)
|
||||
: defaultConfig[prop]
|
||||
}
|
||||
|
||||
// 获取某个样式值
|
||||
|
||||
getStyle(prop, root, isActive) {
|
||||
return this.merge(prop, root, isActive)
|
||||
}
|
||||
|
||||
// 获取自身自定义样式
|
||||
|
||||
getSelfStyle(prop) {
|
||||
return this.ctx.nodeData.data[prop]
|
||||
}
|
||||
|
||||
// 矩形
|
||||
|
||||
rect(node) {
|
||||
this.shape(node)
|
||||
node.radius(this.merge('borderRadius'))
|
||||
}
|
||||
|
||||
// 矩形外的其他形状
|
||||
|
||||
shape(node) {
|
||||
node.fill({
|
||||
color: this.merge('fillColor')
|
||||
})
|
||||
// 节点使用横线样式,不需要渲染非激活状态的边框样式
|
||||
if (
|
||||
!this.ctx.isRoot &&
|
||||
!this.ctx.isGeneralization &&
|
||||
this.themeConfig.nodeUseLineStyle &&
|
||||
!this.ctx.nodeData.data.isActive
|
||||
) {
|
||||
return
|
||||
}
|
||||
node.stroke({
|
||||
color: this.merge('borderColor'),
|
||||
width: this.merge('borderWidth'),
|
||||
dasharray: this.merge('borderDasharray')
|
||||
})
|
||||
}
|
||||
|
||||
// 文字
|
||||
|
||||
text(node) {
|
||||
node
|
||||
.fill({
|
||||
color: this.merge('color')
|
||||
})
|
||||
.css({
|
||||
'font-family': this.merge('fontFamily'),
|
||||
'font-size': this.merge('fontSize'),
|
||||
'font-weight': this.merge('fontWeight'),
|
||||
'font-style': this.merge('fontStyle'),
|
||||
'text-decoration': this.merge('textDecoration')
|
||||
})
|
||||
}
|
||||
|
||||
// 获取文本样式
|
||||
getTextFontStyle() {
|
||||
return {
|
||||
italic: this.merge('fontStyle') === 'italic',
|
||||
bold: this.merge('fontWeight'),
|
||||
fontSize: this.merge('fontSize'),
|
||||
fontFamily: this.merge('fontFamily')
|
||||
}
|
||||
}
|
||||
|
||||
// html文字节点
|
||||
|
||||
domText(node, fontSizeScale = 1) {
|
||||
node.style.fontFamily = this.merge('fontFamily')
|
||||
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
|
||||
node.style.fontWeight = this.merge('fontWeight') || 'normal'
|
||||
node.style.lineHeight = this.merge('lineHeight')
|
||||
node.style.fontStyle = this.merge('fontStyle')
|
||||
}
|
||||
|
||||
// 标签文字
|
||||
|
||||
tagText(node, index) {
|
||||
node
|
||||
.fill({
|
||||
color: tagColorList[index].color
|
||||
})
|
||||
.css({
|
||||
'font-size': '12px'
|
||||
})
|
||||
}
|
||||
|
||||
// 标签矩形
|
||||
|
||||
tagRect(node, index) {
|
||||
node.fill({
|
||||
color: tagColorList[index].background
|
||||
})
|
||||
}
|
||||
|
||||
// 内置图标
|
||||
|
||||
iconNode(node) {
|
||||
node.attr({
|
||||
fill: this.merge('color')
|
||||
})
|
||||
}
|
||||
|
||||
// 连线
|
||||
|
||||
line(node, { width, color, dasharray } = {}) {
|
||||
node.stroke({ width, color, dasharray }).fill({ color: 'none' })
|
||||
}
|
||||
|
||||
// 概要连线
|
||||
|
||||
generalizationLine(node) {
|
||||
node
|
||||
.stroke({
|
||||
width: this.merge('generalizationLineWidth', true),
|
||||
color: this.merge('generalizationLineColor', true)
|
||||
})
|
||||
.fill({ color: 'none' })
|
||||
}
|
||||
|
||||
// 按钮
|
||||
|
||||
iconBtn(node, fillNode) {
|
||||
node.fill({ color: '#808080' })
|
||||
fillNode.fill({ color: '#fff' })
|
||||
}
|
||||
}
|
||||
|
||||
export default Style
|
||||
|
||||
@@ -50,7 +50,12 @@ export default class TextEdit {
|
||||
|
||||
// 显示文本编辑框
|
||||
show(node) {
|
||||
this.showEditTextBox(node, node._textData.node.node.getBoundingClientRect())
|
||||
let rect = node._textData.node.node.getBoundingClientRect()
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.showEditText(node, rect)
|
||||
return
|
||||
}
|
||||
this.showEditTextBox(node, rect)
|
||||
}
|
||||
|
||||
// 显示文本编辑框
|
||||
@@ -59,14 +64,17 @@ export default class TextEdit {
|
||||
this.registerTmpShortcut()
|
||||
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;`
|
||||
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;`
|
||||
this.textEditNode.setAttribute('contenteditable', true)
|
||||
this.textEditNode.addEventListener('keyup', e => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
document.body.appendChild(this.textEditNode)
|
||||
}
|
||||
node.style.domText(this.textEditNode, this.mindMap.view.scale)
|
||||
let scale = this.mindMap.view.scale
|
||||
let lineHeight = node.style.merge('lineHeight')
|
||||
let fontSize = node.style.merge('fontSize')
|
||||
node.style.domText(this.textEditNode, scale)
|
||||
this.textEditNode.innerHTML = node.nodeData.data.text
|
||||
.split(/\n/gim)
|
||||
.join('<br>')
|
||||
@@ -75,6 +83,8 @@ export default class TextEdit {
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth * scale + 'px'
|
||||
this.textEditNode.style.transform = `translateY(${-(lineHeight * fontSize - fontSize) / 2 * scale}px)`
|
||||
this.showTextEdit = true
|
||||
// 选中文本
|
||||
this.selectNodeText()
|
||||
@@ -91,6 +101,9 @@ export default class TextEdit {
|
||||
|
||||
// 隐藏文本编辑框
|
||||
hideEditTextBox() {
|
||||
if (this.mindMap.richText) {
|
||||
return this.mindMap.richText.hideEditText()
|
||||
}
|
||||
if (!this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,12 +55,25 @@ class View {
|
||||
})
|
||||
// 放大缩小视图
|
||||
this.mindMap.event.on('mousewheel', (e, dir) => {
|
||||
// // 放大
|
||||
if (dir === 'down') {
|
||||
this.enlarge()
|
||||
if (this.mindMap.opt.customHandleMousewheel && typeof this.mindMap.opt.customHandleMousewheel === 'function') {
|
||||
return this.mindMap.opt.customHandleMousewheel(e)
|
||||
}
|
||||
if (this.mindMap.opt.mousewheelAction === 'zoom') {
|
||||
// 放大
|
||||
if (dir === 'down') {
|
||||
this.enlarge()
|
||||
} else {
|
||||
// 缩小
|
||||
this.narrow()
|
||||
}
|
||||
} else {
|
||||
// 缩小
|
||||
this.narrow()
|
||||
// 上移
|
||||
if (dir === 'down') {
|
||||
this.translateY(-this.mindMap.opt.mousewheelMoveStep)
|
||||
} else {
|
||||
// 下移
|
||||
this.translateY(this.mindMap.opt.mousewheelMoveStep)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { degToRad, camelCaseToHyphen } from './utils'
|
||||
import merge from 'deepmerge'
|
||||
|
||||
// 水印类
|
||||
export default class Watermark {
|
||||
class Watermark {
|
||||
constructor(opt = {}) {
|
||||
this.mindMap = opt.mindMap
|
||||
this.lineSpacing = 0 // 水印行间距
|
||||
@@ -20,6 +20,11 @@ export default class Watermark {
|
||||
this.updateWatermark(this.mindMap.opt.watermarkConfig || {})
|
||||
}
|
||||
|
||||
// 获取是否存在水印
|
||||
hasWatermark() {
|
||||
return !!this.text.trim()
|
||||
}
|
||||
|
||||
// 处理水印配置
|
||||
handleConfig({ text, lineSpacing, textSpacing, angle, textStyle }) {
|
||||
this.text = text === undefined ? '' : String(text).trim()
|
||||
@@ -36,7 +41,7 @@ export default class Watermark {
|
||||
// 非精确绘制,会绘制一些超出可视区域的水印
|
||||
draw() {
|
||||
this.watermarkDraw.clear()
|
||||
if (!this.text.trim()) {
|
||||
if (!this.hasWatermark()) {
|
||||
return
|
||||
}
|
||||
let x = 0
|
||||
@@ -109,3 +114,7 @@ export default class Watermark {
|
||||
this.draw()
|
||||
}
|
||||
}
|
||||
|
||||
Watermark.instanceName = 'watermark'
|
||||
|
||||
export default Watermark
|
||||
10
simple-mind-map/src/css/quill.css
Normal file
@@ -0,0 +1,10 @@
|
||||
.ql-editor {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.ql-container {
|
||||
height: auto;
|
||||
font-size: inherit;
|
||||
}
|
||||
@@ -1,267 +1,289 @@
|
||||
import Base from './Base'
|
||||
import { walk, asyncRun } from '../utils'
|
||||
|
||||
// 逻辑结构图
|
||||
|
||||
class LogicalStructure extends Base {
|
||||
// 构造函数
|
||||
|
||||
constructor(opt = {}) {
|
||||
super(opt)
|
||||
}
|
||||
|
||||
// 布局
|
||||
|
||||
doLayout(callback) {
|
||||
let task = [
|
||||
() => {
|
||||
this.computedBaseValue()
|
||||
},
|
||||
() => {
|
||||
this.computedTopValue()
|
||||
},
|
||||
() => {
|
||||
this.adjustTopValue()
|
||||
},
|
||||
() => {
|
||||
callback(this.root)
|
||||
}
|
||||
]
|
||||
asyncRun(task)
|
||||
}
|
||||
|
||||
// 遍历数据计算节点的left、width、height
|
||||
|
||||
computedBaseValue() {
|
||||
walk(
|
||||
this.renderer.renderTree,
|
||||
null,
|
||||
(cur, parent, isRoot, layerIndex) => {
|
||||
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
|
||||
// 根节点定位在画布中心位置
|
||||
if (isRoot) {
|
||||
this.setNodeCenter(newNode)
|
||||
} else {
|
||||
// 非根节点
|
||||
// 定位到父节点右侧
|
||||
newNode.left =
|
||||
parent._node.left + parent._node.width + this.getMarginX(layerIndex)
|
||||
}
|
||||
if (!cur.data.expand) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
(cur, parent, isRoot, layerIndex) => {
|
||||
// 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距
|
||||
let len = cur.data.expand === false ? 0 : cur._node.children.length
|
||||
cur._node.childrenAreaHeight = len
|
||||
? cur._node.children.reduce((h, item) => {
|
||||
return h + item.height
|
||||
}, 0) +
|
||||
(len + 1) * this.getMarginY(layerIndex + 1)
|
||||
: 0
|
||||
},
|
||||
true,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
// 遍历节点树计算节点的top
|
||||
|
||||
computedTopValue() {
|
||||
walk(
|
||||
this.root,
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex) => {
|
||||
if (
|
||||
node.nodeData.data.expand &&
|
||||
node.children &&
|
||||
node.children.length
|
||||
) {
|
||||
let marginY = this.getMarginY(layerIndex + 1)
|
||||
// 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
|
||||
let top = node.top + node.height / 2 - node.childrenAreaHeight / 2
|
||||
let totalTop = top + marginY
|
||||
node.children.forEach(cur => {
|
||||
cur.top = totalTop
|
||||
totalTop += cur.height + marginY
|
||||
})
|
||||
}
|
||||
},
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 调整节点top
|
||||
|
||||
adjustTopValue() {
|
||||
walk(
|
||||
this.root,
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex) => {
|
||||
if (!node.nodeData.data.expand) {
|
||||
return
|
||||
}
|
||||
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
|
||||
let difference =
|
||||
node.childrenAreaHeight -
|
||||
this.getMarginY(layerIndex + 1) * 2 -
|
||||
node.height
|
||||
if (difference > 0) {
|
||||
this.updateBrothers(node, difference / 2)
|
||||
}
|
||||
},
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 更新兄弟节点的top
|
||||
|
||||
updateBrothers(node, addHeight) {
|
||||
if (node.parent) {
|
||||
let childrenList = node.parent.children
|
||||
let index = childrenList.findIndex(item => {
|
||||
return item === node
|
||||
})
|
||||
childrenList.forEach((item, _index) => {
|
||||
if (item === node || item.hasCustomPosition()) {
|
||||
// 适配自定义位置
|
||||
return
|
||||
}
|
||||
let _offset = 0
|
||||
// 上面的节点往上移
|
||||
if (_index < index) {
|
||||
_offset = -addHeight
|
||||
} else if (_index > index) {
|
||||
// 下面的节点往下移
|
||||
_offset = addHeight
|
||||
}
|
||||
item.top += _offset
|
||||
// 同步更新子节点的位置
|
||||
if (item.children && item.children.length) {
|
||||
this.updateChildren(item.children, 'top', _offset)
|
||||
}
|
||||
})
|
||||
// 更新父节点的位置
|
||||
this.updateBrothers(node.parent, addHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制连线,连接该节点到其子节点
|
||||
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'curve') {
|
||||
this.renderLineCurve(node, lines, style)
|
||||
} else if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
// 直线风格连线
|
||||
|
||||
renderLineStraight(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let s1 = (marginX - expandBtnSize) * 0.6
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0 ? left + width : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
? item.width
|
||||
: 0
|
||||
let path = `M ${x1},${y1} L ${x1 + s1},${y1} L ${x1 + s1},${y2} L ${
|
||||
x2 + nodeUseLineStyleOffset
|
||||
},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
// 直连风格
|
||||
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
? ` L ${item.left + item.width},${y2}`
|
||||
: ''
|
||||
let path = `M ${x1},${y1} L ${x2},${y2}` + nodeUseLineStylePath
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
// 曲线风格连线
|
||||
|
||||
renderLineCurve(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = ''
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
? ` L ${item.left + item.width},${y2}`
|
||||
: ''
|
||||
if (node.isRoot) {
|
||||
path = this.quadraticCurvePath(x1, y1, x2, y2) + nodeUseLineStylePath
|
||||
} else {
|
||||
path = this.cubicBezierPath(x1, y1, x2, y2) + nodeUseLineStylePath
|
||||
}
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染按钮
|
||||
|
||||
renderExpandBtn(node, btn) {
|
||||
let { width, height } = node
|
||||
let { translateX, translateY } = btn.transform()
|
||||
// 节点使用横线风格,需要调整展开收起按钮位置
|
||||
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
? height / 2
|
||||
: 0
|
||||
btn.translate(
|
||||
width - translateX,
|
||||
height / 2 - translateY + nodeUseLineStyleOffset
|
||||
)
|
||||
}
|
||||
|
||||
// 创建概要节点
|
||||
|
||||
renderGeneralization(node, gLine, gNode) {
|
||||
let {
|
||||
top,
|
||||
bottom,
|
||||
right,
|
||||
generalizationLineMargin,
|
||||
generalizationNodeMargin
|
||||
} = this.getNodeBoundaries(node, 'h')
|
||||
let x1 = right + generalizationLineMargin
|
||||
import Base from './Base'
|
||||
import { walk, asyncRun } from '../utils'
|
||||
|
||||
// 逻辑结构图
|
||||
|
||||
class LogicalStructure extends Base {
|
||||
// 构造函数
|
||||
|
||||
constructor(opt = {}) {
|
||||
super(opt)
|
||||
}
|
||||
|
||||
// 布局
|
||||
|
||||
doLayout(callback) {
|
||||
let task = [
|
||||
() => {
|
||||
this.computedBaseValue()
|
||||
},
|
||||
() => {
|
||||
this.computedTopValue()
|
||||
},
|
||||
() => {
|
||||
this.adjustTopValue()
|
||||
},
|
||||
() => {
|
||||
callback(this.root)
|
||||
}
|
||||
]
|
||||
asyncRun(task)
|
||||
}
|
||||
|
||||
// 遍历数据计算节点的left、width、height
|
||||
|
||||
computedBaseValue() {
|
||||
walk(
|
||||
this.renderer.renderTree,
|
||||
null,
|
||||
(cur, parent, isRoot, layerIndex) => {
|
||||
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
|
||||
// 根节点定位在画布中心位置
|
||||
if (isRoot) {
|
||||
this.setNodeCenter(newNode)
|
||||
} else {
|
||||
// 非根节点
|
||||
// 定位到父节点右侧
|
||||
newNode.left =
|
||||
parent._node.left + parent._node.width + this.getMarginX(layerIndex)
|
||||
}
|
||||
if (!cur.data.expand) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
(cur, parent, isRoot, layerIndex) => {
|
||||
// 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距
|
||||
let len = cur.data.expand === false ? 0 : cur._node.children.length
|
||||
cur._node.childrenAreaHeight = len
|
||||
? cur._node.children.reduce((h, item) => {
|
||||
return h + item.height
|
||||
}, 0) +
|
||||
(len + 1) * this.getMarginY(layerIndex + 1)
|
||||
: 0
|
||||
},
|
||||
true,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
// 遍历节点树计算节点的top
|
||||
|
||||
computedTopValue() {
|
||||
walk(
|
||||
this.root,
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex) => {
|
||||
if (
|
||||
node.nodeData.data.expand &&
|
||||
node.children &&
|
||||
node.children.length
|
||||
) {
|
||||
let marginY = this.getMarginY(layerIndex + 1)
|
||||
// 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
|
||||
let top = node.top + node.height / 2 - node.childrenAreaHeight / 2
|
||||
let totalTop = top + marginY
|
||||
node.children.forEach(cur => {
|
||||
cur.top = totalTop
|
||||
totalTop += cur.height + marginY
|
||||
})
|
||||
}
|
||||
},
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 调整节点top
|
||||
|
||||
adjustTopValue() {
|
||||
walk(
|
||||
this.root,
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex) => {
|
||||
if (!node.nodeData.data.expand) {
|
||||
return
|
||||
}
|
||||
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
|
||||
let difference =
|
||||
node.childrenAreaHeight -
|
||||
this.getMarginY(layerIndex + 1) * 2 -
|
||||
node.height
|
||||
if (difference > 0) {
|
||||
this.updateBrothers(node, difference / 2)
|
||||
}
|
||||
},
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 更新兄弟节点的top
|
||||
|
||||
updateBrothers(node, addHeight) {
|
||||
if (node.parent) {
|
||||
let childrenList = node.parent.children
|
||||
let index = childrenList.findIndex(item => {
|
||||
return item === node
|
||||
})
|
||||
childrenList.forEach((item, _index) => {
|
||||
if (item === node || item.hasCustomPosition()) {
|
||||
// 适配自定义位置
|
||||
return
|
||||
}
|
||||
let _offset = 0
|
||||
// 上面的节点往上移
|
||||
if (_index < index) {
|
||||
_offset = -addHeight
|
||||
} else if (_index > index) {
|
||||
// 下面的节点往下移
|
||||
_offset = addHeight
|
||||
}
|
||||
item.top += _offset
|
||||
// 同步更新子节点的位置
|
||||
if (item.children && item.children.length) {
|
||||
this.updateChildren(item.children, 'top', _offset)
|
||||
}
|
||||
})
|
||||
// 更新父节点的位置
|
||||
this.updateBrothers(node.parent, addHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制连线,连接该节点到其子节点
|
||||
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'curve') {
|
||||
this.renderLineCurve(node, lines, style)
|
||||
} else if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
// 直线风格连线
|
||||
|
||||
renderLineStraight(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let s1 = (marginX - expandBtnSize) * 0.6
|
||||
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0 ? left + width : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStyleOffset = nodeUseLineStyle
|
||||
? item.width
|
||||
: 0
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
let path = `M ${x1},${y1} L ${x1 + s1},${y1} L ${x1 + s1},${y2} L ${
|
||||
x2 + nodeUseLineStyleOffset
|
||||
},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
// 直连风格
|
||||
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = nodeUseLineStyle
|
||||
? ` L ${item.left + item.width},${y2}`
|
||||
: ''
|
||||
let path = `M ${x1},${y1} L ${x2},${y2}` + nodeUseLineStylePath
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
// 曲线风格连线
|
||||
|
||||
renderLineCurve(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0 ? left + width / 2 : left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = ''
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = nodeUseLineStyle
|
||||
? ` L ${item.left + item.width},${y2}`
|
||||
: ''
|
||||
if (node.isRoot) {
|
||||
path = this.quadraticCurvePath(x1, y1, x2, y2) + nodeUseLineStylePath
|
||||
} else {
|
||||
path = this.cubicBezierPath(x1, y1, x2, y2) + nodeUseLineStylePath
|
||||
}
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染按钮
|
||||
|
||||
renderExpandBtn(node, btn) {
|
||||
let { width, height } = node
|
||||
let { translateX, translateY } = btn.transform()
|
||||
// 节点使用横线风格,需要调整展开收起按钮位置
|
||||
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
? height / 2
|
||||
: 0
|
||||
btn.translate(
|
||||
width - translateX,
|
||||
height / 2 - translateY + nodeUseLineStyleOffset
|
||||
)
|
||||
}
|
||||
|
||||
// 创建概要节点
|
||||
|
||||
renderGeneralization(node, gLine, gNode) {
|
||||
let {
|
||||
top,
|
||||
bottom,
|
||||
right,
|
||||
generalizationLineMargin,
|
||||
generalizationNodeMargin
|
||||
} = this.getNodeBoundaries(node, 'h')
|
||||
let x1 = right + generalizationLineMargin
|
||||
let y1 = top
|
||||
let x2 = right + generalizationLineMargin
|
||||
let y2 = bottom
|
||||
let cx = x1 + 20
|
||||
let cy = y1 + (y2 - y1) / 2
|
||||
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
|
||||
gLine.plot(path)
|
||||
gNode.left = right + generalizationNodeMargin
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
}
|
||||
|
||||
export default LogicalStructure
|
||||
|
||||
@@ -208,11 +208,12 @@ class MindMap extends Base {
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let s1 = (marginX - expandBtnSize) * 0.6
|
||||
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 = 0
|
||||
let _s = 0
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStyleOffset = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
let nodeUseLineStyleOffset = nodeUseLineStyle
|
||||
? item.width
|
||||
: 0
|
||||
if (item.dir === 'left') {
|
||||
@@ -226,6 +227,8 @@ class MindMap extends Base {
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
let path = `M ${x1},${y1} L ${x1 + _s},${y1} L ${x1 + _s},${y2} L ${
|
||||
x2 + nodeUseLineStyleOffset
|
||||
},${y2}`
|
||||
@@ -241,6 +244,7 @@ class MindMap extends Base {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0
|
||||
@@ -251,9 +255,11 @@ class MindMap extends Base {
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = ''
|
||||
if (this.mindMap.themeConfig.nodeUseLineStyle) {
|
||||
if (nodeUseLineStyle) {
|
||||
if (item.dir === 'left') {
|
||||
nodeUseLineStylePath = ` L ${item.left},${y2}`
|
||||
} else {
|
||||
@@ -273,6 +279,7 @@ class MindMap extends Base {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
let nodeUseLineStyle = this.mindMap.themeConfig.nodeUseLineStyle
|
||||
node.children.forEach((item, index) => {
|
||||
let x1 =
|
||||
node.layerIndex === 0
|
||||
@@ -284,6 +291,8 @@ class MindMap extends Base {
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = ''
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = ''
|
||||
if (this.mindMap.themeConfig.nodeUseLineStyle) {
|
||||
|
||||
@@ -1,142 +1,147 @@
|
||||
// 默认主题
|
||||
|
||||
export default {
|
||||
// 节点内边距
|
||||
paddingX: 15,
|
||||
paddingY: 5,
|
||||
// 图片显示的最大宽度
|
||||
imgMaxWidth: 100,
|
||||
// 图片显示的最大高度
|
||||
imgMaxHeight: 100,
|
||||
// icon的大小
|
||||
iconSize: 20,
|
||||
// 连线的粗细
|
||||
lineWidth: 1,
|
||||
// 连线的颜色
|
||||
lineColor: '#549688',
|
||||
// 连线样式
|
||||
lineDasharray: 'none',
|
||||
// 连线风格
|
||||
lineStyle: 'straight', // 针对logicalStructure、mindMap两种结构。曲线(curve)、直线(straight)、直连(direct)
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#549688',
|
||||
// 概要曲线距节点的距离
|
||||
generalizationLineMargin: 0,
|
||||
// 概要节点距节点的距离
|
||||
generalizationNodeMargin: 20,
|
||||
// 背景颜色
|
||||
backgroundColor: '#fafafa',
|
||||
// 背景图片
|
||||
backgroundImage: 'none',
|
||||
// 背景重复
|
||||
backgroundRepeat: 'no-repeat',
|
||||
// 节点使用横线样式
|
||||
nodeUseLineStyle: false,
|
||||
// 根节点样式
|
||||
root: {
|
||||
shape: 'rectangle',
|
||||
fillColor: '#549688',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#fff',
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
shape: 'rectangle',
|
||||
marginX: 100,
|
||||
marginY: 40,
|
||||
fillColor: '#fff',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#565656',
|
||||
fontSize: 16,
|
||||
fontWeight: 'noraml',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
shape: 'rectangle',
|
||||
marginX: 50,
|
||||
marginY: 0,
|
||||
fillColor: 'transparent',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#6a6d6c',
|
||||
fontSize: 14,
|
||||
fontWeight: 'noraml',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderRadius: 5,
|
||||
borderDasharray: 'none',
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
shape: 'rectangle',
|
||||
marginX: 100,
|
||||
marginY: 40,
|
||||
fillColor: '#fff',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#565656',
|
||||
fontSize: 16,
|
||||
fontWeight: 'noraml',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 支持激活样式的属性
|
||||
// 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小
|
||||
export const supportActiveStyle = [
|
||||
'fillColor',
|
||||
'color',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'borderDasharray',
|
||||
'borderRadius',
|
||||
'textDecoration'
|
||||
]
|
||||
|
||||
// 默认主题
|
||||
|
||||
export default {
|
||||
// 节点内边距
|
||||
paddingX: 15,
|
||||
paddingY: 5,
|
||||
// 图片显示的最大宽度
|
||||
imgMaxWidth: 100,
|
||||
// 图片显示的最大高度
|
||||
imgMaxHeight: 100,
|
||||
// icon的大小
|
||||
iconSize: 20,
|
||||
// 连线的粗细
|
||||
lineWidth: 1,
|
||||
// 连线的颜色
|
||||
lineColor: '#549688',
|
||||
// 连线样式
|
||||
lineDasharray: 'none',
|
||||
// 连线风格
|
||||
lineStyle: 'straight', // 针对logicalStructure、mindMap两种结构。曲线(curve)、直线(straight)、直连(direct)
|
||||
// 概要连线的粗细
|
||||
generalizationLineWidth: 1,
|
||||
// 概要连线的颜色
|
||||
generalizationLineColor: '#549688',
|
||||
// 概要曲线距节点的距离
|
||||
generalizationLineMargin: 0,
|
||||
// 概要节点距节点的距离
|
||||
generalizationNodeMargin: 20,
|
||||
// 背景颜色
|
||||
backgroundColor: '#fafafa',
|
||||
// 背景图片
|
||||
backgroundImage: 'none',
|
||||
// 背景重复
|
||||
backgroundRepeat: 'no-repeat',
|
||||
// 设置背景图像的起始位置
|
||||
backgroundPosition: 'center center',
|
||||
// 设置背景图片大小
|
||||
backgroundSize: 'cover',
|
||||
// 节点使用横线样式
|
||||
nodeUseLineStyle: false,
|
||||
// 根节点样式
|
||||
root: {
|
||||
shape: 'rectangle',
|
||||
fillColor: '#549688',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#fff',
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
},
|
||||
// 二级节点样式
|
||||
second: {
|
||||
shape: 'rectangle',
|
||||
marginX: 100,
|
||||
marginY: 40,
|
||||
fillColor: '#fff',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#565656',
|
||||
fontSize: 16,
|
||||
fontWeight: 'noraml',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
},
|
||||
// 三级及以下节点样式
|
||||
node: {
|
||||
shape: 'rectangle',
|
||||
marginX: 50,
|
||||
marginY: 0,
|
||||
fillColor: 'transparent',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#6a6d6c',
|
||||
fontSize: 14,
|
||||
fontWeight: 'noraml',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: 'transparent',
|
||||
borderWidth: 0,
|
||||
borderRadius: 5,
|
||||
borderDasharray: 'none',
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
},
|
||||
// 概要节点样式
|
||||
generalization: {
|
||||
shape: 'rectangle',
|
||||
marginX: 100,
|
||||
marginY: 40,
|
||||
fillColor: '#fff',
|
||||
fontFamily: '微软雅黑, Microsoft YaHei',
|
||||
color: '#565656',
|
||||
fontSize: 16,
|
||||
fontWeight: 'noraml',
|
||||
fontStyle: 'normal',
|
||||
lineHeight: 1.5,
|
||||
borderColor: '#549688',
|
||||
borderWidth: 1,
|
||||
borderDasharray: 'none',
|
||||
borderRadius: 5,
|
||||
textDecoration: 'none',
|
||||
active: {
|
||||
borderColor: 'rgb(57, 80, 96)',
|
||||
borderWidth: 3,
|
||||
borderDasharray: 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 支持激活样式的属性
|
||||
// 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小
|
||||
export const supportActiveStyle = [
|
||||
'fillColor',
|
||||
'color',
|
||||
'fontWeight',
|
||||
'fontStyle',
|
||||
'borderColor',
|
||||
'borderWidth',
|
||||
'borderDasharray',
|
||||
'borderRadius',
|
||||
'textDecoration'
|
||||
]
|
||||
|
||||
export const lineStyleProps = ['lineColor', 'lineDasharray', 'lineWidth']
|
||||
|
||||
@@ -235,4 +235,34 @@ export const camelCaseToHyphen = (str) => {
|
||||
return str.replace(/([a-z])([A-Z])/g, (...args) => {
|
||||
return args[1] + '-' + args[2].toLowerCase()
|
||||
})
|
||||
}
|
||||
|
||||
//计算节点的文本长宽
|
||||
let measureTextContext = null
|
||||
export const measureText = (text, { italic, bold, fontSize, fontFamily }) => {
|
||||
const font = joinFontStr({
|
||||
italic,
|
||||
bold,
|
||||
fontSize,
|
||||
fontFamily
|
||||
})
|
||||
if (!measureTextContext) {
|
||||
const canvas = document.createElement('canvas')
|
||||
measureTextContext = canvas.getContext('2d')
|
||||
}
|
||||
measureTextContext.save()
|
||||
measureTextContext.font = font
|
||||
const {
|
||||
width,
|
||||
actualBoundingBoxAscent,
|
||||
actualBoundingBoxDescent
|
||||
} = measureTextContext.measureText(text)
|
||||
measureTextContext.restore()
|
||||
const height = actualBoundingBoxAscent + actualBoundingBoxDescent
|
||||
return { width, height }
|
||||
}
|
||||
|
||||
// 拼接font字符串
|
||||
export const joinFontStr = ({ italic, bold, fontSize, fontFamily }) => {
|
||||
return `${italic ? 'italic ' : ''} ${bold ? 'bold ' : ''} ${fontSize}px ${fontFamily} `
|
||||
}
|
||||
354
simple-mind-map/src/utils/simulateCSSBackgroundInCanvas.js
Normal file
@@ -0,0 +1,354 @@
|
||||
// 将以空格分隔的字符串值转换成成数字/单位/值数组
|
||||
const getNumberValueFromStr = value => {
|
||||
let arr = String(value).split(/\s+/)
|
||||
return arr.map(item => {
|
||||
if (/^[\d.]+/.test(item)) {
|
||||
// 数字+单位
|
||||
let res = /^([\d.]+)(.*)$/.exec(item)
|
||||
return [Number(res[1]), res[2]]
|
||||
} else {
|
||||
// 单个值
|
||||
return item
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 缩放宽度
|
||||
const zoomWidth = (ratio, height) => {
|
||||
// w / height = ratio
|
||||
return ratio * height
|
||||
}
|
||||
|
||||
// 缩放高度
|
||||
const zoomHeight = (ratio, width) => {
|
||||
// width / h = ratio
|
||||
return width / ratio
|
||||
}
|
||||
|
||||
// 关键词到百分比值的映射
|
||||
const keyWordToPercentageMap = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
center: 50,
|
||||
bottom: 100,
|
||||
right: 100
|
||||
}
|
||||
|
||||
// 模拟background-size
|
||||
const handleBackgroundSize = ({
|
||||
backgroundSize,
|
||||
drawOpt,
|
||||
imageRatio,
|
||||
canvasWidth,
|
||||
canvasHeight,
|
||||
canvasRatio
|
||||
}) => {
|
||||
if (backgroundSize) {
|
||||
// 将值转换成数组
|
||||
let backgroundSizeValueArr = getNumberValueFromStr(backgroundSize)
|
||||
// 两个值都为auto,那就相当于不设置
|
||||
if (
|
||||
backgroundSizeValueArr[0] === 'auto' &&
|
||||
backgroundSizeValueArr[1] === 'auto'
|
||||
) {
|
||||
return
|
||||
}
|
||||
// 值为cover
|
||||
if (backgroundSizeValueArr[0] === 'cover') {
|
||||
if (imageRatio > canvasRatio) {
|
||||
// 图片的宽高比大于canvas的宽高比,那么图片高度缩放到和canvas的高度一致,宽度自适应
|
||||
drawOpt.height = canvasHeight
|
||||
drawOpt.width = zoomWidth(imageRatio, canvasHeight)
|
||||
} else {
|
||||
// 否则图片宽度缩放到和canvas的宽度一致,高度自适应
|
||||
drawOpt.width = canvasWidth
|
||||
drawOpt.height = zoomHeight(imageRatio, canvasWidth)
|
||||
}
|
||||
return
|
||||
}
|
||||
// 值为contain
|
||||
if (backgroundSizeValueArr[0] === 'contain') {
|
||||
if (imageRatio > canvasRatio) {
|
||||
// 图片的宽高比大于canvas的宽高比,那么图片宽度缩放到和canvas的宽度一致,高度自适应
|
||||
drawOpt.width = canvasWidth
|
||||
drawOpt.height = zoomHeight(imageRatio, canvasWidth)
|
||||
} else {
|
||||
// 否则图片高度缩放到和canvas的高度一致,宽度自适应
|
||||
drawOpt.height = canvasHeight
|
||||
drawOpt.width = zoomWidth(imageRatio, canvasHeight)
|
||||
}
|
||||
return
|
||||
}
|
||||
// 图片宽度
|
||||
let newNumberWidth = -1
|
||||
if (backgroundSizeValueArr[0]) {
|
||||
if (Array.isArray(backgroundSizeValueArr[0])) {
|
||||
// 数字+单位类型
|
||||
if (backgroundSizeValueArr[0][1] === '%') {
|
||||
// %单位
|
||||
drawOpt.width = (backgroundSizeValueArr[0][0] / 100) * canvasWidth
|
||||
newNumberWidth = drawOpt.width
|
||||
} else {
|
||||
// 其他都认为是px单位
|
||||
drawOpt.width = backgroundSizeValueArr[0][0]
|
||||
newNumberWidth = backgroundSizeValueArr[0][0]
|
||||
}
|
||||
} else if (backgroundSizeValueArr[0] === 'auto') {
|
||||
// auto类型,那么根据设置的新高度以图片原宽高比进行自适应
|
||||
if (backgroundSizeValueArr[1]) {
|
||||
if (backgroundSizeValueArr[1][1] === '%') {
|
||||
// 高度为%单位
|
||||
drawOpt.width = zoomWidth(
|
||||
imageRatio,
|
||||
(backgroundSizeValueArr[1][0] / 100) * canvasHeight
|
||||
)
|
||||
} else {
|
||||
// 其他都认为是px单位
|
||||
drawOpt.width = zoomWidth(imageRatio, backgroundSizeValueArr[1][0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置了图片高度
|
||||
if (backgroundSizeValueArr[1] && Array.isArray(backgroundSizeValueArr[1])) {
|
||||
// 数字+单位类型
|
||||
if (backgroundSizeValueArr[1][1] === '%') {
|
||||
// 高度为%单位
|
||||
drawOpt.height = (backgroundSizeValueArr[1][0] / 100) * canvasHeight
|
||||
} else {
|
||||
// 其他都认为是px单位
|
||||
drawOpt.height = backgroundSizeValueArr[1][0]
|
||||
}
|
||||
} else if (newNumberWidth !== -1) {
|
||||
// 没有设置图片高度或者设置为auto,那么根据设置的新宽度以图片原宽高比进行自适应
|
||||
drawOpt.height = zoomHeight(imageRatio, newNumberWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟background-position
|
||||
const handleBackgroundPosition = ({
|
||||
backgroundPosition,
|
||||
drawOpt,
|
||||
imgWidth,
|
||||
imgHeight,
|
||||
canvasWidth,
|
||||
canvasHeight
|
||||
}) => {
|
||||
if (backgroundPosition) {
|
||||
// 将值转换成数组
|
||||
let backgroundPositionValueArr = getNumberValueFromStr(backgroundPosition)
|
||||
// 将关键词转为百分比
|
||||
backgroundPositionValueArr = backgroundPositionValueArr.map(item => {
|
||||
if (typeof item === 'string') {
|
||||
return keyWordToPercentageMap[item] !== undefined
|
||||
? [keyWordToPercentageMap[item], '%']
|
||||
: item
|
||||
}
|
||||
return item
|
||||
})
|
||||
if (Array.isArray(backgroundPositionValueArr[0])) {
|
||||
if (backgroundPositionValueArr.length === 1) {
|
||||
// 如果只设置了一个值,第二个默认为50%
|
||||
backgroundPositionValueArr.push([50, '%'])
|
||||
}
|
||||
// 水平位置
|
||||
if (backgroundPositionValueArr[0][1] === '%') {
|
||||
// 单位为%
|
||||
let canvasX = (backgroundPositionValueArr[0][0] / 100) * canvasWidth
|
||||
let imgX = (backgroundPositionValueArr[0][0] / 100) * imgWidth
|
||||
// 计算差值
|
||||
drawOpt.x = canvasX - imgX
|
||||
} else {
|
||||
// 其他单位默认都为px
|
||||
drawOpt.x = backgroundPositionValueArr[0][0]
|
||||
}
|
||||
// 垂直位置
|
||||
if (backgroundPositionValueArr[1][1] === '%') {
|
||||
// 单位为%
|
||||
let canvasY = (backgroundPositionValueArr[1][0] / 100) * canvasHeight
|
||||
let imgY = (backgroundPositionValueArr[1][0] / 100) * imgHeight
|
||||
// 计算差值
|
||||
drawOpt.y = canvasY - imgY
|
||||
} else {
|
||||
// 其他单位默认都为px
|
||||
drawOpt.y = backgroundPositionValueArr[1][0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟background-repeat
|
||||
const handleBackgroundRepeat = ({
|
||||
ctx,
|
||||
image,
|
||||
backgroundRepeat,
|
||||
drawOpt,
|
||||
imgWidth,
|
||||
imgHeight,
|
||||
canvasWidth,
|
||||
canvasHeight
|
||||
}) => {
|
||||
if (backgroundRepeat) {
|
||||
// 保存在handleBackgroundPosition中计算出来的x、y
|
||||
let ox = drawOpt.x
|
||||
let oy = drawOpt.y
|
||||
// 计算ox和oy能平铺的图片数量
|
||||
let oxRepeatNum = Math.ceil(ox / imgWidth)
|
||||
let oyRepeatNum = Math.ceil(oy / imgHeight)
|
||||
// 计算ox和oy第一张图片的位置
|
||||
let oxRepeatX = ox - oxRepeatNum * imgWidth
|
||||
let oxRepeatY = oy - oyRepeatNum * imgHeight
|
||||
// 将值转换成数组
|
||||
let backgroundRepeatValueArr = getNumberValueFromStr(backgroundRepeat)
|
||||
// 不处理
|
||||
if (
|
||||
backgroundRepeatValueArr[0] === 'no-repeat' ||
|
||||
(imgWidth >= canvasWidth && imgHeight >= canvasHeight)
|
||||
) {
|
||||
return
|
||||
}
|
||||
// 水平平铺
|
||||
if (backgroundRepeatValueArr[0] === 'repeat-x') {
|
||||
if (canvasWidth > imgWidth) {
|
||||
let x = oxRepeatX
|
||||
while (x < canvasWidth) {
|
||||
drawImage(ctx, image, {
|
||||
...drawOpt,
|
||||
x
|
||||
})
|
||||
x += imgWidth
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
// 垂直平铺
|
||||
if (backgroundRepeatValueArr[0] === 'repeat-y') {
|
||||
if (canvasHeight > imgHeight) {
|
||||
let y = oxRepeatY
|
||||
while (y < canvasHeight) {
|
||||
drawImage(ctx, image, {
|
||||
...drawOpt,
|
||||
y
|
||||
})
|
||||
y += imgHeight
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
// 平铺
|
||||
if (backgroundRepeatValueArr[0] === 'repeat') {
|
||||
let x = oxRepeatX
|
||||
while (x < canvasWidth) {
|
||||
if (canvasHeight > imgHeight) {
|
||||
let y = oxRepeatY
|
||||
while (y < canvasHeight) {
|
||||
drawImage(ctx, image, {
|
||||
...drawOpt,
|
||||
x,
|
||||
y
|
||||
})
|
||||
y += imgHeight
|
||||
}
|
||||
}
|
||||
x += imgWidth
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 根据参数绘制图片
|
||||
const drawImage = (ctx, image, drawOpt) => {
|
||||
ctx.drawImage(
|
||||
image,
|
||||
drawOpt.sx,
|
||||
drawOpt.sy,
|
||||
drawOpt.swidth,
|
||||
drawOpt.sheight,
|
||||
drawOpt.x,
|
||||
drawOpt.y,
|
||||
drawOpt.width,
|
||||
drawOpt.height
|
||||
)
|
||||
}
|
||||
|
||||
const drawBackgroundImageToCanvas = (
|
||||
ctx,
|
||||
width,
|
||||
height,
|
||||
img,
|
||||
{ backgroundSize, backgroundPosition, backgroundRepeat },
|
||||
callback = () => {}
|
||||
) => {
|
||||
// 画布的长宽比
|
||||
let canvasRatio = width / height
|
||||
// 加载图片
|
||||
let image = new Image()
|
||||
image.src = img
|
||||
image.onload = () => {
|
||||
// 图片的宽度及长宽比
|
||||
let imgWidth = image.width
|
||||
let imgHeight = image.height
|
||||
let imageRatio = imgWidth / imgHeight
|
||||
// 绘制图片
|
||||
// drawImage方法的参数值
|
||||
let drawOpt = {
|
||||
sx: 0,
|
||||
sy: 0,
|
||||
swidth: imgWidth,
|
||||
sheight: imgHeight,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: imgWidth,
|
||||
height: imgHeight
|
||||
}
|
||||
// 模拟background-size
|
||||
handleBackgroundSize({
|
||||
backgroundSize,
|
||||
drawOpt,
|
||||
imageRatio,
|
||||
canvasWidth: width,
|
||||
canvasHeight: height,
|
||||
canvasRatio
|
||||
})
|
||||
|
||||
// 模拟background-position
|
||||
handleBackgroundPosition({
|
||||
backgroundPosition,
|
||||
drawOpt,
|
||||
imgWidth: drawOpt.width,
|
||||
imgHeight: drawOpt.height,
|
||||
imageRatio,
|
||||
canvasWidth: width,
|
||||
canvasHeight: height,
|
||||
canvasRatio
|
||||
})
|
||||
|
||||
// 模拟background-repeat
|
||||
let notNeedDraw = handleBackgroundRepeat({
|
||||
ctx,
|
||||
image,
|
||||
backgroundRepeat,
|
||||
drawOpt,
|
||||
imgWidth: drawOpt.width,
|
||||
imgHeight: drawOpt.height,
|
||||
imageRatio,
|
||||
canvasWidth: width,
|
||||
canvasHeight: height,
|
||||
canvasRatio
|
||||
})
|
||||
|
||||
// 绘制图片
|
||||
if (!notNeedDraw) {
|
||||
drawImage(ctx, image, drawOpt)
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
image.onerror = e => {
|
||||
callback(e)
|
||||
}
|
||||
}
|
||||
|
||||
export default drawBackgroundImageToCanvas
|
||||
25
web/package-lock.json
generated
@@ -24,6 +24,7 @@
|
||||
"@vue/cli-plugin-eslint": "^4.5.0",
|
||||
"@vue/cli-service": "^4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"less": "^3.12.2",
|
||||
@@ -3524,7 +3525,6 @@
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -4138,7 +4138,6 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
],
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
@@ -4160,7 +4159,6 @@
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
@@ -4173,7 +4171,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@@ -4186,7 +4183,6 @@
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
@@ -4199,7 +4195,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@@ -4209,7 +4204,6 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
@@ -8606,7 +8600,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
@@ -12109,7 +12102,6 @@
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
@@ -17914,6 +17906,7 @@
|
||||
"integrity": "sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q==",
|
||||
"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",
|
||||
@@ -17926,6 +17919,7 @@
|
||||
"@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"
|
||||
}
|
||||
@@ -18943,8 +18937,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
@@ -19454,7 +19447,6 @@
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
@@ -19471,7 +19463,6 @@
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
@@ -19481,7 +19472,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
@@ -19491,7 +19481,6 @@
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
@@ -19500,15 +19489,13 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
@@ -22941,7 +22928,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
@@ -25800,7 +25786,6 @@
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build && node ../copy.js",
|
||||
"lint": "vue-cli-service lint",
|
||||
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/index.js --dest ../simple-mind-map/dist",
|
||||
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/full.js --dest ../simple-mind-map/dist",
|
||||
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*",
|
||||
"buildDoc": "node ./scripts/buildDoc.js"
|
||||
"buildDoc": "node ./scripts/buildDoc.js",
|
||||
"autoBuildDoc": "node ./scripts/autoBuildDoc.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@toast-ui/editor": "^3.1.5",
|
||||
@@ -27,6 +28,7 @@
|
||||
"@vue/cli-plugin-eslint": "^4.5.0",
|
||||
"@vue/cli-service": "^4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"less": "^3.12.2",
|
||||
|
||||
38
web/scripts/autoBuildDoc.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const chokidar = require('chokidar')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { exec } = require('node:child_process')
|
||||
const { transformMdToVue } = require('./transformMdToVue')
|
||||
|
||||
const reBuildAll = () => {
|
||||
exec(
|
||||
'node ./buildDoc.js',
|
||||
{
|
||||
cwd: path.resolve(__dirname)
|
||||
},
|
||||
(error, msg) => {
|
||||
console.log(error, msg)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const buildOne = file => {
|
||||
let content = fs.readFileSync(file, 'utf-8')
|
||||
let doc = transformMdToVue(content)
|
||||
let destPath = path.join(path.dirname(file), './index.vue')
|
||||
fs.writeFileSync(destPath, doc)
|
||||
}
|
||||
|
||||
chokidar
|
||||
.watch(path.join(__dirname, '../src/pages/Doc/'), {
|
||||
ignoreInitial: true
|
||||
})
|
||||
.on('all', (event, file) => {
|
||||
if (/\.md$/.test(file)) {
|
||||
if (event === 'change') {
|
||||
buildOne(file)
|
||||
} else {
|
||||
reBuildAll()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,24 +1,7 @@
|
||||
// 编译文档
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const hljs = require('highlight.js')
|
||||
const md = require('markdown-it')({
|
||||
highlight: function(str, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return (
|
||||
'<pre class="hljs"><code>' +
|
||||
hljs.highlight(lang, str, true).value +
|
||||
'</code></pre>'
|
||||
)
|
||||
} catch (__) {}
|
||||
}
|
||||
|
||||
return (
|
||||
'<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
|
||||
)
|
||||
}
|
||||
}).use(require('markdown-it-checkbox'))
|
||||
const { transformMdToVue } = require('./transformMdToVue')
|
||||
|
||||
// 文档语言种类
|
||||
let langList = ['zh', 'en']
|
||||
@@ -48,15 +31,12 @@ const compilerDir = (dir, dirName, routerList) => {
|
||||
const compilerFile = (dir, file, dirName, routerList) => {
|
||||
let filePath = path.join(dir, file)
|
||||
let destPath = path.join(dir, './index.vue')
|
||||
let templatePath = path.join(__dirname, '../src/pages/Doc/Template.vue')
|
||||
let content = fs.readFileSync(filePath, 'utf-8')
|
||||
let title = /(^|\n\r)\s*#\s+([^\n\r]+)/g.exec(content)
|
||||
if (title && title[2]) {
|
||||
addRouter(dirName, routerList, title[2])
|
||||
}
|
||||
let result = md.render(content)
|
||||
let template = fs.readFileSync(templatePath, 'utf-8')
|
||||
let doc = template.replace('$$$$', result)
|
||||
let doc = transformMdToVue(content)
|
||||
fs.writeFileSync(destPath, doc)
|
||||
}
|
||||
|
||||
|
||||
31
web/scripts/transformMdToVue.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const hljs = require('highlight.js')
|
||||
const md = require('markdown-it')({
|
||||
highlight: function(str, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return (
|
||||
'<pre class="hljs"><code>' +
|
||||
hljs.highlight(str, {
|
||||
language: lang,
|
||||
ignoreIllegals: true
|
||||
}).value +
|
||||
'</code></pre>'
|
||||
)
|
||||
} catch (__) {}
|
||||
}
|
||||
|
||||
return (
|
||||
'<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
|
||||
)
|
||||
}
|
||||
}).use(require('markdown-it-checkbox'))
|
||||
|
||||
const templatePath = path.join(__dirname, '../src/pages/Doc/Template.vue')
|
||||
|
||||
exports.transformMdToVue = (content) => {
|
||||
let result = md.render(content)
|
||||
let template = fs.readFileSync(templatePath, 'utf-8')
|
||||
return template.replace('$$$$', result)
|
||||
}
|
||||
@@ -54,6 +54,60 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">背景颜色</div>
|
||||
<div class="code-name">&#xe6f8;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">清除</div>
|
||||
<div class="code-name">&#xe605;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">case</div>
|
||||
<div class="code-name">&#xe6c6;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">形状-文字</div>
|
||||
<div class="code-name">&#xeb99;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">字体加粗</div>
|
||||
<div class="code-name">&#xec83;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">字体下划线</div>
|
||||
<div class="code-name">&#xec85;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">字体斜体</div>
|
||||
<div class="code-name">&#xec86;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">删除线</div>
|
||||
<div class="code-name">&#xe612;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">字体颜色</div>
|
||||
<div class="code-name">&#xe854;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">github</div>
|
||||
@@ -348,9 +402,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1673600274529') format('woff2'),
|
||||
url('iconfont.woff?t=1673600274529') format('woff'),
|
||||
url('iconfont.ttf?t=1673600274529') format('truetype');
|
||||
src: url('iconfont.woff2?t=1677478278322') format('woff2'),
|
||||
url('iconfont.woff?t=1677478278322') format('woff'),
|
||||
url('iconfont.ttf?t=1677478278322') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -376,6 +430,87 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconbeijingyanse"></span>
|
||||
<div class="name">
|
||||
背景颜色
|
||||
</div>
|
||||
<div class="code-name">.iconbeijingyanse
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconqingchu"></span>
|
||||
<div class="name">
|
||||
清除
|
||||
</div>
|
||||
<div class="code-name">.iconqingchu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconcase"></span>
|
||||
<div class="name">
|
||||
case
|
||||
</div>
|
||||
<div class="code-name">.iconcase
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconxingzhuang-wenzi"></span>
|
||||
<div class="name">
|
||||
形状-文字
|
||||
</div>
|
||||
<div class="code-name">.iconxingzhuang-wenzi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconzitijiacu"></span>
|
||||
<div class="name">
|
||||
字体加粗
|
||||
</div>
|
||||
<div class="code-name">.iconzitijiacu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconzitixiahuaxian"></span>
|
||||
<div class="name">
|
||||
字体下划线
|
||||
</div>
|
||||
<div class="code-name">.iconzitixiahuaxian
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconzitixieti"></span>
|
||||
<div class="name">
|
||||
字体斜体
|
||||
</div>
|
||||
<div class="code-name">.iconzitixieti
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconshanchuxian"></span>
|
||||
<div class="name">
|
||||
删除线
|
||||
</div>
|
||||
<div class="code-name">.iconshanchuxian
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont iconzitiyanse"></span>
|
||||
<div class="name">
|
||||
字体颜色
|
||||
</div>
|
||||
<div class="code-name">.iconzitiyanse
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icongithub"></span>
|
||||
<div class="name">
|
||||
@@ -817,6 +952,78 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconbeijingyanse"></use>
|
||||
</svg>
|
||||
<div class="name">背景颜色</div>
|
||||
<div class="code-name">#iconbeijingyanse</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconqingchu"></use>
|
||||
</svg>
|
||||
<div class="name">清除</div>
|
||||
<div class="code-name">#iconqingchu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconcase"></use>
|
||||
</svg>
|
||||
<div class="name">case</div>
|
||||
<div class="code-name">#iconcase</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconxingzhuang-wenzi"></use>
|
||||
</svg>
|
||||
<div class="name">形状-文字</div>
|
||||
<div class="code-name">#iconxingzhuang-wenzi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconzitijiacu"></use>
|
||||
</svg>
|
||||
<div class="name">字体加粗</div>
|
||||
<div class="code-name">#iconzitijiacu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconzitixiahuaxian"></use>
|
||||
</svg>
|
||||
<div class="name">字体下划线</div>
|
||||
<div class="code-name">#iconzitixiahuaxian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconzitixieti"></use>
|
||||
</svg>
|
||||
<div class="name">字体斜体</div>
|
||||
<div class="code-name">#iconzitixieti</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconshanchuxian"></use>
|
||||
</svg>
|
||||
<div class="name">删除线</div>
|
||||
<div class="code-name">#iconshanchuxian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#iconzitiyanse"></use>
|
||||
</svg>
|
||||
<div class="name">字体颜色</div>
|
||||
<div class="code-name">#iconzitiyanse</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icongithub"></use>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2479351 */
|
||||
src: url('iconfont.woff2?t=1673600274529') format('woff2'),
|
||||
url('iconfont.woff?t=1673600274529') format('woff'),
|
||||
url('iconfont.ttf?t=1673600274529') format('truetype');
|
||||
src: url('iconfont.woff2?t=1677478278322') format('woff2'),
|
||||
url('iconfont.woff?t=1677478278322') format('woff'),
|
||||
url('iconfont.ttf?t=1677478278322') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,42 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconbeijingyanse:before {
|
||||
content: "\e6f8";
|
||||
}
|
||||
|
||||
.iconqingchu:before {
|
||||
content: "\e605";
|
||||
}
|
||||
|
||||
.iconcase:before {
|
||||
content: "\e6c6";
|
||||
}
|
||||
|
||||
.iconxingzhuang-wenzi:before {
|
||||
content: "\eb99";
|
||||
}
|
||||
|
||||
.iconzitijiacu:before {
|
||||
content: "\ec83";
|
||||
}
|
||||
|
||||
.iconzitixiahuaxian:before {
|
||||
content: "\ec85";
|
||||
}
|
||||
|
||||
.iconzitixieti:before {
|
||||
content: "\ec86";
|
||||
}
|
||||
|
||||
.iconshanchuxian:before {
|
||||
content: "\e612";
|
||||
}
|
||||
|
||||
.iconzitiyanse:before {
|
||||
content: "\e854";
|
||||
}
|
||||
|
||||
.icongithub:before {
|
||||
content: "\e64f";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,69 @@
|
||||
"css_prefix_text": "icon",
|
||||
"description": "思维导图",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "1790495",
|
||||
"name": "背景颜色",
|
||||
"font_class": "beijingyanse",
|
||||
"unicode": "e6f8",
|
||||
"unicode_decimal": 59128
|
||||
},
|
||||
{
|
||||
"icon_id": "11321310",
|
||||
"name": "清除",
|
||||
"font_class": "qingchu",
|
||||
"unicode": "e605",
|
||||
"unicode_decimal": 58885
|
||||
},
|
||||
{
|
||||
"icon_id": "586787",
|
||||
"name": "case",
|
||||
"font_class": "case",
|
||||
"unicode": "e6c6",
|
||||
"unicode_decimal": 59078
|
||||
},
|
||||
{
|
||||
"icon_id": "4354254",
|
||||
"name": "形状-文字",
|
||||
"font_class": "xingzhuang-wenzi",
|
||||
"unicode": "eb99",
|
||||
"unicode_decimal": 60313
|
||||
},
|
||||
{
|
||||
"icon_id": "6337466",
|
||||
"name": "字体加粗",
|
||||
"font_class": "zitijiacu",
|
||||
"unicode": "ec83",
|
||||
"unicode_decimal": 60547
|
||||
},
|
||||
{
|
||||
"icon_id": "6337470",
|
||||
"name": "字体下划线",
|
||||
"font_class": "zitixiahuaxian",
|
||||
"unicode": "ec85",
|
||||
"unicode_decimal": 60549
|
||||
},
|
||||
{
|
||||
"icon_id": "6337471",
|
||||
"name": "字体斜体",
|
||||
"font_class": "zitixieti",
|
||||
"unicode": "ec86",
|
||||
"unicode_decimal": 60550
|
||||
},
|
||||
{
|
||||
"icon_id": "11975179",
|
||||
"name": "删除线",
|
||||
"font_class": "shanchuxian",
|
||||
"unicode": "e612",
|
||||
"unicode_decimal": 58898
|
||||
},
|
||||
{
|
||||
"icon_id": "34198316",
|
||||
"name": "字体颜色",
|
||||
"font_class": "zitiyanse",
|
||||
"unicode": "e854",
|
||||
"unicode_decimal": 59476
|
||||
},
|
||||
{
|
||||
"icon_id": "8760187",
|
||||
"name": "github",
|
||||
|
||||
@@ -166,6 +166,22 @@ export const backgroundPositionList = [
|
||||
}
|
||||
]
|
||||
|
||||
// 背景图片大小
|
||||
export const backgroundSizeList = [
|
||||
{
|
||||
name: 'Auto',
|
||||
value: 'auto'
|
||||
},
|
||||
{
|
||||
name: 'Cover',
|
||||
value: 'cover'
|
||||
},
|
||||
{
|
||||
name: 'Contain',
|
||||
value: 'contain'
|
||||
}
|
||||
]
|
||||
|
||||
// 快捷键列表
|
||||
export const shortcutKeyList = [
|
||||
{
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
backgroundPositionList as backgroundPositionListZh,
|
||||
shortcutKeyList as shortcutKeyListZh,
|
||||
shapeList as shapeListZh,
|
||||
sidebarTriggerList as sidebarTriggerListZh
|
||||
sidebarTriggerList as sidebarTriggerListZh,
|
||||
backgroundSizeList as backgroundSizeListZh
|
||||
} from './zh'
|
||||
import {
|
||||
fontFamilyList as fontFamilyListEn,
|
||||
@@ -24,7 +25,8 @@ import {
|
||||
backgroundPositionList as backgroundPositionListEn,
|
||||
shortcutKeyList as shortcutKeyListEn,
|
||||
shapeList as shapeListEn,
|
||||
sidebarTriggerList as sidebarTriggerListEn
|
||||
sidebarTriggerList as sidebarTriggerListEn,
|
||||
backgroundSizeList as backgroundSizeListEn
|
||||
} from './en'
|
||||
|
||||
const fontFamilyList = {
|
||||
@@ -52,6 +54,11 @@ const backgroundPositionList = {
|
||||
en: backgroundPositionListEn
|
||||
}
|
||||
|
||||
const backgroundSizeList = {
|
||||
zh: backgroundSizeListZh,
|
||||
en: backgroundSizeListEn
|
||||
}
|
||||
|
||||
const shortcutKeyList = {
|
||||
zh: shortcutKeyListZh,
|
||||
en: shortcutKeyListEn
|
||||
@@ -81,6 +88,7 @@ export {
|
||||
lineStyleList,
|
||||
backgroundRepeatList,
|
||||
backgroundPositionList,
|
||||
backgroundSizeList,
|
||||
shortcutKeyList,
|
||||
shapeList,
|
||||
sidebarTriggerList
|
||||
|
||||
@@ -221,6 +221,22 @@ export const backgroundPositionList = [
|
||||
}
|
||||
]
|
||||
|
||||
// 背景图片大小
|
||||
export const backgroundSizeList = [
|
||||
{
|
||||
name: '自动',
|
||||
value: 'auto'
|
||||
},
|
||||
{
|
||||
name: '覆盖',
|
||||
value: 'cover'
|
||||
},
|
||||
{
|
||||
name: '保持',
|
||||
value: 'contain'
|
||||
}
|
||||
]
|
||||
|
||||
// 数据存储
|
||||
export const store = {
|
||||
sidebarZIndex: 1 //侧边栏zIndex
|
||||
|
||||
@@ -5,6 +5,8 @@ export default {
|
||||
color: 'Color',
|
||||
image: 'Image',
|
||||
imageRepeat: 'Image repeat',
|
||||
imagePosition: 'Image position',
|
||||
imageSize: 'Image size',
|
||||
line: 'Line',
|
||||
width: 'Width',
|
||||
style: 'Style',
|
||||
@@ -32,7 +34,11 @@ export default {
|
||||
watermarkTextSpacing: 'Text spacing',
|
||||
watermarkAngle: 'Angle',
|
||||
watermarkTextOpacity: 'Text opacity',
|
||||
watermarkTextFontSize: 'Font size'
|
||||
watermarkTextFontSize: 'Font size',
|
||||
isEnableNodeRichText: 'Enable node rich text editing',
|
||||
mousewheelAction: 'Mouse wheel behavior',
|
||||
zoomView: 'Zoom view',
|
||||
moveViewUpDown: 'Move view up and down'
|
||||
},
|
||||
color: {
|
||||
moreColor: 'More color'
|
||||
@@ -77,7 +83,13 @@ export default {
|
||||
imageFile: 'Image file',
|
||||
svgFile: 'svg file',
|
||||
pdfFile: 'pdf file',
|
||||
tips: 'tips:.smm and .json file can be import'
|
||||
tips: 'tips: .smm and .json file can be import',
|
||||
domToImage: 'Whether to convert rich text nodes in svg into pictures',
|
||||
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'
|
||||
},
|
||||
fullscreen: {
|
||||
fullscreenShow: 'Full screen show',
|
||||
@@ -176,5 +188,9 @@ export default {
|
||||
import: 'Import',
|
||||
export: 'Export',
|
||||
shortcutKey: 'Shortcut key'
|
||||
},
|
||||
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.'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ export default {
|
||||
color: '颜色',
|
||||
image: '图片',
|
||||
imageRepeat: '图片重复',
|
||||
imagePosition: '图片位置',
|
||||
imageSize: '图片大小',
|
||||
line: '连线',
|
||||
width: '粗细',
|
||||
style: '风格',
|
||||
@@ -32,7 +34,11 @@ export default {
|
||||
watermarkTextSpacing: '水印文字间距',
|
||||
watermarkAngle: '旋转角度',
|
||||
watermarkTextOpacity: '文字透明度',
|
||||
watermarkTextFontSize: '文字字号'
|
||||
watermarkTextFontSize: '文字字号',
|
||||
isEnableNodeRichText: '是否开启节点富文本编辑',
|
||||
mousewheelAction: '鼠标滚轮行为',
|
||||
zoomView: '缩放视图',
|
||||
moveViewUpDown: '上下移动视图'
|
||||
},
|
||||
color: {
|
||||
moreColor: '更多颜色'
|
||||
@@ -77,7 +83,13 @@ export default {
|
||||
imageFile: '图片文件',
|
||||
svgFile: 'svg文件',
|
||||
pdfFile: 'pdf文件',
|
||||
tips: 'tips:.smm和.json文件可用于导入'
|
||||
tips: 'tips:.smm和.json文件可用于导入',
|
||||
domToImage: '是否将svg中富文本节点转换成图片',
|
||||
pngTips: 'tips:富文本模式导出图片非常耗时,建议导出为svg格式',
|
||||
svgTips: 'tips:富文本模式导出图片非常耗时',
|
||||
transformingDomToImages: '正在转换节点:',
|
||||
notifyTitle: '消息',
|
||||
notifyMessage: '如果没有触发下载,请检查是否被浏览器拦截了'
|
||||
},
|
||||
fullscreen: {
|
||||
fullscreenShow: '全屏查看',
|
||||
@@ -176,5 +188,9 @@ export default {
|
||||
import: '导入',
|
||||
export: '导出',
|
||||
shortcutKey: '快捷键'
|
||||
},
|
||||
edit: {
|
||||
newFeatureNoticeTitle: '新特性提醒',
|
||||
newFeatureNoticeMessage: '本次更新支持了节点富文本编辑,但是存在一定缺陷,最主要的影响是导出为图片的时间和节点数量成正比,所以对导出需求比较依赖的话可以通过【基础样式】-【其他配置】-【是否开启节点富文本编辑】设置关掉富文本编辑模式。'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,17 @@ let APIList = [
|
||||
'node',
|
||||
'render',
|
||||
'view',
|
||||
'miniMap',
|
||||
'doExport',
|
||||
'watermark',
|
||||
'keyCommand',
|
||||
'keyboardNavigation',
|
||||
'command',
|
||||
'select',
|
||||
'batchExecution',
|
||||
'richText',
|
||||
'select',
|
||||
'drag',
|
||||
'keyboardNavigation',
|
||||
'doExport',
|
||||
'miniMap',
|
||||
'watermark',
|
||||
'xmind',
|
||||
'utils'
|
||||
]
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ export default {
|
||||
this.onScroll()
|
||||
},
|
||||
lang(newVal, oldVal) {
|
||||
console.log(newVal, oldVal)
|
||||
if (!oldVal) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,5 +1,49 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.3
|
||||
|
||||
Fix: No trigger after forward and backward `data_ Change` event.
|
||||
|
||||
New: Support user-defined mouse wheel events; The mouse wheel is adjusted to support zooming and moving the view up and down.
|
||||
|
||||
## 0.4.2
|
||||
|
||||
New: The `setText` method of the Node class adds a second parameter to support setting rich text content.
|
||||
|
||||
## 0.4.1
|
||||
|
||||
New: 1.Add and throw node mouseenter and mouseleave events; 2.Node rich text supports setting background color; 3.Node rich text supports clear style.
|
||||
|
||||
Fix: 1.Mac system touchpad scaling is the opposite problem; 2.When the device window.devicePixelRatio is not 1, the size of the rich text node in the exported image will become larger when there are rich text nodes.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
New: The node supports rich text editing.
|
||||
|
||||
## 0.3.4
|
||||
|
||||
New: Automatic line wrapping function is added to node text.
|
||||
|
||||
Fix: 1.Fix the problem of deletion exceptions if there are root nodes in the batch deleted nodes. 2.Fix the problem that high node height will overlap with other nodes in the case of bottom edge style.
|
||||
|
||||
## 0.3.3
|
||||
|
||||
Fix: The root node text cannot wrap.
|
||||
|
||||
## 0.3.2
|
||||
|
||||
Fix: 1.Fix the problem that the node style is not updated when the secondary node is dragged to other nodes or other nodes are dragged to the secondary node; 2.Fix the problem that when the actual content of the mind map is larger than the screen width and height, the excess part is not watermarked when exporting.
|
||||
|
||||
## 0.3.1
|
||||
|
||||
Fix: 1.The problem that deleting the background image does not take effect; 2.The problem that the connector runs above the root node when the node is dragged to the root node.
|
||||
|
||||
New: Add position and size settings for background image display. This setting is also supported for exported pictures.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
Upgrade to plugin architecture, pull out some non-core functions as plugins, register as needed, and reduce the overall volume.
|
||||
|
||||
## 0.2.24
|
||||
|
||||
New: Node free drag is changed to configurable, the default is `false`, not open; Support add watermark.
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.4.3</h2>
|
||||
<p>Fix: No trigger after forward and backward <code>data_ Change</code> event.</p>
|
||||
<p>New: Support user-defined mouse wheel events; The mouse wheel is adjusted to support zooming and moving the view up and down.</p>
|
||||
<h2>0.4.2</h2>
|
||||
<p>New: The <code>setText</code> method of the Node class adds a second parameter to support setting rich text content.</p>
|
||||
<h2>0.4.1</h2>
|
||||
<p>New: 1.Add and throw node mouseenter and mouseleave events; 2.Node rich text supports setting background color; 3.Node rich text supports clear style.</p>
|
||||
<p>Fix: 1.Mac system touchpad scaling is the opposite problem; 2.When the device window.devicePixelRatio is not 1, the size of the rich text node in the exported image will become larger when there are rich text nodes.</p>
|
||||
<h2>0.4.0</h2>
|
||||
<p>New: The node supports rich text editing.</p>
|
||||
<h2>0.3.4</h2>
|
||||
<p>New: Automatic line wrapping function is added to node text.</p>
|
||||
<p>Fix: 1.Fix the problem of deletion exceptions if there are root nodes in the batch deleted nodes. 2.Fix the problem that high node height will overlap with other nodes in the case of bottom edge style.</p>
|
||||
<h2>0.3.3</h2>
|
||||
<p>Fix: The root node text cannot wrap.</p>
|
||||
<h2>0.3.2</h2>
|
||||
<p>Fix: 1.Fix the problem that the node style is not updated when the secondary node is dragged to other nodes or other nodes are dragged to the secondary node; 2.Fix the problem that when the actual content of the mind map is larger than the screen width and height, the excess part is not watermarked when exporting.</p>
|
||||
<h2>0.3.1</h2>
|
||||
<p>Fix: 1.The problem that deleting the background image does not take effect; 2.The problem that the connector runs above the root node when the node is dragged to the root node.</p>
|
||||
<p>New: Add position and size settings for background image display. This setting is also supported for exported pictures.</p>
|
||||
<h2>0.3.0</h2>
|
||||
<p>Upgrade to plugin architecture, pull out some non-core functions as plugins, register as needed, and reduce the overall volume.</p>
|
||||
<h2>0.2.24</h2>
|
||||
<p>New: Node free drag is changed to configurable, the default is <code>false</code>, not open; Support add watermark.</p>
|
||||
<h2>0.2.23</h2>
|
||||
|
||||
@@ -20,50 +20,6 @@ const mindMap = new MindMap({
|
||||
});
|
||||
```
|
||||
|
||||
## Xmind Parsing Method
|
||||
|
||||
> v0.2.7+
|
||||
|
||||
You can get the `Xmind` parsing method as follows:
|
||||
|
||||
```js
|
||||
import MindMap from "simple-mind-map";
|
||||
|
||||
console.log(MindMap.xmind);
|
||||
```
|
||||
|
||||
The `MindMap.xmind` object has two methods attached:
|
||||
|
||||
### parseXmindFile(file)
|
||||
|
||||
Parsing the `.xmind` file and returning the parsed data. Note that this is
|
||||
complete data, including the node tree, theme, and structure. You can use
|
||||
`mindMap.setFullData(data)` to render the returned data to the canvas.
|
||||
|
||||
`file`: `File` object
|
||||
|
||||
### transformXmind(content)
|
||||
|
||||
Convert `xmind` data. The `.xmind` file is essentially a `zip` file that can be
|
||||
decompressed by changing the suffix to zip. Inside, there is a `content.json`
|
||||
file. If you have parsed this file yourself, you can pass the contents of this
|
||||
file to this method for conversion. The converted data is the complete data,
|
||||
including the node tree, theme, structure, etc. You can use
|
||||
`mindMap.setFullData(data)` to render the returned data to the canvas.
|
||||
|
||||
`content`: the contents of the `content.json` file within the `.xmind` zip
|
||||
package
|
||||
|
||||
### transformOldXmind(content)
|
||||
|
||||
> v0.2.8+
|
||||
|
||||
For data parsing of the `xmind8` version, because the `.xmind` file in this
|
||||
version does not have a `content.json`, it corresponds to `content.xml`.
|
||||
|
||||
`content`: the contents of the `content.xml` file within the `.xmind` zip
|
||||
package
|
||||
|
||||
## Instantiation options
|
||||
|
||||
| Field Name | Type | Default Value | Description | Required |
|
||||
@@ -84,6 +40,10 @@ package
|
||||
| readonly(v0.1.7+) | Boolean | false | Whether it is read-only mode | |
|
||||
| enableFreeDrag(v0.2.4+) | Boolean | false | Enable node free drag | |
|
||||
| watermarkConfig(v0.2.4+) | Object | | Watermark config, Please refer to the table 【Watermark config】 below for detailed configuration | |
|
||||
| textAutoWrapWidth(v0.3.4+) | Number | 500 | Each line of text in the node will wrap automatically when it reaches the width | |
|
||||
| customHandleMousewheel(v0.4.3+) | Function | null | User-defined mouse wheel event processing can pass a function, and the callback parameter is the event object | |
|
||||
| mousewheelAction(v0.4.3+) | String | zoom | The behavior of the mouse wheel, `zoom`(Zoom in and out)、`move`(Move up and down). If `customHandleMousewheel` passes a custom function, this property will not take effect | |
|
||||
| mousewheelMoveStep(v0.4.3+) | Number | 100 | When the `mousewheelAction` is set to `move`, you can use this attribute to control the step length of the view movement when the mouse scrolls. The unit is `px` | |
|
||||
|
||||
### Watermark config
|
||||
|
||||
@@ -125,16 +85,63 @@ mindMap.setTheme('Theme name')
|
||||
|
||||
For all configurations of theme, please refer to [Default Topic](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js). The `defineTheme`method will merge the configuration you passed in with the default configuration. Most of the themes do not need custom many parts. For a typical customized theme configuration, please refer to [blueSky](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/blueSky.js).
|
||||
|
||||
### usePlugin(plugin, opt = {})
|
||||
|
||||
> v0.3.0+
|
||||
|
||||
- `opt`:v0.4.0+,Plugin options. If a plugin supports custom options, it can be passed in through this parameter.
|
||||
|
||||
If you need to use some non-core functions, such as mini map, watermark, etc, you can register plugin through this method. Can be called in chain.
|
||||
|
||||
Note: The plugin needs to be registered before instantiating `MindMap`.
|
||||
|
||||
### hasPlugin(plugin)
|
||||
|
||||
> v0.4.0+
|
||||
|
||||
Get whether a plugin is registered, The index of the plugin in the registered plugin list is returned, If it is `-1`, it means that the plugin is not registered.
|
||||
|
||||
|
||||
## Static props
|
||||
|
||||
### pluginList
|
||||
|
||||
> v0.3.0+
|
||||
|
||||
List of all currently registered plugins.
|
||||
|
||||
|
||||
|
||||
## Instance methods
|
||||
|
||||
### render()
|
||||
### getSvgData()
|
||||
|
||||
> v0.3.0+
|
||||
|
||||
Get the `svg` data and return an object. The detailed structure is as follows:
|
||||
|
||||
```js
|
||||
{
|
||||
svg, // Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)
|
||||
svgHTML, // String, svg string, i.e. html string, can be directly rendered to the small map container you prepared
|
||||
rect: // Object, position, size, etc. of mind map graphics before zoom
|
||||
origWidth, // Number, canvas width
|
||||
origHeight, // Number, canvas height
|
||||
scaleX, // Number, horizontal zoom value of mind map graphics
|
||||
scaleY, // Number, vertical zoom value of mind map graphics
|
||||
}
|
||||
```
|
||||
|
||||
### render(callback)
|
||||
|
||||
- `callback`: `v0.3.2+`, `Function`, Called when the re-rendering is complete
|
||||
|
||||
Triggers a full rendering, which will reuse nodes for better performance. If
|
||||
only the node positions have changed, this method can be called to `reRender`.
|
||||
|
||||
### reRender()
|
||||
### reRender(callback)
|
||||
|
||||
- `callback`: `v0.3.2+`, `Function`, Called when the re-rendering is complete
|
||||
|
||||
Performs a full re-render, clearing the canvas and creating new nodes. This has
|
||||
poor performance and should be used sparingly.
|
||||
@@ -174,6 +181,8 @@ Listen to an event. Event list:
|
||||
| node_mouseup | Node mouseup event | this (node instance), e (event object) |
|
||||
| node_dblclick | Node double-click event | this (node instance), e (event object) |
|
||||
| node_contextmenu | Node right-click menu event | e (event object), this (node instance) |
|
||||
| node_mouseenter(v0.4.1+) | Node mouseenter event | this (node instance), e (event object) |
|
||||
| node_mouseleave(v0.4.1+) | Node mouseleave event | this (node instance), e (event object) |
|
||||
| before_node_active | Event before node activation | this (node instance), activeNodeList (current list of active nodes) |
|
||||
| node_active | Node activation event | this (node instance), activeNodeList (current list of active nodes) |
|
||||
| expand_btn_click | Node expand or collapse event | this (node instance) |
|
||||
@@ -182,6 +191,8 @@ Listen to an event. Event list:
|
||||
| scale | Zoom event | scale (zoom ratio) |
|
||||
| node_img_dblclick(v0.2.15+) | Node image double-click event | this (node instance), e (event object) |
|
||||
| node_tree_render_end(v0.2.16+) | Node tree render end event | |
|
||||
| rich_text_selection_change(v0.4.0+) | Available when the `RichText` plugin is registered. Triggered when the text selection area changes when the node is edited | hasRange(Whether there is a selection)、rectInfo(Size and location information of the selected area)、formatInfo(Text formatting information of the selected area) |
|
||||
| transforming-dom-to-images(v0.4.0+) | Available when the `RichText` plugin is registered. When there is a `DOM` node in `svg`, the `DOM` node will be converted to an image when exporting to an image. This event will be triggered during the conversion process. You can use this event to prompt the user about the node to which you are currently converting | index(Index of the node currently converted to)、len(Total number of nodes to be converted) |
|
||||
|
||||
### emit(event, ...args)
|
||||
|
||||
@@ -250,39 +261,39 @@ in the options table above.
|
||||
Executes a command, which will add a record to the history stack for undo or
|
||||
redo. All commands are as follows:
|
||||
|
||||
| Command name | Description | Parameters |
|
||||
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| SELECT_ALL | Select all | |
|
||||
| BACK | Go back a specified number of steps | step (the number of steps to go back, default is 1) |
|
||||
| FORWARD | Go forward a specified number of steps | step (the number of steps to go forward, default is 1) |
|
||||
| INSERT_NODE | Insert a sibling node, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective | |
|
||||
| INSERT_CHILD_NODE | Insert a child node, the active node will be the operation node | |
|
||||
| UP_NODE | Move node up, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the first node in the list will be invalid | |
|
||||
| DOWN_NODE | Move node down, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the last node in the list will be invalid | |
|
||||
| REMOVE_NODE | Remove node, the active node will be the operation node | |
|
||||
| PASTE_NODE | Paste node to a node, the active node will be the operation node | data (the node data to paste, usually obtained through the renderer.copyNode() and renderer.cutNode() methods) |
|
||||
| SET_NODE_STYLE | Modify node style | node (the node to set the style of), prop (style property), value (style property value), isActive (boolean, whether the style being set is for the active state) |
|
||||
| SET_NODE_ACTIVE | Set whether the node is active | node (the node to set), active (boolean, whether to activate) |
|
||||
| CLEAR_ACTIVE_NODE | Clear the active state of the currently active node(s), the active node will be the operation node | |
|
||||
| SET_NODE_EXPAND | Set whether the node is expanded | node (the node to set), expand (boolean, whether to expand) |
|
||||
| EXPAND_ALL | Expand all nodes | |
|
||||
| UNEXPAND_ALL | Collapse all nodes | |
|
||||
| UNEXPAND_TO_LEVEL (v0.2.8+) | Expand to a specified level | level (the level to expand to, 1, 2, 3...) |
|
||||
| SET_NODE_DATA | Update node data, that is, update the data in the data object of the node data object | node (the node to set), data (object, the data to update, e.g. `{expand: true}`) |
|
||||
| SET_NODE_TEXT | Set node text | node (the node to set), text (the new text for the node) |
|
||||
| SET_NODE_IMAGE | Set Node Image | node (node to set), imgData (object, image information, structured as: `{url, title, width, height}`, the width and height of the image must be passed) |
|
||||
| SET_NODE_ICON | Set Node Icon | node (node to set), icons (array, predefined image names array, available icons can be obtained in the nodeIconList list in the [https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js) file, icon name is type_name, such as ['priority_1']) |
|
||||
| SET_NODE_HYPERLINK | Set Node Hyperlink | node (node to set), link (hyperlink address), title (hyperlink name, optional) |
|
||||
| SET_NODE_NOTE | Set Node Note | node (node to set), note (note text) |
|
||||
| SET_NODE_TAG | Set Node Tag | node (node to set), tag (string array, built-in color information can be obtained in [https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)) |
|
||||
| INSERT_AFTER (v0.1.5+) | Move Node to After Another Node | node (node to move), exist (target node) |
|
||||
| INSERT_BEFORE (v0.1.5+) | Move Node to Before Another Node | node (node to move), exist (target node) |
|
||||
| MOVE_NODE_TO (v0.1.5+) | Move a node as a child of another node | node (the node to move), toNode (the target node) |
|
||||
| ADD_GENERALIZATION (v0.2.0+) | Add a node summary | data (the data for the summary, in object format, all numerical fields of the node are supported, default is `{text: 'summary'}`) |
|
||||
| REMOVE_GENERALIZATION (v0.2.0+) | Remove a node summary | |
|
||||
| SET_NODE_CUSTOM_POSITION (v0.2.0+) | Set a custom position for a node | node (the node to set), left (custom x coordinate, default is undefined), top (custom y coordinate, default is undefined) |
|
||||
| RESET_LAYOUT (v0.2.0+) | Arrange layout with one click | |
|
||||
| SET_NODE_SHAPE (v0.2.4+) | Set the shape of a node | node (the node to set), shape (the shape, all shapes: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js) |
|
||||
| Command name | Description | Parameters |
|
||||
| ---------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| SELECT_ALL | Select all | |
|
||||
| BACK | Go back a specified number of steps | step (the number of steps to go back, default is 1) |
|
||||
| FORWARD | Go forward a specified number of steps | step (the number of steps to go forward, default is 1) |
|
||||
| INSERT_NODE | Insert a sibling node, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective | |
|
||||
| INSERT_CHILD_NODE | Insert a child node, the active node will be the operation node | |
|
||||
| UP_NODE | Move node up, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the first node in the list will be invalid | |
|
||||
| DOWN_NODE | Move node down, the active node will be the operation node. If there are multiple active nodes, only the first one will be effective. Using this command on the root node or the last node in the list will be invalid | |
|
||||
| REMOVE_NODE | Remove node, the active node will be the operation node | |
|
||||
| PASTE_NODE | Paste node to a node, the active node will be the operation node | data (the node data to paste, usually obtained through the renderer.copyNode() and renderer.cutNode() methods) |
|
||||
| SET_NODE_STYLE | Modify node style | node (the node to set the style of), prop (style property), value (style property value), isActive (boolean, whether the style being set is for the active state) |
|
||||
| SET_NODE_ACTIVE | Set whether the node is active | node (the node to set), active (boolean, whether to activate) |
|
||||
| CLEAR_ACTIVE_NODE | Clear the active state of the currently active node(s), the active node will be the operation node | |
|
||||
| SET_NODE_EXPAND | Set whether the node is expanded | node (the node to set), expand (boolean, whether to expand) |
|
||||
| EXPAND_ALL | Expand all nodes | |
|
||||
| UNEXPAND_ALL | Collapse all nodes | |
|
||||
| UNEXPAND_TO_LEVEL (v0.2.8+) | Expand to a specified level | level (the level to expand to, 1, 2, 3...) |
|
||||
| SET_NODE_DATA | Update node data, that is, update the data in the data object of the node data object | node (the node to set), data (object, the data to update, e.g. `{expand: true}`) |
|
||||
| SET_NODE_TEXT | Set node text | node (the node to set), text (the new text for the node), richText(v0.4.0+, If you want to set a rich text character, you need to set it to `true`) |
|
||||
| SET_NODE_IMAGE | Set Node Image | node (node to set), imgData (object, image information, structured as: `{url, title, width, height}`, the width and height of the image must be passed) |
|
||||
| SET_NODE_ICON | Set Node Icon | node (node to set), icons (array, predefined image names array, available icons can be obtained in the nodeIconList list in the [https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js) file, icon name is type_name, such as ['priority_1']) |
|
||||
| SET_NODE_HYPERLINK | Set Node Hyperlink | node (node to set), link (hyperlink address), title (hyperlink name, optional) |
|
||||
| SET_NODE_NOTE | Set Node Note | node (node to set), note (note text) |
|
||||
| SET_NODE_TAG | Set Node Tag | node (node to set), tag (string array, built-in color information can be obtained in [https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/utils/constant.js)) |
|
||||
| INSERT_AFTER (v0.1.5+) | Move Node to After Another Node | node (node to move), exist (target node) |
|
||||
| INSERT_BEFORE (v0.1.5+) | Move Node to Before Another Node | node (node to move), exist (target node) |
|
||||
| MOVE_NODE_TO (v0.1.5+) | Move a node as a child of another node | node (the node to move), toNode (the target node) |
|
||||
| ADD_GENERALIZATION (v0.2.0+) | Add a node summary | data (the data for the summary, in object format, all numerical fields of the node are supported, default is `{text: 'summary'}`) |
|
||||
| REMOVE_GENERALIZATION (v0.2.0+) | Remove a node summary | |
|
||||
| SET_NODE_CUSTOM_POSITION (v0.2.0+) | Set a custom position for a node | node (the node to set), left (custom x coordinate, default is undefined), top (custom y coordinate, default is undefined) |
|
||||
| RESET_LAYOUT (v0.2.0+) | Arrange layout with one click | |
|
||||
| SET_NODE_SHAPE (v0.2.4+) | Set the shape of a node | node (the node to set), shape (the shape, all shapes: https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/Shape.js) |
|
||||
|
||||
### setData(data)
|
||||
|
||||
@@ -311,6 +322,8 @@ view, etc. data
|
||||
|
||||
### export(type, isDownload, fileName)
|
||||
|
||||
> You need to register the `Export` plugin first
|
||||
|
||||
Export
|
||||
|
||||
`type`: the type to be exported, optional values: png, svg, json, pdf (v0.2.1+),
|
||||
@@ -327,4 +340,16 @@ map).
|
||||
> v0.1.5+
|
||||
|
||||
Convert the coordinates of the browser's visible window to coordinates relative
|
||||
to the canvas.
|
||||
to the canvas.
|
||||
|
||||
### addPlugin(plugin, opt)
|
||||
|
||||
> v0.4.0+
|
||||
|
||||
Register plugin, Use `MindMap.usePlugin` to register plugin only before instantiation, The registered plugin will not take effect after instantiation, So if you want to register the plugin after instantiation, you can use the `addPlugin` method of the instance.
|
||||
|
||||
### removePlugin(plugin)
|
||||
|
||||
> v0.4.0+
|
||||
|
||||
Remove registered plugin, Plugins registered through the `usePlugin` or `addPlugin` methods can be removed.
|
||||
@@ -16,38 +16,6 @@
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
<h2>Xmind Parsing Method</h2>
|
||||
<blockquote>
|
||||
<p>v0.2.7+</p>
|
||||
</blockquote>
|
||||
<p>You can get the <code>Xmind</code> parsing method as follows:</p>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">"simple-mind-map"</span>;
|
||||
|
||||
<span class="hljs-built_in">console</span>.log(MindMap.xmind);
|
||||
</code></pre>
|
||||
<p>The <code>MindMap.xmind</code> object has two methods attached:</p>
|
||||
<h3>parseXmindFile(file)</h3>
|
||||
<p>Parsing the <code>.xmind</code> file and returning the parsed data. Note that this is
|
||||
complete data, including the node tree, theme, and structure. You can use
|
||||
<code>mindMap.setFullData(data)</code> to render the returned data to the canvas.</p>
|
||||
<p><code>file</code>: <code>File</code> object</p>
|
||||
<h3>transformXmind(content)</h3>
|
||||
<p>Convert <code>xmind</code> data. The <code>.xmind</code> file is essentially a <code>zip</code> file that can be
|
||||
decompressed by changing the suffix to zip. Inside, there is a <code>content.json</code>
|
||||
file. If you have parsed this file yourself, you can pass the contents of this
|
||||
file to this method for conversion. The converted data is the complete data,
|
||||
including the node tree, theme, structure, etc. You can use
|
||||
<code>mindMap.setFullData(data)</code> to render the returned data to the canvas.</p>
|
||||
<p><code>content</code>: the contents of the <code>content.json</code> file within the <code>.xmind</code> zip
|
||||
package</p>
|
||||
<h3>transformOldXmind(content)</h3>
|
||||
<blockquote>
|
||||
<p>v0.2.8+</p>
|
||||
</blockquote>
|
||||
<p>For data parsing of the <code>xmind8</code> version, because the <code>.xmind</code> file in this
|
||||
version does not have a <code>content.json</code>, it corresponds to <code>content.xml</code>.</p>
|
||||
<p><code>content</code>: the contents of the <code>content.xml</code> file within the <code>.xmind</code> zip
|
||||
package</p>
|
||||
<h2>Instantiation options</h2>
|
||||
<table>
|
||||
<thead>
|
||||
@@ -172,6 +140,34 @@ package</p>
|
||||
<td>Watermark config, Please refer to the table 【Watermark config】 below for detailed configuration</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>textAutoWrapWidth(v0.3.4+)</td>
|
||||
<td>Number</td>
|
||||
<td>500</td>
|
||||
<td>Each line of text in the node will wrap automatically when it reaches the width</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>customHandleMousewheel(v0.4.3+)</td>
|
||||
<td>Function</td>
|
||||
<td>null</td>
|
||||
<td>User-defined mouse wheel event processing can pass a function, and the callback parameter is the event object</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mousewheelAction(v0.4.3+)</td>
|
||||
<td>String</td>
|
||||
<td>zoom</td>
|
||||
<td>The behavior of the mouse wheel, <code>zoom</code>(Zoom in and out)、<code>move</code>(Move up and down). If <code>customHandleMousewheel</code> passes a custom function, this property will not take effect</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mousewheelMoveStep(v0.4.3+)</td>
|
||||
<td>Number</td>
|
||||
<td>100</td>
|
||||
<td>When the <code>mousewheelAction</code> is set to <code>move</code>, you can use this attribute to control the step length of the view movement when the mouse scrolls. The unit is <code>px</code></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Watermark config</h3>
|
||||
@@ -239,11 +235,52 @@ MindMap.defineTheme(<span class="hljs-string">'Theme name'</span>, {})
|
||||
mindMap.setTheme(<span class="hljs-string">'Theme name'</span>)
|
||||
</code></pre>
|
||||
<p>For all configurations of theme, please refer to <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/default.js">Default Topic</a>. The <code>defineTheme</code>method will merge the configuration you passed in with the default configuration. Most of the themes do not need custom many parts. For a typical customized theme configuration, please refer to <a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/themes/blueSky.js">blueSky</a>.</p>
|
||||
<h3>usePlugin(plugin, opt = {})</h3>
|
||||
<blockquote>
|
||||
<p>v0.3.0+</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li><code>opt</code>:v0.4.0+,Plugin options. If a plugin supports custom options, it can be passed in through this parameter.</li>
|
||||
</ul>
|
||||
<p>If you need to use some non-core functions, such as mini map, watermark, etc, you can register plugin through this method. Can be called in chain.</p>
|
||||
<p>Note: The plugin needs to be registered before instantiating <code>MindMap</code>.</p>
|
||||
<h3>hasPlugin(plugin)</h3>
|
||||
<blockquote>
|
||||
<p>v0.4.0+</p>
|
||||
</blockquote>
|
||||
<p>Get whether a plugin is registered, The index of the plugin in the registered plugin list is returned, If it is <code>-1</code>, it means that the plugin is not registered.</p>
|
||||
<h2>Static props</h2>
|
||||
<h3>pluginList</h3>
|
||||
<blockquote>
|
||||
<p>v0.3.0+</p>
|
||||
</blockquote>
|
||||
<p>List of all currently registered plugins.</p>
|
||||
<h2>Instance methods</h2>
|
||||
<h3>render()</h3>
|
||||
<h3>getSvgData()</h3>
|
||||
<blockquote>
|
||||
<p>v0.3.0+</p>
|
||||
</blockquote>
|
||||
<p>Get the <code>svg</code> data and return an object. The detailed structure is as follows:</p>
|
||||
<pre class="hljs"><code>{
|
||||
svg, <span class="hljs-comment">// Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)</span>
|
||||
svgHTML, <span class="hljs-comment">// String, svg string, i.e. html string, can be directly rendered to the small map container you prepared</span>
|
||||
<span class="hljs-attr">rect</span>: <span class="hljs-comment">// Object, position, size, etc. of mind map graphics before zoom</span>
|
||||
origWidth, <span class="hljs-comment">// Number, canvas width</span>
|
||||
origHeight, <span class="hljs-comment">// Number, canvas height</span>
|
||||
scaleX, <span class="hljs-comment">// Number, horizontal zoom value of mind map graphics</span>
|
||||
scaleY, <span class="hljs-comment">// Number, vertical zoom value of mind map graphics</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3>render(callback)</h3>
|
||||
<ul>
|
||||
<li><code>callback</code>: <code>v0.3.2+</code>, <code>Function</code>, Called when the re-rendering is complete</li>
|
||||
</ul>
|
||||
<p>Triggers a full rendering, which will reuse nodes for better performance. If
|
||||
only the node positions have changed, this method can be called to <code>reRender</code>.</p>
|
||||
<h3>reRender()</h3>
|
||||
<h3>reRender(callback)</h3>
|
||||
<ul>
|
||||
<li><code>callback</code>: <code>v0.3.2+</code>, <code>Function</code>, Called when the re-rendering is complete</li>
|
||||
</ul>
|
||||
<p>Performs a full re-render, clearing the canvas and creating new nodes. This has
|
||||
poor performance and should be used sparingly.</p>
|
||||
<h3>resize()</h3>
|
||||
@@ -351,6 +388,16 @@ poor performance and should be used sparingly.</p>
|
||||
<td>e (event object), this (node instance)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>node_mouseenter(v0.4.1+)</td>
|
||||
<td>Node mouseenter event</td>
|
||||
<td>this (node instance), e (event object)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>node_mouseleave(v0.4.1+)</td>
|
||||
<td>Node mouseleave event</td>
|
||||
<td>this (node instance), e (event object)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>before_node_active</td>
|
||||
<td>Event before node activation</td>
|
||||
<td>this (node instance), activeNodeList (current list of active nodes)</td>
|
||||
@@ -390,6 +437,16 @@ poor performance and should be used sparingly.</p>
|
||||
<td>Node tree render end event</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>rich_text_selection_change(v0.4.0+)</td>
|
||||
<td>Available when the <code>RichText</code> plugin is registered. Triggered when the text selection area changes when the node is edited</td>
|
||||
<td>hasRange(Whether there is a selection)、rectInfo(Size and location information of the selected area)、formatInfo(Text formatting information of the selected area)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>transforming-dom-to-images(v0.4.0+)</td>
|
||||
<td>Available when the <code>RichText</code> plugin is registered. When there is a <code>DOM</code> node in <code>svg</code>, the <code>DOM</code> node will be converted to an image when exporting to an image. This event will be triggered during the conversion process. You can use this event to prompt the user about the node to which you are currently converting</td>
|
||||
<td>index(Index of the node currently converted to)、len(Total number of nodes to be converted)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>emit(event, ...args)</h3>
|
||||
@@ -529,7 +586,7 @@ redo. All commands are as follows:</p>
|
||||
<tr>
|
||||
<td>SET_NODE_TEXT</td>
|
||||
<td>Set node text</td>
|
||||
<td>node (the node to set), text (the new text for the node)</td>
|
||||
<td>node (the node to set), text (the new text for the node), richText(v0.4.0+, If you want to set a rich text character, you need to set it to <code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SET_NODE_IMAGE</td>
|
||||
@@ -617,6 +674,9 @@ redo. All commands are as follows:</p>
|
||||
includes the node tree, if <code>true</code> is passed, it will also include theme, layout,
|
||||
view, etc. data</p>
|
||||
<h3>export(type, isDownload, fileName)</h3>
|
||||
<blockquote>
|
||||
<p>You need to register the <code>Export</code> plugin first</p>
|
||||
</blockquote>
|
||||
<p>Export</p>
|
||||
<p><code>type</code>: the type to be exported, optional values: png, svg, json, pdf (v0.2.1+),
|
||||
smm (essentially also json)</p>
|
||||
@@ -630,6 +690,16 @@ map).</p>
|
||||
</blockquote>
|
||||
<p>Convert the coordinates of the browser's visible window to coordinates relative
|
||||
to the canvas.</p>
|
||||
<h3>addPlugin(plugin, opt)</h3>
|
||||
<blockquote>
|
||||
<p>v0.4.0+</p>
|
||||
</blockquote>
|
||||
<p>Register plugin, Use <code>MindMap.usePlugin</code> to register plugin only before instantiation, The registered plugin will not take effect after instantiation, So if you want to register the plugin after instantiation, you can use the <code>addPlugin</code> method of the instance.</p>
|
||||
<h3>removePlugin(plugin)</h3>
|
||||
<blockquote>
|
||||
<p>v0.4.0+</p>
|
||||
</blockquote>
|
||||
<p>Remove registered plugin, Plugins registered through the <code>usePlugin</code> or <code>addPlugin</code> methods can be removed.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
# DoExport instance
|
||||
# Export plugin
|
||||
|
||||
The `doExport` instance is responsible for exporting and can be obtained through
|
||||
`mindMap.doExport`.
|
||||
The `Export` plugin provides the export function.
|
||||
|
||||
## Register
|
||||
|
||||
```js
|
||||
import MindMap from 'simple-mind-map'
|
||||
import Export from 'simple-mind-map/src/Export.js'
|
||||
|
||||
MindMap.usePlugin(Export)
|
||||
```
|
||||
|
||||
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.doExport`.
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -10,19 +20,40 @@ The `doExport` instance is responsible for exporting and can be obtained through
|
||||
Exports as `png`, an async method that returns image data, `data:url` data which
|
||||
can be downloaded or displayed.
|
||||
|
||||
### svg()
|
||||
### svg(name, domToImage = false, plusCssText)
|
||||
|
||||
- `name`:`svg` title
|
||||
|
||||
- `domToImage`:v0.4.0+, When node rich text editing is enabled, you can use this parameter to specify whether to convert the `dom` node in the `svg` into a picture
|
||||
|
||||
- `plusCssText`:v0.4.0+, When node rich text editing is enabled and `domToImage` passes `false`, additional `css` styles can be added. If there is a `dom` node in `svg`, you can set some styles for the node through this parameter, such as:
|
||||
|
||||
```js
|
||||
svg(
|
||||
'',
|
||||
false,
|
||||
`* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}`
|
||||
)
|
||||
```
|
||||
|
||||
Exports as `svg`, an async method that returns `svg` data, `data:url` data which
|
||||
can be downloaded or displayed.
|
||||
|
||||
### getSvgData()
|
||||
### getSvgData(domToImage)
|
||||
|
||||
- `domToImage`:v0.4.0+, If node rich text is enabled, you can use this parameter to specify whether to convert the `DOM` node embedded in `svg` into a picture.
|
||||
|
||||
Gets `svg` data, an async method that returns an object:
|
||||
|
||||
```js
|
||||
{
|
||||
node; // svg object
|
||||
str; // svg string
|
||||
str; // svg string, if rich text editing is enabled and domToImage is set to true, the dom node in the svg character returned by this value will be converted into the form of an image
|
||||
nodeWithDomToImg// v0.4.0+,The svg object after the DOM node is converted to an image has a value only when rich text editing is enabled and domToImage is set to true, otherwise null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,20 +1,51 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>DoExport instance</h1>
|
||||
<p>The <code>doExport</code> instance is responsible for exporting and can be obtained through
|
||||
<code>mindMap.doExport</code>.</p>
|
||||
<h1>Export plugin</h1>
|
||||
<p>The <code>Export</code> plugin provides the export function.</p>
|
||||
<h2>Register</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> Export <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/Export.js'</span>
|
||||
|
||||
MindMap.usePlugin(Export)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.doExport</code>.</p>
|
||||
<h2>Methods</h2>
|
||||
<h3>png()</h3>
|
||||
<p>Exports as <code>png</code>, an async method that returns image data, <code>data:url</code> data which
|
||||
can be downloaded or displayed.</p>
|
||||
<h3>svg()</h3>
|
||||
<h3>svg(name, domToImage = false, plusCssText)</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>name</code>:<code>svg</code> title</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>domToImage</code>:v0.4.0+, When node rich text editing is enabled, you can use this parameter to specify whether to convert the <code>dom</code> node in the <code>svg</code> into a picture</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>plusCssText</code>:v0.4.0+, When node rich text editing is enabled and <code>domToImage</code> passes <code>false</code>, additional <code>css</code> styles can be added. If there is a <code>dom</code> node in <code>svg</code>, you can set some styles for the node through this parameter, such as:</p>
|
||||
</li>
|
||||
</ul>
|
||||
<pre class="hljs"><code>svg(
|
||||
<span class="hljs-string">''</span>,
|
||||
<span class="hljs-literal">false</span>,
|
||||
<span class="hljs-string">`* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}`</span>
|
||||
)
|
||||
</code></pre>
|
||||
<p>Exports as <code>svg</code>, an async method that returns <code>svg</code> data, <code>data:url</code> data which
|
||||
can be downloaded or displayed.</p>
|
||||
<h3>getSvgData()</h3>
|
||||
<h3>getSvgData(domToImage)</h3>
|
||||
<ul>
|
||||
<li><code>domToImage</code>:v0.4.0+, If node rich text is enabled, you can use this parameter to specify whether to convert the <code>DOM</code> node embedded in <code>svg</code> into a picture.</li>
|
||||
</ul>
|
||||
<p>Gets <code>svg</code> data, an async method that returns an object:</p>
|
||||
<pre class="hljs"><code>{
|
||||
node; <span class="hljs-comment">// svg object</span>
|
||||
str; <span class="hljs-comment">// svg string</span>
|
||||
str; <span class="hljs-comment">// svg string, if rich text editing is enabled and domToImage is set to true, the dom node in the svg character returned by this value will be converted into the form of an image</span>
|
||||
nodeWithDomToImg<span class="hljs-comment">// v0.4.0+,The svg object after the DOM node is converted to an image has a value only when rich text editing is enabled and domToImage is set to true, otherwise null</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3>pdf(name)</h3>
|
||||
|
||||
21
web/src/pages/Doc/en/drag/index.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Drag plugin
|
||||
|
||||
The `Drag` plugin provides the function of node dragging, including:
|
||||
|
||||
1.Drag the node to move and change its position in the node tree, That is, as a child node, sibling node, etc. of other nodes
|
||||
|
||||
2.Drag the node to the custom canvas location
|
||||
|
||||
Please refer to the [Instantiation Options](/mind-map/#/doc/zh/constructor) of the `MindMap` class for configuration.
|
||||
|
||||
## Register
|
||||
|
||||
```js
|
||||
import MindMap from 'simple-mind-map'
|
||||
import Drag from 'simple-mind-map/src/Drag.js'
|
||||
|
||||
MindMap.usePlugin(Drag)
|
||||
```
|
||||
|
||||
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.drag`.
|
||||
|
||||
27
web/src/pages/Doc/en/drag/index.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Drag plugin</h1>
|
||||
<p>The <code>Drag</code> plugin provides the function of node dragging, including:</p>
|
||||
<p>1.Drag the node to move and change its position in the node tree, That is, as a child node, sibling node, etc. of other nodes</p>
|
||||
<p>2.Drag the node to the custom canvas location</p>
|
||||
<p>Please refer to the <a href="/mind-map/#/doc/zh/constructor">Instantiation Options</a> of the <code>MindMap</code> class for configuration.</p>
|
||||
<h2>Register</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> Drag <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/Drag.js'</span>
|
||||
|
||||
MindMap.usePlugin(Drag)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.drag</code>.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
## Features
|
||||
|
||||
- [x] Plugin architecture. In addition to core functions, other functions are provided as plugins, which can be used as needed to reduce the overall volume
|
||||
- [x] Supports four types of structures: logical structure diagrams, mind maps,
|
||||
organizational structure diagrams, and directory organization diagrams
|
||||
- [x] Built-in multiple themes and allows for highly customized styles, and support register new themes
|
||||
@@ -16,7 +17,7 @@
|
||||
- [x] Supports free dragging and dragging to adjust nodes
|
||||
- [x] Supports various node shapes
|
||||
- [x] Supports export to json, png, svg, pdf, and import from json, xmind
|
||||
- [x] Supports mini map
|
||||
- [x] Supports mini map、support watermark
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -32,21 +33,19 @@ on `Vue2.x` and `ElementUI`. Features include:
|
||||
|
||||
- [x] Toolbar, which supports inserting and deleting nodes, and editing node
|
||||
images, icons, hyperlinks, notes, tags, and summaries
|
||||
|
||||
- [x] Sidebar, with panels for basic style settings, node style settings,
|
||||
outline, theme selection, and structure selection
|
||||
|
||||
- [x] Import and export functionality; data is saved in the browser's local
|
||||
storage by default, but it also supports creating, opening, and editing
|
||||
local files on the computer directly
|
||||
|
||||
- [x] Right-click menu, which supports operations such as expanding, collapsing,
|
||||
and organizing layout
|
||||
|
||||
- [x] Bottom bar, which supports node and word count statistics, switching
|
||||
between edit and read-only modes, zooming in and out, and switching to
|
||||
full screen, support mini map
|
||||
|
||||
Provide document page service.
|
||||
|
||||
3.`dist`
|
||||
|
||||
The folder containing the packaged resources for the `web` folder.
|
||||
@@ -61,11 +60,15 @@ Documentation, etc.
|
||||
|
||||
[Only a hundred lines of code are needed to add local file operation capability to your Web page. Are you sure not to try?](https://juejin.cn/post/7157681502506090510)
|
||||
|
||||
[When you press the direction key, how does the TV find the next focus](https://juejin.cn/post/7199666255883927612)
|
||||
|
||||
[How to simulate the background image style of css in canvas](https://juejin.cn/post/7204854015463538744)
|
||||
|
||||
## Special Note
|
||||
|
||||
This project is rough and has not been thoroughly tested, its features are not
|
||||
yet fully developed, and there are some performance issues. It is only for
|
||||
learning and reference purposes and should not be used in actual projects.
|
||||
yet fully developed, and there are some performance issues, especially when the number of nodes is large. It is only for
|
||||
learning and reference purposes and please use it carefully for actual projects.
|
||||
|
||||
The built-in themes and icons in the project come from:
|
||||
|
||||
|
||||
@@ -4,19 +4,20 @@
|
||||
<p><code>simple-mind-map</code> is a simple and powerful web mind map library, not dependent on any specific framework.</p>
|
||||
<h2>Features</h2>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox16" checked="true"><label for="checkbox16">Supports four types of structures: logical structure diagrams, mind maps,</label>
|
||||
<li><input type="checkbox" id="checkbox17" checked="true"><label for="checkbox17">Plugin architecture. In addition to core functions, other functions are provided as plugins, which can be used as needed to reduce the overall volume</label></li>
|
||||
<li><input type="checkbox" id="checkbox18" checked="true"><label for="checkbox18">Supports four types of structures: logical structure diagrams, mind maps,</label>
|
||||
organizational structure diagrams, and directory organization diagrams</li>
|
||||
<li><input type="checkbox" id="checkbox17" checked="true"><label for="checkbox17">Built-in multiple themes and allows for highly customized styles, and support register new themes</label></li>
|
||||
<li><input type="checkbox" id="checkbox18" checked="true"><label for="checkbox18">Supports shortcuts</label></li>
|
||||
<li><input type="checkbox" id="checkbox19" checked="true"><label for="checkbox19">Node content supports images, icons, hyperlinks, notes, tags, and</label>
|
||||
<li><input type="checkbox" id="checkbox19" checked="true"><label for="checkbox19">Built-in multiple themes and allows for highly customized styles, and support register new themes</label></li>
|
||||
<li><input type="checkbox" id="checkbox20" checked="true"><label for="checkbox20">Supports shortcuts</label></li>
|
||||
<li><input type="checkbox" id="checkbox21" checked="true"><label for="checkbox21">Node content supports images, icons, hyperlinks, notes, tags, and</label>
|
||||
summaries</li>
|
||||
<li><input type="checkbox" id="checkbox20" checked="true"><label for="checkbox20">Supports forward and backward navigation</label></li>
|
||||
<li><input type="checkbox" id="checkbox21" checked="true"><label for="checkbox21">Supports dragging and scaling</label></li>
|
||||
<li><input type="checkbox" id="checkbox22" checked="true"><label for="checkbox22">Supports right-click and Ctrl + left-click to select multiple items</label></li>
|
||||
<li><input type="checkbox" id="checkbox23" checked="true"><label for="checkbox23">Supports free dragging and dragging to adjust nodes</label></li>
|
||||
<li><input type="checkbox" id="checkbox24" checked="true"><label for="checkbox24">Supports various node shapes</label></li>
|
||||
<li><input type="checkbox" id="checkbox25" checked="true"><label for="checkbox25">Supports export to json, png, svg, pdf, and import from json, xmind</label></li>
|
||||
<li><input type="checkbox" id="checkbox26" checked="true"><label for="checkbox26">Supports mini map</label></li>
|
||||
<li><input type="checkbox" id="checkbox22" checked="true"><label for="checkbox22">Supports forward and backward navigation</label></li>
|
||||
<li><input type="checkbox" id="checkbox23" checked="true"><label for="checkbox23">Supports dragging and scaling</label></li>
|
||||
<li><input type="checkbox" id="checkbox24" checked="true"><label for="checkbox24">Supports right-click and Ctrl + left-click to select multiple items</label></li>
|
||||
<li><input type="checkbox" id="checkbox25" checked="true"><label for="checkbox25">Supports free dragging and dragging to adjust nodes</label></li>
|
||||
<li><input type="checkbox" id="checkbox26" checked="true"><label for="checkbox26">Supports various node shapes</label></li>
|
||||
<li><input type="checkbox" id="checkbox27" checked="true"><label for="checkbox27">Supports export to json, png, svg, pdf, and import from json, xmind</label></li>
|
||||
<li><input type="checkbox" id="checkbox28" checked="true"><label for="checkbox28">Supports mini map、support watermark</label></li>
|
||||
</ul>
|
||||
<h2>Table of Contents</h2>
|
||||
<p>1.<code>simple-mind-map</code></p>
|
||||
@@ -26,29 +27,20 @@ frameworks such as Vue and React, or without a framework.</p>
|
||||
<p>This is an online mind map built using the <code>simple-mind-map</code> library and based
|
||||
on <code>Vue2.x</code> and <code>ElementUI</code>. Features include:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><input type="checkbox" id="checkbox27" checked="true"><label for="checkbox27">Toolbar, which supports inserting and deleting nodes, and editing node</label>
|
||||
images, icons, hyperlinks, notes, tags, and summaries</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><input type="checkbox" id="checkbox28" checked="true"><label for="checkbox28">Sidebar, with panels for basic style settings, node style settings,</label>
|
||||
outline, theme selection, and structure selection</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><input type="checkbox" id="checkbox29" checked="true"><label for="checkbox29">Import and export functionality; data is saved in the browser's local</label>
|
||||
<li><input type="checkbox" id="checkbox29" checked="true"><label for="checkbox29">Toolbar, which supports inserting and deleting nodes, and editing node</label>
|
||||
images, icons, hyperlinks, notes, tags, and summaries</li>
|
||||
<li><input type="checkbox" id="checkbox30" checked="true"><label for="checkbox30">Sidebar, with panels for basic style settings, node style settings,</label>
|
||||
outline, theme selection, and structure selection</li>
|
||||
<li><input type="checkbox" id="checkbox31" checked="true"><label for="checkbox31">Import and export functionality; data is saved in the browser's local</label>
|
||||
storage by default, but it also supports creating, opening, and editing
|
||||
local files on the computer directly</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><input type="checkbox" id="checkbox30" checked="true"><label for="checkbox30">Right-click menu, which supports operations such as expanding, collapsing,</label>
|
||||
and organizing layout</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><input type="checkbox" id="checkbox31" checked="true"><label for="checkbox31">Bottom bar, which supports node and word count statistics, switching</label>
|
||||
local files on the computer directly</li>
|
||||
<li><input type="checkbox" id="checkbox32" checked="true"><label for="checkbox32">Right-click menu, which supports operations such as expanding, collapsing,</label>
|
||||
and organizing layout</li>
|
||||
<li><input type="checkbox" id="checkbox33" checked="true"><label for="checkbox33">Bottom bar, which supports node and word count statistics, switching</label>
|
||||
between edit and read-only modes, zooming in and out, and switching to
|
||||
full screen, support mini map</p>
|
||||
</li>
|
||||
full screen, support mini map</li>
|
||||
</ul>
|
||||
<p>Provide document page service.</p>
|
||||
<p>3.<code>dist</code></p>
|
||||
<p>The folder containing the packaged resources for the <code>web</code> folder.</p>
|
||||
<p>4.<code>docs</code></p>
|
||||
@@ -56,10 +48,12 @@ full screen, support mini map</p>
|
||||
<h2>Related Articles</h2>
|
||||
<p><a href="https://juejin.cn/post/6987711560521089061">Technical Analysis of Web Mind Map Implementation (chi)</a></p>
|
||||
<p><a href="https://juejin.cn/post/7157681502506090510">Only a hundred lines of code are needed to add local file operation capability to your Web page. Are you sure not to try?</a></p>
|
||||
<p><a href="https://juejin.cn/post/7199666255883927612">When you press the direction key, how does the TV find the next focus</a></p>
|
||||
<p><a href="https://juejin.cn/post/7204854015463538744">How to simulate the background image style of css in canvas</a></p>
|
||||
<h2>Special Note</h2>
|
||||
<p>This project is rough and has not been thoroughly tested, its features are not
|
||||
yet fully developed, and there are some performance issues. It is only for
|
||||
learning and reference purposes and should not be used in actual projects.</p>
|
||||
yet fully developed, and there are some performance issues, especially when the number of nodes is large. It is only for
|
||||
learning and reference purposes and please use it carefully for actual projects.</p>
|
||||
<p>The built-in themes and icons in the project come from:</p>
|
||||
<p><a href="https://naotu.baidu.com/">Baidu Mind Map</a></p>
|
||||
<p><a href="https://www.zhixi.com/">Zhixi Mind Map</a></p>
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
# KeyboardNavigation instance
|
||||
# KeyboardNavigation plugin
|
||||
|
||||
> v0.2.17+
|
||||
|
||||
`KeyboardNavigation` instance is responsible for handling keyboard navigation, that is, when you press the direction key, it will automatically find the next node and activate it, can be obtained through `mindMap.keyboardNavigation`
|
||||
`KeyboardNavigation` plugin provides keyboard navigation function, that is, when you press the direction key, it will automatically find the next node and activate it.
|
||||
|
||||
## Register
|
||||
|
||||
```js
|
||||
import MindMap from 'simple-mind-map'
|
||||
import KeyboardNavigation from 'simple-mind-map/src/KeyboardNavigation.js'
|
||||
|
||||
MindMap.usePlugin(KeyboardNavigation)
|
||||
```
|
||||
|
||||
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.keyboardNavigation`.
|
||||
|
||||
## Methods
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>KeyboardNavigation instance</h1>
|
||||
<h1>KeyboardNavigation plugin</h1>
|
||||
<blockquote>
|
||||
<p>v0.2.17+</p>
|
||||
</blockquote>
|
||||
<p><code>KeyboardNavigation</code> instance is responsible for handling keyboard navigation, that is, when you press the direction key, it will automatically find the next node and activate it, can be obtained through <code>mindMap.keyboardNavigation</code></p>
|
||||
<p><code>KeyboardNavigation</code> plugin provides keyboard navigation function, that is, when you press the direction key, it will automatically find the next node and activate it.</p>
|
||||
<h2>Register</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> KeyboardNavigation <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/KeyboardNavigation.js'</span>
|
||||
|
||||
MindMap.usePlugin(KeyboardNavigation)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.keyboardNavigation</code>.</p>
|
||||
<h2>Methods</h2>
|
||||
<h3>focus(dir)</h3>
|
||||
<p><code>dir</code>:Which direction to find the next node,Optional value:<code>Left</code>、 <code>Up</code>、 <code>Right</code>、 <code>Down</code></p>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# MiniMap instance
|
||||
# MiniMap plugin
|
||||
|
||||
> v0.2.11+
|
||||
|
||||
@@ -8,27 +8,19 @@ When zoomed, moved, or there are too many elements, the canvas may only display
|
||||
part of the mind map content. The viewport frame can be used to view the current
|
||||
viewport location, and can be quickly positioned by dragging on the small map.
|
||||
|
||||
The `mindMap.miniMap` instance can be obtained through this.
|
||||
|
||||
## Methods
|
||||
|
||||
### getMiniMap()
|
||||
|
||||
Obtain small map related data, this function is generally not used directly, the
|
||||
function returns:
|
||||
## Register
|
||||
|
||||
```js
|
||||
{
|
||||
svg, // Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)
|
||||
svgHTML, // String, svg string, i.e. html string, can be directly rendered to the small map container you prepared
|
||||
rect: // Object, position, size, etc. of mind map graphics before zoom
|
||||
origWidth, // Number, canvas width
|
||||
origHeight, // Number, canvas height
|
||||
scaleX, // Number, horizontal zoom value of mind map graphics
|
||||
scaleY, // Number, vertical zoom value of mind map graphics
|
||||
}
|
||||
import MindMap from 'simple-mind-map'
|
||||
import MiniMap from 'simple-mind-map/src/MiniMap.js'
|
||||
|
||||
MindMap.usePlugin(MiniMap)
|
||||
```
|
||||
|
||||
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.miniMap`.
|
||||
|
||||
## Methods
|
||||
|
||||
### calculationMiniMap(boxWidth, boxHeight)
|
||||
|
||||
"Calculate the rendering data for the small map, this function will call the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>MiniMap instance</h1>
|
||||
<h1>MiniMap plugin</h1>
|
||||
<blockquote>
|
||||
<p>v0.2.11+</p>
|
||||
</blockquote>
|
||||
@@ -9,21 +9,14 @@ parts, one is the current canvas content, and the other is the viewport frame.
|
||||
When zoomed, moved, or there are too many elements, the canvas may only display
|
||||
part of the mind map content. The viewport frame can be used to view the current
|
||||
viewport location, and can be quickly positioned by dragging on the small map.</p>
|
||||
<p>The <code>mindMap.miniMap</code> instance can be obtained through this.</p>
|
||||
<h2>Methods</h2>
|
||||
<h3>getMiniMap()</h3>
|
||||
<p>Obtain small map related data, this function is generally not used directly, the
|
||||
function returns:</p>
|
||||
<pre class="hljs"><code>{
|
||||
svg, <span class="hljs-comment">// Element, the overall svg element of the mind map graphics, including: svg (canvas container), g (actual mind map group)</span>
|
||||
svgHTML, <span class="hljs-comment">// String, svg string, i.e. html string, can be directly rendered to the small map container you prepared</span>
|
||||
<span class="hljs-attr">rect</span>: <span class="hljs-comment">// Object, position, size, etc. of mind map graphics before zoom</span>
|
||||
origWidth, <span class="hljs-comment">// Number, canvas width</span>
|
||||
origHeight, <span class="hljs-comment">// Number, canvas height</span>
|
||||
scaleX, <span class="hljs-comment">// Number, horizontal zoom value of mind map graphics</span>
|
||||
scaleY, <span class="hljs-comment">// Number, vertical zoom value of mind map graphics</span>
|
||||
}
|
||||
<h2>Register</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> MiniMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/MiniMap.js'</span>
|
||||
|
||||
MindMap.usePlugin(MiniMap)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.miniMap</code>.</p>
|
||||
<h2>Methods</h2>
|
||||
<h3>calculationMiniMap(boxWidth, boxHeight)</h3>
|
||||
<p>"Calculate the rendering data for the small map, this function will call the
|
||||
<code>getMiniMap()</code> method, so using this function is sufficient.</p>
|
||||
|
||||
@@ -121,7 +121,9 @@ if `key` is not passed, return the `data` object
|
||||
Set the value of the specified key in the data object of the node's real data
|
||||
nodeData, `SET_NODE_DATA` command's shortcut method
|
||||
|
||||
### setText(text)
|
||||
### setText(text, richText)
|
||||
|
||||
- `richText`: v0.4.2+,`Boolean`, If you want to set rich text content, that is, `html` character, `richText` needs to be passed `true`
|
||||
|
||||
Setting the node text, a shortcut for the `SET_NODE_TEXT` command
|
||||
|
||||
|
||||
@@ -67,7 +67,10 @@ if <code>key</code> is not passed, return the <code>data</code> object</p>
|
||||
<h3>setData(data)</h3>
|
||||
<p>Set the value of the specified key in the data object of the node's real data
|
||||
nodeData, <code>SET_NODE_DATA</code> command's shortcut method</p>
|
||||
<h3>setText(text)</h3>
|
||||
<h3>setText(text, richText)</h3>
|
||||
<ul>
|
||||
<li><code>richText</code>: v0.4.2+,<code>Boolean</code>, If you want to set rich text content, that is, <code>html</code> character, <code>richText</code> needs to be passed <code>true</code></li>
|
||||
</ul>
|
||||
<p>Setting the node text, a shortcut for the <code>SET_NODE_TEXT</code> command</p>
|
||||
<h3>setImage(imgData)</h3>
|
||||
<p>Setting the node image, a shortcut for the <code>SET_NODE_IMAGE</code> command</p>
|
||||
|
||||
153
web/src/pages/Doc/en/richText/index.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# RichText plugin
|
||||
|
||||
> v0.4.0+
|
||||
|
||||
> Note: This is a testing nature and imperfect function
|
||||
|
||||
This plugin provides the ability to edit rich text of nodes, and takes effect after registration.
|
||||
|
||||
By default, node editing can only uniformly apply styles to all text in the node. This plugin can support rich text editing effects. Currently, it supports bold, italic, underline, strikethrough, font, font size, color, and backgroundColor. Underline and line height are not supported.
|
||||
|
||||
The principle of this plugin is to use [Quill](https://github.com/quilljs/quill) editor implements rich text editing, and then uses the edited `DOM` node directly as the text data of the node, and embeds the `DOM` node through the `svg` `foreignObject` tag during rendering.
|
||||
|
||||
This also caused a problem, that is, the function of exporting as a picture was affected, The original principle of exporting `svg` as an image is very simple, Get the `svg` string, and then create the `blob` data of the `type=image/svg+xml` type. Then use the `URL.createObjectURL` method to generate the `data:url` data. Then create a `Image` tag, use the `data:url` as the `src` of the image, and finally draw the image on the `canvas` object for export, However, after testing, when the `DOM` node is embedded in the `svg`, this method of export will cause errors, and after trying many ways, the perfect export effect cannot be achieved, The current method is to traverse the `foreignObject` node in `svg`, using [html2canvas](https://github.com/niklasvh/html2canvas) Convert the `DOM` node in the `foreignObject` node into an image and then replace the `foreignObject` node. This method can work, but it is very time-consuming. Because the `html2canvas` conversion takes a long time, it takes about 2 seconds to convert a node. This leads to the more nodes, the slower the conversion time. Therefore, it is recommended not to use this plugin if you cannot tolerate the long time of export.
|
||||
|
||||
If you have a better way, please leave a message.
|
||||
|
||||
## Register
|
||||
|
||||
```js
|
||||
import MindMap from 'simple-mind-map'
|
||||
import RichText from 'simple-mind-map/src/RichText.js'
|
||||
|
||||
MindMap.usePlugin(RichText, opt?)
|
||||
```
|
||||
|
||||
After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.richText`.
|
||||
|
||||
### Register options
|
||||
|
||||
The `opt` option can pass the following parameters:
|
||||
|
||||
- `opt.fontFamilyList`
|
||||
|
||||
Replace the built-in font list during rich text editing. The built-in list is:
|
||||
|
||||
```js
|
||||
[
|
||||
'宋体, SimSun, Songti SC',
|
||||
'微软雅黑, Microsoft YaHei',
|
||||
'楷体, 楷体_GB2312, SimKai, STKaiti',
|
||||
'黑体, SimHei, Heiti SC',
|
||||
'隶书, SimLi',
|
||||
'andale mono',
|
||||
'arial, helvetica, sans-serif',
|
||||
'arial black, avant garde',
|
||||
'comic sans ms',
|
||||
'impact, chicago',
|
||||
'times new roman',
|
||||
'sans-serif',
|
||||
'serif'
|
||||
]
|
||||
```
|
||||
|
||||
- `opt.fontSizeList`
|
||||
|
||||
Replace the built-in font size list during rich text editing. The built-in list is:
|
||||
|
||||
```js
|
||||
[1, 2, 3, ...100]
|
||||
```
|
||||
|
||||
## Method
|
||||
|
||||
### selectAll()
|
||||
|
||||
Select All. When the node is being edited, you can select all the text in the node through this method.
|
||||
|
||||
### formatText(config = {})
|
||||
|
||||
- `config`:Object. The key is the style attribute and the value is the style value. The complete configuration is as follows:
|
||||
|
||||
```js
|
||||
{
|
||||
font: '字体',
|
||||
size: '12px,' // font size
|
||||
bold: true, // Bold or not, true/false
|
||||
italic: true, // Italic or not, true/false
|
||||
underline: true, // Show underline or not, true/false
|
||||
strike: true, // Whether to display strikethrough, true/false
|
||||
color: '#333' // color
|
||||
}
|
||||
```
|
||||
|
||||
Formats the currently selected text.
|
||||
|
||||
### formatRangeText(range, config = {})
|
||||
|
||||
- `range`:The range object of `Quill`, has the following format:
|
||||
|
||||
```js
|
||||
{
|
||||
index,
|
||||
length
|
||||
}
|
||||
```
|
||||
|
||||
- `config`:Same as `formatText` method
|
||||
|
||||
Formats the text of the specified range.
|
||||
|
||||
### formatAllText(config = {})
|
||||
|
||||
- `config`:Same as `formatText` method
|
||||
|
||||
Formats all text of the current edit node.
|
||||
|
||||
### removeFormat()
|
||||
|
||||
> v0.4.1+
|
||||
|
||||
Clears the style of the currently selected text.
|
||||
|
||||
### normalStyleToRichTextStyle(style)
|
||||
|
||||
Converts a normal node style object to a rich text style object. Because there are differences between node style attributes and rich text style attributes during non-rich text editing, a conversion operation is required. For example:
|
||||
|
||||
```js
|
||||
{
|
||||
fontFamily: 'xxx'
|
||||
}
|
||||
|
||||
// After conversion
|
||||
|
||||
{
|
||||
font: 'xxx'
|
||||
}
|
||||
```
|
||||
|
||||
### richTextStyleToNormalStyle(config)
|
||||
|
||||
Converts rich text style objects to normal node style objects. For example:
|
||||
|
||||
```js
|
||||
{
|
||||
size: '16px'
|
||||
}
|
||||
|
||||
// After conversion
|
||||
|
||||
{
|
||||
fontSize: 16
|
||||
}
|
||||
```
|
||||
|
||||
### handleSvgDomElements(svg)
|
||||
|
||||
- `svg`: `svg` node
|
||||
|
||||
Convert the `dom` element embedded in the `svg` into a picture and return a `Promise`.
|
||||
|
||||
### transformAllNodesToNormalNode()
|
||||
|
||||
Convert all nodes to non-rich text nodes.
|
||||
134
web/src/pages/Doc/en/richText/index.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>RichText plugin</h1>
|
||||
<blockquote>
|
||||
<p>v0.4.0+</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>Note: This is a testing nature and imperfect function</p>
|
||||
</blockquote>
|
||||
<p>This plugin provides the ability to edit rich text of nodes, and takes effect after registration.</p>
|
||||
<p>By default, node editing can only uniformly apply styles to all text in the node. This plugin can support rich text editing effects. Currently, it supports bold, italic, underline, strikethrough, font, font size, color, and backgroundColor. Underline and line height are not supported.</p>
|
||||
<p>The principle of this plugin is to use <a href="https://github.com/quilljs/quill">Quill</a> editor implements rich text editing, and then uses the edited <code>DOM</code> node directly as the text data of the node, and embeds the <code>DOM</code> node through the <code>svg</code> <code>foreignObject</code> tag during rendering.</p>
|
||||
<p>This also caused a problem, that is, the function of exporting as a picture was affected, The original principle of exporting <code>svg</code> as an image is very simple, Get the <code>svg</code> string, and then create the <code>blob</code> data of the <code>type=image/svg+xml</code> type. Then use the <code>URL.createObjectURL</code> method to generate the <code>data:url</code> data. Then create a <code>Image</code> tag, use the <code>data:url</code> as the <code>src</code> of the image, and finally draw the image on the <code>canvas</code> object for export, However, after testing, when the <code>DOM</code> node is embedded in the <code>svg</code>, this method of export will cause errors, and after trying many ways, the perfect export effect cannot be achieved, The current method is to traverse the <code>foreignObject</code> node in <code>svg</code>, using <a href="https://github.com/niklasvh/html2canvas">html2canvas</a> Convert the <code>DOM</code> node in the <code>foreignObject</code> node into an image and then replace the <code>foreignObject</code> node. This method can work, but it is very time-consuming. Because the <code>html2canvas</code> conversion takes a long time, it takes about 2 seconds to convert a node. This leads to the more nodes, the slower the conversion time. Therefore, it is recommended not to use this plugin if you cannot tolerate the long time of export.</p>
|
||||
<p>If you have a better way, please leave a message.</p>
|
||||
<h2>Register</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||
<span class="hljs-keyword">import</span> RichText <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/RichText.js'</span>
|
||||
|
||||
MindMap.usePlugin(RichText, opt?)
|
||||
</code></pre>
|
||||
<p>After registration and instantiation of <code>MindMap</code>, the instance can be obtained through <code>mindMap.richText</code>.</p>
|
||||
<h3>Register options</h3>
|
||||
<p>The <code>opt</code> option can pass the following parameters:</p>
|
||||
<ul>
|
||||
<li><code>opt.fontFamilyList</code></li>
|
||||
</ul>
|
||||
<p>Replace the built-in font list during rich text editing. The built-in list is:</p>
|
||||
<pre class="hljs"><code>[
|
||||
<span class="hljs-string">'宋体, SimSun, Songti SC'</span>,
|
||||
<span class="hljs-string">'微软雅黑, Microsoft YaHei'</span>,
|
||||
<span class="hljs-string">'楷体, 楷体_GB2312, SimKai, STKaiti'</span>,
|
||||
<span class="hljs-string">'黑体, SimHei, Heiti SC'</span>,
|
||||
<span class="hljs-string">'隶书, SimLi'</span>,
|
||||
<span class="hljs-string">'andale mono'</span>,
|
||||
<span class="hljs-string">'arial, helvetica, sans-serif'</span>,
|
||||
<span class="hljs-string">'arial black, avant garde'</span>,
|
||||
<span class="hljs-string">'comic sans ms'</span>,
|
||||
<span class="hljs-string">'impact, chicago'</span>,
|
||||
<span class="hljs-string">'times new roman'</span>,
|
||||
<span class="hljs-string">'sans-serif'</span>,
|
||||
<span class="hljs-string">'serif'</span>
|
||||
]
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><code>opt.fontSizeList</code></li>
|
||||
</ul>
|
||||
<p>Replace the built-in font size list during rich text editing. The built-in list is:</p>
|
||||
<pre class="hljs"><code>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, ..<span class="hljs-number">.100</span>]
|
||||
</code></pre>
|
||||
<h2>Method</h2>
|
||||
<h3>selectAll()</h3>
|
||||
<p>Select All. When the node is being edited, you can select all the text in the node through this method.</p>
|
||||
<h3>formatText(config = {})</h3>
|
||||
<ul>
|
||||
<li><code>config</code>:Object. The key is the style attribute and the value is the style value. The complete configuration is as follows:</li>
|
||||
</ul>
|
||||
<pre class="hljs"><code>{
|
||||
<span class="hljs-attr">font</span>: <span class="hljs-string">'字体'</span>,
|
||||
<span class="hljs-attr">size</span>: <span class="hljs-string">'12px,'</span> <span class="hljs-comment">// font size</span>
|
||||
<span class="hljs-attr">bold</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Bold or not, true/false </span>
|
||||
<span class="hljs-attr">italic</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Italic or not, true/false </span>
|
||||
<span class="hljs-attr">underline</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Show underline or not, true/false </span>
|
||||
<span class="hljs-attr">strike</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Whether to display strikethrough, true/false </span>
|
||||
<span class="hljs-attr">color</span>: <span class="hljs-string">'#333'</span> <span class="hljs-comment">// color</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>Formats the currently selected text.</p>
|
||||
<h3>formatRangeText(range, config = {})</h3>
|
||||
<ul>
|
||||
<li><code>range</code>:The range object of <code>Quill</code>, has the following format:</li>
|
||||
</ul>
|
||||
<pre class="hljs"><code>{
|
||||
index,
|
||||
length
|
||||
}
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><code>config</code>:Same as <code>formatText</code> method</li>
|
||||
</ul>
|
||||
<p>Formats the text of the specified range.</p>
|
||||
<h3>formatAllText(config = {})</h3>
|
||||
<ul>
|
||||
<li><code>config</code>:Same as <code>formatText</code> method</li>
|
||||
</ul>
|
||||
<p>Formats all text of the current edit node.</p>
|
||||
<h3>removeFormat()</h3>
|
||||
<blockquote>
|
||||
<p>v0.4.1+</p>
|
||||
</blockquote>
|
||||
<p>Clears the style of the currently selected text.</p>
|
||||
<h3>normalStyleToRichTextStyle(style)</h3>
|
||||
<p>Converts a normal node style object to a rich text style object. Because there are differences between node style attributes and rich text style attributes during non-rich text editing, a conversion operation is required. For example:</p>
|
||||
<pre class="hljs"><code>{
|
||||
<span class="hljs-attr">fontFamily</span>: <span class="hljs-string">'xxx'</span>
|
||||
}
|
||||
|
||||
<span class="hljs-comment">// After conversion</span>
|
||||
|
||||
{
|
||||
<span class="hljs-attr">font</span>: <span class="hljs-string">'xxx'</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3>richTextStyleToNormalStyle(config)</h3>
|
||||
<p>Converts rich text style objects to normal node style objects. For example:</p>
|
||||
<pre class="hljs"><code>{
|
||||
<span class="hljs-attr">size</span>: <span class="hljs-string">'16px'</span>
|
||||
}
|
||||
|
||||
<span class="hljs-comment">// After conversion</span>
|
||||
|
||||
{
|
||||
<span class="hljs-attr">fontSize</span>: <span class="hljs-number">16</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3>handleSvgDomElements(svg)</h3>
|
||||
<ul>
|
||||
<li><code>svg</code>: <code>svg</code> node</li>
|
||||
</ul>
|
||||
<p>Convert the <code>dom</code> element embedded in the <code>svg</code> into a picture and return a <code>Promise</code>.</p>
|
||||
<h3>transformAllNodesToNormalNode()</h3>
|
||||
<p>Convert all nodes to non-rich text nodes.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||