201 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Text } from '@svgdotjs/svg.js'
import {
getStrWithBrFromHtml,
focusInput,
selectAllInput
} from '../../utils/index'
// 创建文字节点
function createText(data) {
let g = this.associativeLineDraw.group()
const setActive = () => {
if (
!this.activeLine ||
this.activeLine[3] !== data.node ||
this.activeLine[4] !== data.toNode
) {
this.setActiveLine({
...data,
text: g
})
}
}
g.click(e => {
e.stopPropagation()
setActive()
})
g.on('dblclick', e => {
e.stopPropagation()
setActive()
if (!this.activeLine) return
this.showEditTextBox(g)
})
return g
}
// 显示文本编辑框
function showEditTextBox(g) {
this.mindMap.emit('before_show_text_edit')
// 注册回车快捷键
this.mindMap.keyCommand.addShortcut('Enter', () => {
this.hideEditTextBox()
})
// 输入框元素没有创建过,则先创建
if (!this.textEditNode) {
this.textEditNode = document.createElement('div')
this.textEditNode.className = 'associative-line-text-edit-warp'
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()
})
this.textEditNode.addEventListener('click', e => {
e.stopPropagation()
})
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
targetNode.appendChild(this.textEditNode)
}
let [, , , node, toNode] = this.activeLine
let {
associativeLineTextFontSize,
associativeLineTextFontFamily,
associativeLineTextLineHeight
} = this.getStyleConfig(node, toNode)
let { defaultAssociativeLineText, nodeTextEditZIndex } = this.mindMap.opt
let scale = this.mindMap.view.scale
let text = this.getText(node, toNode)
let textLines = (text || defaultAssociativeLineText).split(/\n/gim)
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px'
this.textEditNode.style.lineHeight =
textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
this.textEditNode.style.zIndex = nodeTextEditZIndex
this.textEditNode.innerHTML = textLines.join('<br>')
this.textEditNode.style.display = 'block'
this.updateTextEditBoxPos(g)
this.showTextEdit = true
// 如果是默认文本要全选输入框
if (text === '' || text === defaultAssociativeLineText) {
selectAllInput(this.textEditNode)
} else {
// 否则聚焦即可
focusInput(this.textEditNode)
}
}
// 删除文本编辑框元素
function removeTextEditEl() {
if (!this.textEditNode) return
const targetNode = this.mindMap.opt.customInnerElsAppendTo || document.body
targetNode.removeChild(this.textEditNode)
}
// 处理画布缩放
function onScale() {
this.hideEditTextBox()
}
// 更新文本编辑框位置
function updateTextEditBoxPos(g) {
let rect = g.node.getBoundingClientRect()
if (this.textEditNode) {
this.textEditNode.style.minWidth = `${rect.width + 10}px`
this.textEditNode.style.minHeight = `${rect.height + 6}px`
this.textEditNode.style.left = `${rect.left}px`
this.textEditNode.style.top = `${rect.top}px`
}
}
// 隐藏文本编辑框
function hideEditTextBox() {
if (!this.showTextEdit) {
return
}
let [path, , text, node, toNode] = this.activeLine
let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
// 如果是默认文本,那么不保存
let isDefaultText = str === this.mindMap.opt.defaultAssociativeLineText
str = isDefaultText ? '' : str
this.mindMap.execCommand('SET_NODE_DATA', node, {
associativeLineText: {
...(node.getData('associativeLineText') || {}),
[toNode.getData('uid')]: str
}
})
this.textEditNode.style.display = 'none'
this.textEditNode.innerHTML = ''
this.showTextEdit = false
this.renderText(str, path, text, node, toNode)
this.mindMap.emit('hide_text_edit')
}
// 获取某根关联线的文字
function getText(node, toNode) {
let obj = node.getData('associativeLineText')
if (!obj) {
return ''
}
return obj[toNode.getData('uid')] || ''
}
// 渲染关联线文字
function renderText(str, path, text, node, toNode) {
if (!str) return
let { associativeLineTextFontSize, associativeLineTextLineHeight } =
this.getStyleConfig(node, toNode)
text.clear()
let textArr = str.replace(/\n$/g, '').split(/\n/gim)
textArr.forEach((item, index) => {
// 避免尾部的空行不占宽度,导致文本编辑框定位异常的问题
if (item === '') {
item = ''
}
let textNode = new Text().text(item)
textNode.y(
associativeLineTextFontSize * associativeLineTextLineHeight * index
)
this.styleText(textNode, node, toNode)
text.add(textNode)
})
updateTextPos(path, text)
}
// 给文本设置样式
function styleText(textNode, node, toNode) {
let {
associativeLineTextColor,
associativeLineTextFontSize,
associativeLineTextFontFamily
} = this.getStyleConfig(node, toNode)
textNode
.fill({
color: associativeLineTextColor
})
.css({
'font-family': associativeLineTextFontFamily,
'font-size': associativeLineTextFontSize + 'px'
})
}
// 更新关联线文字位置
function updateTextPos(path, text) {
let pathLength = path.length()
let centerPoint = path.pointAt(pathLength / 2)
let { width: textWidth, height: textHeight } = text.bbox()
text.x(centerPoint.x - textWidth / 2)
text.y(centerPoint.y - textHeight / 2)
}
export default {
getText,
createText,
styleText,
onScale,
showEditTextBox,
removeTextEditEl,
hideEditTextBox,
updateTextEditBoxPos,
renderText,
updateTextPos
}