mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-17 14:04:47 +08:00
Feat:新增开启节点文本编辑实时更新节点大小和位置的实例化选项
This commit is contained in:
@@ -238,6 +238,10 @@ export const defaultOpt = {
|
||||
padding: 100, // 超出画布四周指定范围内依旧渲染节点
|
||||
removeNodeWhenOutCanvas: true // 节点移除画布可视区域后从画布删除
|
||||
},
|
||||
// 如果节点文本为空,那么为了避免空白节点高度塌陷,会用该字段指定的文本测量一个高度
|
||||
emptyTextMeasureHeightText: 'abc123我和你',
|
||||
// 是否在进行节点文本编辑时实时更新节点大小和节点位置,开启后当节点数量比较多时可能会造成卡顿
|
||||
openRealtimeRenderOnNodeTextEdit: false,
|
||||
|
||||
// 【Select插件】
|
||||
// 多选节点时鼠标移动到边缘时的画布移动偏移量
|
||||
|
||||
@@ -147,6 +147,19 @@ class Render {
|
||||
})
|
||||
// 性能模式
|
||||
this.performanceMode()
|
||||
// 实时渲染当节点文本编辑时
|
||||
if (this.mindMap.opt.openRealtimeRenderOnNodeTextEdit) {
|
||||
this.mindMap.on('node_text_edit_change', ({ node, text }) => {
|
||||
node._textData = node.createTextNode(text)
|
||||
const { width, height } = node.getNodeRect()
|
||||
node.width = width
|
||||
node.height = height
|
||||
node.layout()
|
||||
this.mindMap.render(() => {
|
||||
this.textEdit.updateTextEditNode()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 性能模式,懒加载节点
|
||||
|
||||
@@ -25,6 +25,8 @@ export default class TextEdit {
|
||||
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
||||
this.cacheEditingText = ''
|
||||
this.hasBodyMousedown = false
|
||||
this.textNodePaddingX = 5
|
||||
this.textNodePaddingY = 3
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
@@ -214,7 +216,7 @@ 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; word-break: break-all;`
|
||||
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: ${this.textNodePaddingY}px ${this.textNodePaddingX}px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||
this.textEditNode.setAttribute('contenteditable', true)
|
||||
this.textEditNode.addEventListener('keyup', e => {
|
||||
e.stopPropagation()
|
||||
@@ -240,6 +242,13 @@ export default class TextEdit {
|
||||
handleInputPasteText(e)
|
||||
}
|
||||
})
|
||||
this.textEditNode.addEventListener('input', () => {
|
||||
this.mindMap.emit('node_text_edit_change', {
|
||||
node: this.currentNode,
|
||||
text: this.getEditText(),
|
||||
richText: false
|
||||
})
|
||||
})
|
||||
const targetNode =
|
||||
this.mindMap.opt.customInnerElsAppendTo || document.body
|
||||
targetNode.appendChild(this.textEditNode)
|
||||
@@ -256,8 +265,10 @@ export default class TextEdit {
|
||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
||||
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
||||
this.textEditNode.style.minWidth =
|
||||
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight =
|
||||
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
@@ -280,6 +291,24 @@ export default class TextEdit {
|
||||
this.cacheEditingText = ''
|
||||
}
|
||||
|
||||
// 更新文本编辑框的大小和位置
|
||||
updateTextEditNode() {
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.updateTextEditNode()
|
||||
return
|
||||
}
|
||||
if (!this.showTextEdit || !this.currentNode) {
|
||||
return
|
||||
}
|
||||
const rect = this.currentNode._textData.node.node.getBoundingClientRect()
|
||||
this.textEditNode.style.minWidth =
|
||||
rect.width + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight =
|
||||
rect.height + this.textNodePaddingY * 2 + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
}
|
||||
|
||||
// 删除文本编辑元素
|
||||
removeTextEditEl() {
|
||||
if (this.mindMap.richText) {
|
||||
|
||||
@@ -114,8 +114,10 @@ function createIconNode() {
|
||||
}
|
||||
|
||||
// 创建富文本节点
|
||||
function createRichTextNode() {
|
||||
const { textAutoWrapWidth } = this.mindMap.opt
|
||||
function createRichTextNode(specifyText) {
|
||||
let text =
|
||||
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||
const { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
||||
let g = new G()
|
||||
// 重新设置富文本节点内容
|
||||
let recoverText = false
|
||||
@@ -129,7 +131,6 @@ function createRichTextNode() {
|
||||
recoverText = true
|
||||
}
|
||||
}
|
||||
let text = this.getData('text')
|
||||
if (recoverText && !isUndef(text)) {
|
||||
// 判断节点内容是否是富文本
|
||||
let isRichText = checkIsRichText(text)
|
||||
@@ -153,7 +154,7 @@ function createRichTextNode() {
|
||||
text: text
|
||||
})
|
||||
}
|
||||
let html = `<div>${this.getData('text')}</div>`
|
||||
let html = `<div>${text}</div>`
|
||||
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
||||
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
|
||||
document.createElement('div')
|
||||
@@ -174,7 +175,7 @@ function createRichTextNode() {
|
||||
let { width, height } = el.getBoundingClientRect()
|
||||
// 如果文本为空,那么需要计算一个默认高度
|
||||
if (height <= 0) {
|
||||
div.innerHTML = '<p>abc123我和你</p>'
|
||||
div.innerHTML = `<p>${emptyTextMeasureHeightText}</p>`
|
||||
let elTmp = div.children[0]
|
||||
elTmp.classList.add('smm-richtext-node-wrap')
|
||||
height = elTmp.getBoundingClientRect().height
|
||||
@@ -199,10 +200,12 @@ function createRichTextNode() {
|
||||
}
|
||||
|
||||
// 创建文本节点
|
||||
function createTextNode() {
|
||||
function createTextNode(specifyText) {
|
||||
if (this.getData('richText')) {
|
||||
return this.createRichTextNode()
|
||||
return this.createRichTextNode(specifyText)
|
||||
}
|
||||
const text =
|
||||
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||
if (this.getData('resetRichText')) {
|
||||
delete this.nodeData.data.resetRichText
|
||||
}
|
||||
@@ -212,10 +215,11 @@ function createTextNode() {
|
||||
// 文本超长自动换行
|
||||
let textStyle = this.style.getTextFontStyle()
|
||||
let textArr = []
|
||||
if (!isUndef(this.getData('text'))) {
|
||||
textArr = String(this.getData('text')).split(/\n/gim)
|
||||
if (!isUndef(text)) {
|
||||
textArr = String(text).split(/\n/gim)
|
||||
}
|
||||
let maxWidth = this.mindMap.opt.textAutoWrapWidth
|
||||
const { textAutoWrapWidth: maxWidth, emptyTextMeasureHeightText } =
|
||||
this.mindMap.opt
|
||||
let isMultiLine = false
|
||||
textArr.forEach((item, index) => {
|
||||
let arr = item.split('')
|
||||
@@ -247,6 +251,13 @@ function createTextNode() {
|
||||
g.add(node)
|
||||
})
|
||||
let { width, height } = g.bbox()
|
||||
// 如果文本为空,那么需要计算一个默认高度
|
||||
if (height <= 0) {
|
||||
const tmpNode = new Text().text(emptyTextMeasureHeightText)
|
||||
this.style.text(tmpNode)
|
||||
const tmpBbox = tmpNode.bbox()
|
||||
height = tmpBbox.height
|
||||
}
|
||||
width = Math.min(Math.ceil(width), maxWidth)
|
||||
height = Math.ceil(height)
|
||||
g.attr('data-width', width)
|
||||
|
||||
@@ -57,6 +57,8 @@ class RichText {
|
||||
this.cacheEditingText = ''
|
||||
this.lostStyle = false
|
||||
this.isCompositing = false
|
||||
this.textNodePaddingX = 6
|
||||
this.textNodePaddingY = 4
|
||||
this.initOpt()
|
||||
this.extendQuill()
|
||||
this.appendCss()
|
||||
@@ -71,14 +73,17 @@ class RichText {
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
this.onCompositionStart = this.onCompositionStart.bind(this)
|
||||
this.onCompositionUpdate = this.onCompositionUpdate.bind(this)
|
||||
this.onCompositionEnd = this.onCompositionEnd.bind(this)
|
||||
window.addEventListener('compositionstart', this.onCompositionStart)
|
||||
window.addEventListener('compositionupdate', this.onCompositionUpdate)
|
||||
window.addEventListener('compositionend', this.onCompositionEnd)
|
||||
}
|
||||
|
||||
// 解绑事件
|
||||
unbindEvent() {
|
||||
window.removeEventListener('compositionstart', this.onCompositionStart)
|
||||
window.removeEventListener('compositionupdate', this.onCompositionUpdate)
|
||||
window.removeEventListener('compositionend', this.onCompositionEnd)
|
||||
}
|
||||
|
||||
@@ -198,8 +203,8 @@ class RichText {
|
||||
let scaleX = rect.width / originWidth
|
||||
let scaleY = rect.height / originHeight
|
||||
// 内边距
|
||||
let paddingX = 6
|
||||
let paddingY = 4
|
||||
let paddingX = this.textNodePaddingX
|
||||
let paddingY = this.textNodePaddingY
|
||||
if (richTextEditFakeInPlace) {
|
||||
let paddingValue = node.getPaddingVale()
|
||||
paddingX = paddingValue.paddingX
|
||||
@@ -287,6 +292,20 @@ class RichText {
|
||||
this.cacheEditingText = ''
|
||||
}
|
||||
|
||||
// 更新文本编辑框的大小和位置
|
||||
updateTextEditNode() {
|
||||
if (!this.node) return
|
||||
const rect = this.node._textData.node.node.getBoundingClientRect()
|
||||
const g = this.node._textData.node
|
||||
const originWidth = g.attr('data-width')
|
||||
const originHeight = g.attr('data-height')
|
||||
this.textEditNode.style.minWidth =
|
||||
originWidth + this.textNodePaddingX * 2 + 'px'
|
||||
this.textEditNode.style.minHeight = originHeight + 'px'
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
}
|
||||
|
||||
// 删除文本编辑框元素
|
||||
removeTextEditEl() {
|
||||
if (!this.textEditNode) return
|
||||
@@ -491,6 +510,11 @@ class RichText {
|
||||
this.setTextStyleIfNotRichText(this.node)
|
||||
this.lostStyle = false
|
||||
}
|
||||
this.mindMap.emit('node_text_edit_change', {
|
||||
node: this.node,
|
||||
text: this.getEditText(),
|
||||
richText: true
|
||||
})
|
||||
})
|
||||
// 拦截粘贴,只允许粘贴纯文本
|
||||
// this.quill.clipboard.addMatcher(Node.TEXT_NODE, node => {
|
||||
@@ -545,6 +569,16 @@ class RichText {
|
||||
this.isCompositing = true
|
||||
}
|
||||
|
||||
// 中文输入中
|
||||
onCompositionUpdate() {
|
||||
if (!this.showTextEdit || !this.node) return
|
||||
this.mindMap.emit('node_text_edit_change', {
|
||||
node: this.node,
|
||||
text: this.getEditText(),
|
||||
richText: true
|
||||
})
|
||||
}
|
||||
|
||||
// 中文输入结束
|
||||
onCompositionEnd() {
|
||||
if (!this.showTextEdit) {
|
||||
|
||||
Reference in New Issue
Block a user