mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 14:04:47 +08:00
Feat:1.支持暂停检查鼠标是否在画布内;2.快捷键响应目标由硬编码改为由插件控制
This commit is contained in:
@@ -78,6 +78,10 @@ class MindMap {
|
||||
*/
|
||||
this.nodeInnerPrefixList = []
|
||||
|
||||
// 编辑节点的类名列表,快捷键响应会检查事件目标是否是body或该列表中的元素,是的话才会响应
|
||||
// 该检查可以通过customCheckEnableShortcut选项来覆盖
|
||||
this.editNodeClassList = []
|
||||
|
||||
// 画布
|
||||
this.initContainer()
|
||||
|
||||
@@ -241,6 +245,29 @@ class MindMap {
|
||||
if (this.cssEl) document.head.removeChild(this.cssEl)
|
||||
}
|
||||
|
||||
// 检查某个编辑节点类名是否存在,返回索引
|
||||
checkEditNodeClassIndex(className) {
|
||||
return this.editNodeClassList.findIndex(item => {
|
||||
return item === className
|
||||
})
|
||||
}
|
||||
|
||||
// 添加一个编辑节点类名
|
||||
addEditNodeClass(className) {
|
||||
const index = this.checkEditNodeClassIndex(className)
|
||||
if (index === -1) {
|
||||
this.editNodeClassList.push(className)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除一个编辑节点类名
|
||||
deleteEditNodeClass(className) {
|
||||
const index = this.checkEditNodeClassIndex(className)
|
||||
if (index !== -1) {
|
||||
this.editNodeClassList.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染,部分渲染
|
||||
render(callback, source = '') {
|
||||
this.initTheme()
|
||||
|
||||
@@ -79,11 +79,6 @@ export const CONSTANTS = {
|
||||
TOP: 'top',
|
||||
RIGHT: 'right',
|
||||
BOTTOM: 'bottom'
|
||||
},
|
||||
EDIT_NODE_CLASS: {
|
||||
SMM_NODE_EDIT_WRAP: 'smm-node-edit-wrap',
|
||||
RICH_TEXT_EDIT_WRAP: 'ql-editor',
|
||||
ASSOCIATIVE_LINE_TEXT_EDIT_WRAP: 'associative-line-text-edit-warp'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { keyMap } from './keyMap'
|
||||
import { CONSTANTS } from '../../constants/constant'
|
||||
|
||||
// 快捷按键、命令处理类
|
||||
export default class KeyCommand {
|
||||
@@ -13,6 +12,8 @@ export default class KeyCommand {
|
||||
this.shortcutMapCache = {}
|
||||
this.isPause = false
|
||||
this.isInSvg = false
|
||||
this.isStopCheckInSvg = false
|
||||
this.defaultEnableCheck = this.defaultEnableCheck.bind(this)
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -58,6 +59,22 @@ export default class KeyCommand {
|
||||
this.shortcutMapCache = {}
|
||||
}
|
||||
|
||||
// 停止对鼠标是否在画布内的检查,前提是开启了enableShortcutOnlyWhenMouseInSvg选项
|
||||
// 库内部节点文本编辑、关联线文本编辑、外框文本编辑前都会暂停检查,否则无法响应回车快捷键用于结束编辑
|
||||
// 如果你新增了额外的文本编辑,也可以在编辑前调用此方法
|
||||
stopCheckInSvg() {
|
||||
const { enableShortcutOnlyWhenMouseInSvg } = this.mindMap.opt
|
||||
if (!enableShortcutOnlyWhenMouseInSvg) return
|
||||
this.isStopCheckInSvg = true
|
||||
}
|
||||
|
||||
// 恢复对鼠标是否在画布内的检查
|
||||
recoveryCheckInSvg() {
|
||||
const { enableShortcutOnlyWhenMouseInSvg } = this.mindMap.opt
|
||||
if (!enableShortcutOnlyWhenMouseInSvg) return
|
||||
this.isStopCheckInSvg = true
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
this.onKeydown = this.onKeydown.bind(this)
|
||||
@@ -66,13 +83,6 @@ export default class KeyCommand {
|
||||
this.isInSvg = true
|
||||
})
|
||||
this.mindMap.on('svg_mouseleave', () => {
|
||||
if (this.mindMap.renderer.textEdit.isShowTextEdit()) return
|
||||
if (
|
||||
this.mindMap.associativeLine &&
|
||||
this.mindMap.associativeLine.showTextEdit
|
||||
) {
|
||||
return
|
||||
}
|
||||
this.isInSvg = false
|
||||
})
|
||||
window.addEventListener('keydown', this.onKeydown)
|
||||
@@ -89,12 +99,14 @@ export default class KeyCommand {
|
||||
// 根据事件目标判断是否响应快捷键事件
|
||||
defaultEnableCheck(e) {
|
||||
const target = e.target
|
||||
return (
|
||||
target === document.body ||
|
||||
target.classList.contains(CONSTANTS.EDIT_NODE_CLASS.SMM_NODE_EDIT_WRAP) ||
|
||||
target.classList.contains(CONSTANTS.EDIT_NODE_CLASS.RICH_TEXT_EDIT_WRAP) ||
|
||||
target.classList.contains(CONSTANTS.EDIT_NODE_CLASS.ASSOCIATIVE_LINE_TEXT_EDIT_WRAP)
|
||||
)
|
||||
if (target === document.body) return true
|
||||
for (let i = 0; i < this.mindMap.editNodeClassList.length; i++) {
|
||||
const cur = this.mindMap.editNodeClassList[i]
|
||||
if (target.classList.contains(cur)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 按键事件
|
||||
@@ -109,7 +121,12 @@ export default class KeyCommand {
|
||||
? customCheckEnableShortcut
|
||||
: this.defaultEnableCheck
|
||||
if (!checkFn(e)) return
|
||||
if (this.isPause || (enableShortcutOnlyWhenMouseInSvg && !this.isInSvg)) {
|
||||
if (
|
||||
this.isPause ||
|
||||
(enableShortcutOnlyWhenMouseInSvg &&
|
||||
!this.isStopCheckInSvg &&
|
||||
!this.isInSvg)
|
||||
) {
|
||||
return
|
||||
}
|
||||
Object.keys(this.shortcutMap).forEach(key => {
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
noneRichTextNodeLineHeight
|
||||
} from '../../constants/constant'
|
||||
|
||||
const SMM_NODE_EDIT_WRAP = 'smm-node-edit-wrap'
|
||||
|
||||
// 节点文字编辑类
|
||||
export default class TextEdit {
|
||||
// 构造函数
|
||||
@@ -34,6 +36,7 @@ export default class TextEdit {
|
||||
this.textNodePaddingX = 5
|
||||
this.textNodePaddingY = 3
|
||||
this.isNeedUpdateTextEditNode = false
|
||||
this.mindMap.addEditNodeClass(SMM_NODE_EDIT_WRAP)
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -193,6 +196,16 @@ export default class TextEdit {
|
||||
return this.showTextEdit
|
||||
}
|
||||
|
||||
// 设置文本编辑框是否处于显示状态
|
||||
setIsShowTextEdit(val) {
|
||||
this.showTextEdit = val
|
||||
if (val) {
|
||||
this.mindMap.keyCommand.stopCheckInSvg()
|
||||
} else {
|
||||
this.mindMap.keyCommand.recoveryCheckInSvg()
|
||||
}
|
||||
}
|
||||
|
||||
// 显示文本编辑框
|
||||
// isInserting:是否是刚创建的节点
|
||||
// isFromKeyDown:是否是在按键事件进入的编辑
|
||||
@@ -275,7 +288,7 @@ export default class TextEdit {
|
||||
this.mindMap.richText.showTextEdit = false
|
||||
} else {
|
||||
this.cacheEditingText = this.getEditText()
|
||||
this.showTextEdit = false
|
||||
this.setIsShowTextEdit(false)
|
||||
}
|
||||
this.show({
|
||||
node,
|
||||
@@ -299,9 +312,7 @@ export default class TextEdit {
|
||||
this.registerTmpShortcut()
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.classList.add(
|
||||
CONSTANTS.EDIT_NODE_CLASS.SMM_NODE_EDIT_WRAP
|
||||
)
|
||||
this.textEditNode.classList.add(SMM_NODE_EDIT_WRAP)
|
||||
this.textEditNode.style.cssText = `
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
@@ -383,7 +394,7 @@ export default class TextEdit {
|
||||
} else {
|
||||
this.textEditNode.style.lineHeight = 'normal'
|
||||
}
|
||||
this.showTextEdit = true
|
||||
this.setIsShowTextEdit(true)
|
||||
// 选中文本
|
||||
// if (!this.cacheEditingText) {
|
||||
// selectAllInput(this.textEditNode)
|
||||
@@ -477,7 +488,7 @@ export default class TextEdit {
|
||||
this.textEditNode.style.fontSize = 'inherit'
|
||||
this.textEditNode.style.fontWeight = 'normal'
|
||||
this.textEditNode.style.transform = 'translateY(0)'
|
||||
this.showTextEdit = false
|
||||
this.setIsShowTextEdit(false)
|
||||
this.mindMap.execCommand('SET_NODE_TEXT', currentNode, text)
|
||||
// if (currentNode.isGeneralization) {
|
||||
// // 概要节点
|
||||
|
||||
@@ -23,6 +23,8 @@ const styleProps = [
|
||||
'associativeLineTextFontFamily'
|
||||
]
|
||||
|
||||
const ASSOCIATIVE_LINE_TEXT_EDIT_WRAP = 'associative-line-text-edit-warp'
|
||||
|
||||
// 关联线插件
|
||||
class AssociativeLine {
|
||||
constructor(opt = {}) {
|
||||
@@ -62,9 +64,11 @@ class AssociativeLine {
|
||||
this[item] = associativeLineControlsMethods[item].bind(this)
|
||||
})
|
||||
// 关联线文字相关方法
|
||||
this.showTextEdit = false
|
||||
Object.keys(associativeLineTextMethods).forEach(item => {
|
||||
this[item] = associativeLineTextMethods[item].bind(this)
|
||||
})
|
||||
this.mindMap.addEditNodeClass(ASSOCIATIVE_LINE_TEXT_EDIT_WRAP)
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -744,11 +748,13 @@ class AssociativeLine {
|
||||
|
||||
// 插件被移除前做的事情
|
||||
beforePluginRemove() {
|
||||
this.mindMap.deleteEditNodeClass(ASSOCIATIVE_LINE_TEXT_EDIT_WRAP)
|
||||
this.unBindEvent()
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
this.mindMap.deleteEditNodeClass(ASSOCIATIVE_LINE_TEXT_EDIT_WRAP)
|
||||
this.unBindEvent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ let fontSizeList = new Array(100).fill(0).map((_, index) => {
|
||||
return index + 'px'
|
||||
})
|
||||
|
||||
const RICH_TEXT_EDIT_WRAP = 'ql-editor'
|
||||
|
||||
// 富文本编辑插件
|
||||
class RichText {
|
||||
constructor({ mindMap, pluginOpt }) {
|
||||
@@ -58,6 +60,7 @@ class RichText {
|
||||
this.isCompositing = false
|
||||
this.textNodePaddingX = 6
|
||||
this.textNodePaddingY = 4
|
||||
this.mindMap.addEditNodeClass(RICH_TEXT_EDIT_WRAP)
|
||||
this.initOpt()
|
||||
this.extendQuill()
|
||||
this.appendCss()
|
||||
@@ -113,7 +116,7 @@ class RichText {
|
||||
`
|
||||
)
|
||||
let cssText = `
|
||||
.${CONSTANTS.EDIT_NODE_CLASS.RICH_TEXT_EDIT_WRAP} {
|
||||
.${RICH_TEXT_EDIT_WRAP} {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
@@ -297,7 +300,7 @@ class RichText {
|
||||
}
|
||||
this.initQuillEditor()
|
||||
this.setQuillContainerMinHeight(originHeight)
|
||||
this.showTextEdit = true
|
||||
this.setIsShowTextEdit(true)
|
||||
// 如果是刚创建的节点,那么默认全选,否则普通激活不全选,除非selectTextOnEnterEditText配置为true
|
||||
// 在selectTextOnEnterEditText时,如果是在keydown事件进入的节点编辑,也不需要全选
|
||||
this.focus(
|
||||
@@ -331,9 +334,8 @@ class RichText {
|
||||
|
||||
// 设置quill编辑器容器的最小高度
|
||||
setQuillContainerMinHeight(minHeight) {
|
||||
document.querySelector(
|
||||
'.' + CONSTANTS.EDIT_NODE_CLASS.RICH_TEXT_EDIT_WRAP
|
||||
).style.minHeight = minHeight + 'px'
|
||||
document.querySelector('.' + RICH_TEXT_EDIT_WRAP).style.minHeight =
|
||||
minHeight + 'px'
|
||||
}
|
||||
|
||||
// 更新文本编辑框的大小和位置
|
||||
@@ -385,7 +387,7 @@ class RichText {
|
||||
const list = nodes && nodes.length > 0 ? nodes : [this.node]
|
||||
const node = this.node
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.showTextEdit = false
|
||||
this.setIsShowTextEdit(false)
|
||||
this.mindMap.emit('rich_text_selection_change', false)
|
||||
this.node = null
|
||||
this.isInserting = false
|
||||
@@ -614,6 +616,16 @@ class RichText {
|
||||
this.isCompositing = false
|
||||
}
|
||||
|
||||
// 设置文本编辑框是否处于显示状态
|
||||
setIsShowTextEdit(val) {
|
||||
this.showTextEdit = val
|
||||
if (val) {
|
||||
this.mindMap.keyCommand.stopCheckInSvg()
|
||||
} else {
|
||||
this.mindMap.keyCommand.recoveryCheckInSvg()
|
||||
}
|
||||
}
|
||||
|
||||
// 选中全部
|
||||
selectAll() {
|
||||
this.quill.setSelection(0, this.quill.getLength())
|
||||
@@ -853,12 +865,14 @@ class RichText {
|
||||
document.head.removeChild(this.styleEl)
|
||||
this.unbindEvent()
|
||||
this.mindMap.removeAppendCss('richText')
|
||||
this.mindMap.deleteEditNodeClass(RICH_TEXT_EDIT_WRAP)
|
||||
}
|
||||
|
||||
// 插件被卸载前做的事情
|
||||
beforePluginDestroy() {
|
||||
document.head.removeChild(this.styleEl)
|
||||
this.unbindEvent()
|
||||
this.mindMap.deleteEditNodeClass(RICH_TEXT_EDIT_WRAP)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
selectAllInput
|
||||
} from '../../utils/index'
|
||||
|
||||
const ASSOCIATIVE_LINE_TEXT_EDIT_WRAP = 'associative-line-text-edit-warp'
|
||||
|
||||
// 创建文字节点
|
||||
function createText(data) {
|
||||
let g = this.associativeLineDraw.group()
|
||||
@@ -43,7 +45,7 @@ function showEditTextBox(g) {
|
||||
// 输入框元素没有创建过,则先创建
|
||||
if (!this.textEditNode) {
|
||||
this.textEditNode = document.createElement('div')
|
||||
this.textEditNode.className = 'associative-line-text-edit-warp'
|
||||
this.textEditNode.className = ASSOCIATIVE_LINE_TEXT_EDIT_WRAP
|
||||
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 => {
|
||||
@@ -73,7 +75,7 @@ function showEditTextBox(g) {
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.updateTextEditBoxPos(g)
|
||||
this.showTextEdit = true
|
||||
this.setIsShowTextEdit(true)
|
||||
// 如果是默认文本要全选输入框
|
||||
if (text === '' || text === defaultAssociativeLineText) {
|
||||
selectAllInput(this.textEditNode)
|
||||
@@ -83,6 +85,16 @@ function showEditTextBox(g) {
|
||||
}
|
||||
}
|
||||
|
||||
// 设置文本编辑框是否处于显示状态
|
||||
function setIsShowTextEdit(val) {
|
||||
this.showTextEdit = val
|
||||
if (val) {
|
||||
this.mindMap.keyCommand.stopCheckInSvg()
|
||||
} else {
|
||||
this.mindMap.keyCommand.recoveryCheckInSvg()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文本编辑框元素
|
||||
function removeTextEditEl() {
|
||||
if (!this.textEditNode) return
|
||||
@@ -124,7 +136,7 @@ function hideEditTextBox() {
|
||||
})
|
||||
this.textEditNode.style.display = 'none'
|
||||
this.textEditNode.innerHTML = ''
|
||||
this.showTextEdit = false
|
||||
this.setIsShowTextEdit(false)
|
||||
this.renderText(str, path, text, node, toNode)
|
||||
this.mindMap.emit('hide_text_edit')
|
||||
}
|
||||
@@ -192,6 +204,7 @@ export default {
|
||||
styleText,
|
||||
onScale,
|
||||
showEditTextBox,
|
||||
setIsShowTextEdit,
|
||||
removeTextEditEl,
|
||||
hideEditTextBox,
|
||||
updateTextEditBoxPos,
|
||||
|
||||
Reference in New Issue
Block a user