BreakChange:重构富文本渲染逻辑
This commit is contained in:
parent
71f92c985f
commit
f8f126e8de
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "simple-mind-map",
|
"name": "simple-mind-map",
|
||||||
"version": "0.12.2",
|
"version": "0.13.0",
|
||||||
"description": "一个简单的web在线思维导图",
|
"description": "一个简单的web在线思维导图",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,7 +3,6 @@ export const CONSTANTS = {
|
|||||||
CHANGE_THEME: 'changeTheme',
|
CHANGE_THEME: 'changeTheme',
|
||||||
CHANGE_LAYOUT: 'changeLayout',
|
CHANGE_LAYOUT: 'changeLayout',
|
||||||
SET_DATA: 'setData',
|
SET_DATA: 'setData',
|
||||||
TRANSFORM_TO_NORMAL_NODE: 'transformAllNodesToNormalNode',
|
|
||||||
MODE: {
|
MODE: {
|
||||||
READONLY: 'readonly',
|
READONLY: 'readonly',
|
||||||
EDIT: 'edit'
|
EDIT: 'edit'
|
||||||
@ -157,7 +156,7 @@ export const nodeDataNoStylePropList = [
|
|||||||
'isActive',
|
'isActive',
|
||||||
'generalization',
|
'generalization',
|
||||||
'richText',
|
'richText',
|
||||||
'resetRichText',
|
'resetRichText',// 重新创建富文本内容,去掉原有样式
|
||||||
'uid',
|
'uid',
|
||||||
'activeStyle',
|
'activeStyle',
|
||||||
'associativeLineTargets',
|
'associativeLineTargets',
|
||||||
@ -174,7 +173,8 @@ export const nodeDataNoStylePropList = [
|
|||||||
'customTop',
|
'customTop',
|
||||||
'customTextWidth',
|
'customTextWidth',
|
||||||
'checkbox',
|
'checkbox',
|
||||||
'dir'
|
'dir',
|
||||||
|
'needUpdate'// 重新创建节点内容
|
||||||
]
|
]
|
||||||
|
|
||||||
// 错误类型
|
// 错误类型
|
||||||
@ -226,3 +226,13 @@ export const selfCloseTagList = [
|
|||||||
|
|
||||||
// 非富文本模式下的节点文本行高
|
// 非富文本模式下的节点文本行高
|
||||||
export const noneRichTextNodeLineHeight = 1.2
|
export const noneRichTextNodeLineHeight = 1.2
|
||||||
|
|
||||||
|
// 富文本支持的样式列表
|
||||||
|
export const richTextSupportStyleList = [
|
||||||
|
'fontFamily',
|
||||||
|
'fontSize',
|
||||||
|
'fontWeight',
|
||||||
|
'fontStyle',
|
||||||
|
'textDecoration',
|
||||||
|
'color'
|
||||||
|
]
|
||||||
|
|||||||
@ -30,7 +30,6 @@ import {
|
|||||||
createSmmFormatData,
|
createSmmFormatData,
|
||||||
checkSmmFormatData,
|
checkSmmFormatData,
|
||||||
checkIsNodeStyleDataKey,
|
checkIsNodeStyleDataKey,
|
||||||
removeRichTextStyes,
|
|
||||||
formatGetNodeGeneralization,
|
formatGetNodeGeneralization,
|
||||||
sortNodeList,
|
sortNodeList,
|
||||||
throttle,
|
throttle,
|
||||||
@ -547,13 +546,6 @@ class Render {
|
|||||||
if (this.reRender) {
|
if (this.reRender) {
|
||||||
this.reRender = false
|
this.reRender = false
|
||||||
}
|
}
|
||||||
// 触发一次保存,因为修改了渲染树的数据
|
|
||||||
if (
|
|
||||||
this.hasRichTextPlugin() &&
|
|
||||||
[CONSTANTS.CHANGE_THEME, CONSTANTS.SET_DATA].includes(source)
|
|
||||||
) {
|
|
||||||
this.mindMap.command.addHistory()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.mindMap.emit('node_tree_render_end')
|
this.mindMap.emit('node_tree_render_end')
|
||||||
})
|
})
|
||||||
@ -561,13 +553,14 @@ class Render {
|
|||||||
this.emitNodeActiveEvent()
|
this.emitNodeActiveEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给当前被收起来的节点数据添加文本复位标志
|
// 给当前被收起来的节点数据添加更新标志
|
||||||
resetUnExpandNodeStyle() {
|
resetUnExpandNodeStyle() {
|
||||||
if (!this.renderTree || !this.hasRichTextPlugin()) return
|
if (!this.renderTree) return
|
||||||
walk(this.renderTree, null, node => {
|
walk(this.renderTree, null, node => {
|
||||||
if (!node.data.expand) {
|
if (!node.data.expand) {
|
||||||
walk(node, null, node2 => {
|
walk(node, null, node2 => {
|
||||||
node2.data.resetRichText = true
|
// 主要是触发数据新旧对比,不一样则会重新创建节点
|
||||||
|
node2.data['needUpdate'] = true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -750,11 +743,10 @@ class Render {
|
|||||||
richText: isRichText,
|
richText: isRichText,
|
||||||
isActive: focusNewNode // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
|
isActive: focusNewNode // 如果同时对多个节点插入子节点,那么需要把新增的节点设为激活状态。如果不进入编辑状态,那么也需要手动设为激活状态
|
||||||
}
|
}
|
||||||
if (isRichText) params.resetRichText = isRichText
|
if (isRichText) params.resetRichText = true
|
||||||
// 动态指定的子节点数据也需要添加相关属性
|
// 动态指定的子节点数据也需要添加相关属性
|
||||||
appointChildren = addDataToAppointNodes(appointChildren, {
|
appointChildren = addDataToAppointNodes(appointChildren, params)
|
||||||
...params
|
const alreadyIsRichText = appointData && appointData.richText
|
||||||
})
|
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (node.isGeneralization || node.isRoot) {
|
if (node.isGeneralization || node.isRoot) {
|
||||||
return
|
return
|
||||||
@ -767,6 +759,10 @@ class Render {
|
|||||||
: defaultInsertBelowSecondLevelNodeText
|
: defaultInsertBelowSecondLevelNodeText
|
||||||
// 计算插入位置
|
// 计算插入位置
|
||||||
const index = getNodeDataIndex(node)
|
const index = getNodeDataIndex(node)
|
||||||
|
// 如果指定的数据就是富文本格式,那么不需要重新创建
|
||||||
|
if (alreadyIsRichText && params.resetRichText) {
|
||||||
|
delete params.resetRichText
|
||||||
|
}
|
||||||
const newNodeData = {
|
const newNodeData = {
|
||||||
inserting,
|
inserting,
|
||||||
data: {
|
data: {
|
||||||
@ -802,7 +798,7 @@ class Render {
|
|||||||
richText: isRichText,
|
richText: isRichText,
|
||||||
isActive: focusNewNode
|
isActive: focusNewNode
|
||||||
}
|
}
|
||||||
if (isRichText) params.resetRichText = isRichText
|
if (isRichText) params.resetRichText = true
|
||||||
nodeList = addDataToAppointNodes(nodeList, params)
|
nodeList = addDataToAppointNodes(nodeList, params)
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (node.isGeneralization || node.isRoot) {
|
if (node.isGeneralization || node.isRoot) {
|
||||||
@ -848,11 +844,10 @@ class Render {
|
|||||||
richText: isRichText,
|
richText: isRichText,
|
||||||
isActive: focusNewNode
|
isActive: focusNewNode
|
||||||
}
|
}
|
||||||
if (isRichText) params.resetRichText = isRichText
|
if (isRichText) params.resetRichText = true
|
||||||
// 动态指定的子节点数据也需要添加相关属性
|
// 动态指定的子节点数据也需要添加相关属性
|
||||||
appointChildren = addDataToAppointNodes(appointChildren, {
|
appointChildren = addDataToAppointNodes(appointChildren, params)
|
||||||
...params
|
const alreadyIsRichText = appointData && appointData.richText
|
||||||
})
|
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (node.isGeneralization) {
|
if (node.isGeneralization) {
|
||||||
return
|
return
|
||||||
@ -863,6 +858,10 @@ class Render {
|
|||||||
const text = node.isRoot
|
const text = node.isRoot
|
||||||
? defaultInsertSecondLevelNodeText
|
? defaultInsertSecondLevelNodeText
|
||||||
: defaultInsertBelowSecondLevelNodeText
|
: defaultInsertBelowSecondLevelNodeText
|
||||||
|
// 如果指定的数据就是富文本格式,那么不需要重新创建
|
||||||
|
if (alreadyIsRichText && params.resetRichText) {
|
||||||
|
delete params.resetRichText
|
||||||
|
}
|
||||||
const newNode = {
|
const newNode = {
|
||||||
inserting,
|
inserting,
|
||||||
data: {
|
data: {
|
||||||
@ -902,7 +901,7 @@ class Render {
|
|||||||
richText: isRichText,
|
richText: isRichText,
|
||||||
isActive: focusNewNode
|
isActive: focusNewNode
|
||||||
}
|
}
|
||||||
if (isRichText) params.resetRichText = isRichText
|
if (isRichText) params.resetRichText = true
|
||||||
childList = addDataToAppointNodes(childList, params)
|
childList = addDataToAppointNodes(childList, params)
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (node.isGeneralization) {
|
if (node.isGeneralization) {
|
||||||
@ -947,7 +946,8 @@ class Render {
|
|||||||
richText: isRichText,
|
richText: isRichText,
|
||||||
isActive: focusNewNode
|
isActive: focusNewNode
|
||||||
}
|
}
|
||||||
if (isRichText) params.resetRichText = isRichText
|
if (isRichText) params.resetRichText = true
|
||||||
|
const alreadyIsRichText = appointData && appointData.richText
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (node.isGeneralization || node.isRoot) {
|
if (node.isGeneralization || node.isRoot) {
|
||||||
return
|
return
|
||||||
@ -956,6 +956,10 @@ class Render {
|
|||||||
node.layerIndex === 1
|
node.layerIndex === 1
|
||||||
? defaultInsertSecondLevelNodeText
|
? defaultInsertSecondLevelNodeText
|
||||||
: defaultInsertBelowSecondLevelNodeText
|
: defaultInsertBelowSecondLevelNodeText
|
||||||
|
// 如果指定的数据就是富文本格式,那么不需要重新创建
|
||||||
|
if (alreadyIsRichText && params.resetRichText) {
|
||||||
|
delete params.resetRichText
|
||||||
|
}
|
||||||
const newNode = {
|
const newNode = {
|
||||||
inserting,
|
inserting,
|
||||||
data: {
|
data: {
|
||||||
@ -966,11 +970,6 @@ class Render {
|
|||||||
},
|
},
|
||||||
children: [node.nodeData]
|
children: [node.nodeData]
|
||||||
}
|
}
|
||||||
if (isRichText) {
|
|
||||||
node.setData({
|
|
||||||
resetRichText: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const parent = node.parent
|
const parent = node.parent
|
||||||
// 获取当前节点所在位置
|
// 获取当前节点所在位置
|
||||||
const index = getNodeDataIndex(node)
|
const index = getNodeDataIndex(node)
|
||||||
@ -1046,7 +1045,6 @@ class Render {
|
|||||||
const index = getNodeIndexInNodeList(node, parent.children)
|
const index = getNodeIndexInNodeList(node, parent.children)
|
||||||
const parentIndex = getNodeIndexInNodeList(parent, grandpa.children)
|
const parentIndex = getNodeIndexInNodeList(parent, grandpa.children)
|
||||||
// 节点数据
|
// 节点数据
|
||||||
this.checkNodeLayerChange(node, parent)
|
|
||||||
parent.nodeData.children.splice(index, 1)
|
parent.nodeData.children.splice(index, 1)
|
||||||
grandpa.nodeData.children.splice(parentIndex + 1, 0, node.nodeData)
|
grandpa.nodeData.children.splice(parentIndex + 1, 0, node.nodeData)
|
||||||
this.mindMap.render()
|
this.mindMap.render()
|
||||||
@ -1061,10 +1059,10 @@ class Render {
|
|||||||
delete nodeData[key]
|
delete nodeData[key]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 如果是富文本,那么还要处理富文本内容
|
// 如果是富文本,那么直接全部重新创建,因为有些样式是通过标签来渲染的
|
||||||
if (hasCustomStyles && this.hasRichTextPlugin()) {
|
if (this.hasRichTextPlugin()) {
|
||||||
|
hasCustomStyles = true
|
||||||
nodeData.resetRichText = true
|
nodeData.resetRichText = true
|
||||||
nodeData.text = removeRichTextStyes(nodeData.text)
|
|
||||||
}
|
}
|
||||||
return hasCustomStyles
|
return hasCustomStyles
|
||||||
}
|
}
|
||||||
@ -1302,7 +1300,6 @@ class Render {
|
|||||||
nodeList.reverse()
|
nodeList.reverse()
|
||||||
}
|
}
|
||||||
nodeList.forEach(item => {
|
nodeList.forEach(item => {
|
||||||
this.checkNodeLayerChange(item, exist)
|
|
||||||
// 移动节点
|
// 移动节点
|
||||||
let nodeParent = item.parent
|
let nodeParent = item.parent
|
||||||
let nodeBorthers = nodeParent.children
|
let nodeBorthers = nodeParent.children
|
||||||
@ -1329,25 +1326,6 @@ class Render {
|
|||||||
this.mindMap.render()
|
this.mindMap.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是富文本模式,那么某些层级变化需要更新样式
|
|
||||||
checkNodeLayerChange(node, toNode, toNodeIsParent = false) {
|
|
||||||
if (this.hasRichTextPlugin()) {
|
|
||||||
// 如果设置了自定义样式那么不需要更新
|
|
||||||
if (this.mindMap.richText.checkNodeHasCustomRichTextStyle(node)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const toIndex = toNodeIsParent ? toNode.layerIndex + 1 : toNode.layerIndex
|
|
||||||
let nodeLayerChanged =
|
|
||||||
(node.layerIndex === 1 && toIndex !== 1) ||
|
|
||||||
(node.layerIndex !== 1 && toIndex === 1)
|
|
||||||
if (nodeLayerChanged) {
|
|
||||||
node.setData({
|
|
||||||
resetRichText: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除节点
|
// 移除节点
|
||||||
removeNode(appointNodes = []) {
|
removeNode(appointNodes = []) {
|
||||||
appointNodes = formatDataToArray(appointNodes)
|
appointNodes = formatDataToArray(appointNodes)
|
||||||
@ -1531,7 +1509,6 @@ class Render {
|
|||||||
return !item.isRoot
|
return !item.isRoot
|
||||||
})
|
})
|
||||||
nodeList.forEach(item => {
|
nodeList.forEach(item => {
|
||||||
this.checkNodeLayerChange(item, toNode, true)
|
|
||||||
this.removeNodeFromActiveList(item)
|
this.removeNodeFromActiveList(item)
|
||||||
removeFromParentNodeData(item)
|
removeFromParentNodeData(item)
|
||||||
toNode.setData({
|
toNode.setData({
|
||||||
@ -1558,18 +1535,7 @@ class Render {
|
|||||||
node.nodeData.children.push(
|
node.nodeData.children.push(
|
||||||
...data.map(item => {
|
...data.map(item => {
|
||||||
const newData = simpleDeepClone(item)
|
const newData = simpleDeepClone(item)
|
||||||
createUidForAppointNodes([newData], true, node => {
|
createUidForAppointNodes([newData], true)
|
||||||
// 可能跨层级复制,那么富文本样式需要更新
|
|
||||||
if (this.hasRichTextPlugin()) {
|
|
||||||
// 如果设置了自定义样式那么不需要更新
|
|
||||||
if (
|
|
||||||
this.mindMap.richText.checkNodeHasCustomRichTextStyle(node.data)
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
node.data.resetRichText = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return newData
|
return newData
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -1582,13 +1548,6 @@ class Render {
|
|||||||
const data = {
|
const data = {
|
||||||
[prop]: value
|
[prop]: value
|
||||||
}
|
}
|
||||||
// 如果开启了富文本,则需要应用到富文本上
|
|
||||||
if (
|
|
||||||
this.hasRichTextPlugin() &&
|
|
||||||
this.mindMap.richText.isHasRichTextStyle(data)
|
|
||||||
) {
|
|
||||||
data.resetRichText = true
|
|
||||||
}
|
|
||||||
this.setNodeDataRender(node, data)
|
this.setNodeDataRender(node, data)
|
||||||
// 更新了连线的样式
|
// 更新了连线的样式
|
||||||
if (lineStyleProps.includes(prop)) {
|
if (lineStyleProps.includes(prop)) {
|
||||||
@ -1599,13 +1558,6 @@ class Render {
|
|||||||
// 设置节点多个样式
|
// 设置节点多个样式
|
||||||
setNodeStyles(node, style) {
|
setNodeStyles(node, style) {
|
||||||
const data = { ...style }
|
const data = { ...style }
|
||||||
// 如果开启了富文本,则需要应用到富文本上
|
|
||||||
if (
|
|
||||||
this.hasRichTextPlugin() &&
|
|
||||||
this.mindMap.richText.isHasRichTextStyle(data)
|
|
||||||
) {
|
|
||||||
data.resetRichText = true
|
|
||||||
}
|
|
||||||
this.setNodeDataRender(node, data)
|
this.setNodeDataRender(node, data)
|
||||||
// 更新了连线的样式
|
// 更新了连线的样式
|
||||||
let props = Object.keys(style)
|
let props = Object.keys(style)
|
||||||
@ -1826,6 +1778,7 @@ class Render {
|
|||||||
list.length > 1
|
list.length > 1
|
||||||
)
|
)
|
||||||
let needRender = false
|
let needRender = false
|
||||||
|
const alreadyIsRichText = data && data.richText
|
||||||
list.forEach(item => {
|
list.forEach(item => {
|
||||||
const newData = {
|
const newData = {
|
||||||
inserting,
|
inserting,
|
||||||
@ -1837,7 +1790,7 @@ class Render {
|
|||||||
richText: isRichText,
|
richText: isRichText,
|
||||||
isActive: focusNewNode
|
isActive: focusNewNode
|
||||||
}
|
}
|
||||||
if (isRichText) newData.resetRichText = isRichText
|
if (isRichText && !alreadyIsRichText) newData.resetRichText = isRichText
|
||||||
let generalization = item.node.getData('generalization')
|
let generalization = item.node.getData('generalization')
|
||||||
generalization = generalization
|
generalization = generalization
|
||||||
? Array.isArray(generalization)
|
? Array.isArray(generalization)
|
||||||
|
|||||||
@ -1,19 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
resizeImgSize,
|
resizeImgSize,
|
||||||
removeHtmlStyle,
|
removeRichTextStyes,
|
||||||
addHtmlStyle,
|
|
||||||
checkIsRichText,
|
checkIsRichText,
|
||||||
isUndef,
|
isUndef,
|
||||||
createForeignObjectNode,
|
createForeignObjectNode,
|
||||||
addXmlns,
|
addXmlns,
|
||||||
generateColorByContent
|
generateColorByContent,
|
||||||
|
camelCaseToHyphen,
|
||||||
|
getNodeRichTextStyles
|
||||||
} from '../../../utils'
|
} from '../../../utils'
|
||||||
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
||||||
import iconsSvg from '../../../svg/icons'
|
import iconsSvg from '../../../svg/icons'
|
||||||
import {
|
import { noneRichTextNodeLineHeight } from '../../../constants/constant'
|
||||||
CONSTANTS,
|
|
||||||
noneRichTextNodeLineHeight
|
|
||||||
} from '../../../constants/constant'
|
|
||||||
|
|
||||||
// 测量svg文本宽高
|
// 测量svg文本宽高
|
||||||
const measureText = (text, style) => {
|
const measureText = (text, style) => {
|
||||||
@ -124,20 +122,6 @@ function createIconNode() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试给html指定标签添加内联样式
|
|
||||||
function tryAddHtmlStyle(text, style) {
|
|
||||||
const tagList = ['span', 'strong', 's', 'em', 'u']
|
|
||||||
// let _text = text
|
|
||||||
// for (let i = 0; i < tagList.length; i++) {
|
|
||||||
// text = addHtmlStyle(text, tagList[i], style)
|
|
||||||
// if (text !== _text) {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return text
|
|
||||||
return addHtmlStyle(text, tagList, style)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建富文本节点
|
// 创建富文本节点
|
||||||
function createRichTextNode(specifyText) {
|
function createRichTextNode(specifyText) {
|
||||||
const hasCustomWidth = this.hasCustomWidth()
|
const hasCustomWidth = this.hasCustomWidth()
|
||||||
@ -145,40 +129,32 @@ function createRichTextNode(specifyText) {
|
|||||||
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
typeof specifyText === 'string' ? specifyText : this.getData('text')
|
||||||
let { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
let { textAutoWrapWidth, emptyTextMeasureHeightText } = this.mindMap.opt
|
||||||
textAutoWrapWidth = hasCustomWidth ? this.customTextWidth : textAutoWrapWidth
|
textAutoWrapWidth = hasCustomWidth ? this.customTextWidth : textAutoWrapWidth
|
||||||
let g = new G()
|
const g = new G()
|
||||||
// 重新设置富文本节点内容
|
// 创建富文本结构,或复位富文本样式
|
||||||
let recoverText = false
|
let recoverText = false
|
||||||
if (this.getData('resetRichText')) {
|
if (this.getData('resetRichText')) {
|
||||||
delete this.nodeData.data.resetRichText
|
delete this.nodeData.data.resetRichText
|
||||||
recoverText = true
|
recoverText = true
|
||||||
}
|
}
|
||||||
if ([CONSTANTS.CHANGE_THEME].includes(this.mindMap.renderer.renderSource)) {
|
|
||||||
// 如果自定义过样式则不允许覆盖
|
|
||||||
// if (!this.hasCustomStyle() ) {
|
|
||||||
recoverText = true
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
if (recoverText && !isUndef(text)) {
|
if (recoverText && !isUndef(text)) {
|
||||||
// 判断节点内容是否是富文本
|
if (checkIsRichText(text)) {
|
||||||
const isRichText = checkIsRichText(text)
|
// 如果是富文本那么移除内联样式
|
||||||
// 获取自定义样式
|
text = removeRichTextStyes(text)
|
||||||
const customStyle = this.style.getCustomStyle()
|
|
||||||
// 样式字符串
|
|
||||||
const style = this.style.createStyleText(customStyle)
|
|
||||||
if (isRichText) {
|
|
||||||
// 如果是富文本那么线移除内联样式
|
|
||||||
text = removeHtmlStyle(text)
|
|
||||||
// 再添加新的内联样式
|
|
||||||
text = this.tryAddHtmlStyle(text, style)
|
|
||||||
} else {
|
} else {
|
||||||
// 非富文本
|
// 非富文本则改为富文本结构
|
||||||
text = `<p><span style="${style}">${text}</span></p>`
|
text = `<p>${text}</p>`
|
||||||
}
|
}
|
||||||
this.setData({
|
this.setData({
|
||||||
text: text
|
text
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
let html = `<div>${text}</div>`
|
// 节点的富文本样式数据
|
||||||
|
const nodeTextStyleList = []
|
||||||
|
const nodeRichTextStyles = getNodeRichTextStyles(this)
|
||||||
|
Object.keys(nodeRichTextStyles).forEach(prop => {
|
||||||
|
nodeTextStyleList.push([prop, nodeRichTextStyles[prop]])
|
||||||
|
})
|
||||||
|
// 测量文本大小
|
||||||
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
if (!this.mindMap.commonCaches.measureRichtextNodeTextSizeEl) {
|
||||||
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
|
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl =
|
||||||
document.createElement('div')
|
document.createElement('div')
|
||||||
@ -190,9 +166,15 @@ function createRichTextNode(specifyText) {
|
|||||||
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl
|
this.mindMap.commonCaches.measureRichtextNodeTextSizeEl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let div = this.mindMap.commonCaches.measureRichtextNodeTextSizeEl
|
const div = this.mindMap.commonCaches.measureRichtextNodeTextSizeEl
|
||||||
|
// 应用节点的文本样式
|
||||||
|
nodeTextStyleList.forEach(([prop, value]) => {
|
||||||
|
div.style[prop] = value
|
||||||
|
})
|
||||||
|
div.style.lineHeight = 1.2
|
||||||
|
const html = `<div>${text}</div>`
|
||||||
div.innerHTML = html
|
div.innerHTML = html
|
||||||
let el = div.children[0]
|
const el = div.children[0]
|
||||||
el.classList.add('smm-richtext-node-wrap')
|
el.classList.add('smm-richtext-node-wrap')
|
||||||
addXmlns(el)
|
addXmlns(el)
|
||||||
el.style.maxWidth = textAutoWrapWidth + 'px'
|
el.style.maxWidth = textAutoWrapWidth + 'px'
|
||||||
@ -219,6 +201,15 @@ function createRichTextNode(specifyText) {
|
|||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
})
|
})
|
||||||
|
// 应用节点文本样式
|
||||||
|
// 进入文本编辑时,这个样式也会同样添加到文本编辑框的元素上
|
||||||
|
const foreignObjectStyle = {
|
||||||
|
'line-height': 1.2
|
||||||
|
}
|
||||||
|
nodeTextStyleList.forEach(([prop, value]) => {
|
||||||
|
foreignObjectStyle[camelCaseToHyphen(prop)] = value
|
||||||
|
})
|
||||||
|
foreignObject.css(foreignObjectStyle)
|
||||||
g.add(foreignObject)
|
g.add(foreignObject)
|
||||||
return {
|
return {
|
||||||
node: g,
|
node: g,
|
||||||
@ -230,6 +221,10 @@ function createRichTextNode(specifyText) {
|
|||||||
|
|
||||||
// 创建文本节点
|
// 创建文本节点
|
||||||
function createTextNode(specifyText) {
|
function createTextNode(specifyText) {
|
||||||
|
if (this.getData('needUpdate')) {
|
||||||
|
delete this.nodeData.data.needUpdate
|
||||||
|
}
|
||||||
|
// 如果是富文本内容,那么转给富文本函数
|
||||||
if (this.getData('richText')) {
|
if (this.getData('richText')) {
|
||||||
return this.createRichTextNode(specifyText)
|
return this.createRichTextNode(specifyText)
|
||||||
}
|
}
|
||||||
@ -556,7 +551,6 @@ export default {
|
|||||||
createImgNode,
|
createImgNode,
|
||||||
getImgShowSize,
|
getImgShowSize,
|
||||||
createIconNode,
|
createIconNode,
|
||||||
tryAddHtmlStyle,
|
|
||||||
createRichTextNode,
|
createRichTextNode,
|
||||||
createTextNode,
|
createTextNode,
|
||||||
createHyperlinkNode,
|
createHyperlinkNode,
|
||||||
|
|||||||
@ -49,10 +49,7 @@ class Base {
|
|||||||
|
|
||||||
// 检查当前来源是否需要重新计算节点大小
|
// 检查当前来源是否需要重新计算节点大小
|
||||||
checkIsNeedResizeSources() {
|
checkIsNeedResizeSources() {
|
||||||
return [
|
return [CONSTANTS.CHANGE_THEME].includes(this.renderer.renderSource)
|
||||||
CONSTANTS.CHANGE_THEME,
|
|
||||||
CONSTANTS.TRANSFORM_TO_NORMAL_NODE
|
|
||||||
].includes(this.renderer.renderSource)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 层级类型改变
|
// 层级类型改变
|
||||||
@ -140,6 +137,7 @@ class Base {
|
|||||||
isNodeDataChange ||
|
isNodeDataChange ||
|
||||||
isLayerTypeChange ||
|
isLayerTypeChange ||
|
||||||
newNode.getData('resetRichText') ||
|
newNode.getData('resetRichText') ||
|
||||||
|
newNode.getData('needUpdate') ||
|
||||||
isNodeInnerPrefixChange
|
isNodeInnerPrefixChange
|
||||||
) {
|
) {
|
||||||
newNode.getSize()
|
newNode.getSize()
|
||||||
@ -193,6 +191,7 @@ class Base {
|
|||||||
isNodeDataChange ||
|
isNodeDataChange ||
|
||||||
isLayerTypeChange ||
|
isLayerTypeChange ||
|
||||||
newNode.getData('resetRichText') ||
|
newNode.getData('resetRichText') ||
|
||||||
|
newNode.getData('needUpdate') ||
|
||||||
isNodeInnerPrefixChange
|
isNodeInnerPrefixChange
|
||||||
) {
|
) {
|
||||||
newNode.getSize()
|
newNode.getSize()
|
||||||
|
|||||||
@ -6,11 +6,11 @@ import {
|
|||||||
getTextFromHtml,
|
getTextFromHtml,
|
||||||
isUndef,
|
isUndef,
|
||||||
checkSmmFormatData,
|
checkSmmFormatData,
|
||||||
removeHtmlNodeByClass,
|
|
||||||
formatGetNodeGeneralization,
|
formatGetNodeGeneralization,
|
||||||
nodeRichTextToTextWithWrap
|
nodeRichTextToTextWithWrap,
|
||||||
|
getNodeRichTextStyles
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { CONSTANTS } from '../constants/constant'
|
import { CONSTANTS, richTextSupportStyleList } from '../constants/constant'
|
||||||
import MindMapNode from '../core/render/node/MindMapNode'
|
import MindMapNode from '../core/render/node/MindMapNode'
|
||||||
import { Scope } from 'parchment'
|
import { Scope } from 'parchment'
|
||||||
|
|
||||||
@ -53,27 +53,15 @@ class RichText {
|
|||||||
this.isInserting = false
|
this.isInserting = false
|
||||||
this.styleEl = null
|
this.styleEl = null
|
||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
this.lostStyle = false
|
|
||||||
this.isCompositing = false
|
this.isCompositing = false
|
||||||
this.textNodePaddingX = 6
|
this.textNodePaddingX = 6
|
||||||
this.textNodePaddingY = 4
|
this.textNodePaddingY = 4
|
||||||
this.supportStyleProps = [
|
|
||||||
'fontFamily',
|
|
||||||
'fontSize',
|
|
||||||
'fontWeight',
|
|
||||||
'fontStyle',
|
|
||||||
'textDecoration',
|
|
||||||
'color'
|
|
||||||
]
|
|
||||||
this.initOpt()
|
this.initOpt()
|
||||||
this.extendQuill()
|
this.extendQuill()
|
||||||
this.appendCss()
|
this.appendCss()
|
||||||
this.bindEvent()
|
this.bindEvent()
|
||||||
|
|
||||||
// 处理数据,转成富文本格式
|
this.handleDataToRichTextOnInit()
|
||||||
if (this.mindMap.opt.data) {
|
|
||||||
this.mindMap.opt.data = this.handleSetData(this.mindMap.opt.data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定事件
|
// 绑定事件
|
||||||
@ -107,10 +95,6 @@ class RichText {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smm-richtext-node-wrap p {
|
|
||||||
font-family: auto;
|
|
||||||
}
|
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
let cssText = `
|
let cssText = `
|
||||||
@ -118,7 +102,7 @@ class RichText {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: auto;
|
height: auto;
|
||||||
line-height: normal;
|
line-height: 1.2;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +114,6 @@ class RichText {
|
|||||||
.ql-container.ql-snow {
|
.ql-container.ql-snow {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smm-richtext-node-edit-wrap p {
|
|
||||||
font-family: auto;
|
|
||||||
}
|
|
||||||
`
|
`
|
||||||
this.styleEl = document.createElement('style')
|
this.styleEl = document.createElement('style')
|
||||||
this.styleEl.type = 'text/css'
|
this.styleEl.type = 'text/css'
|
||||||
@ -238,6 +218,7 @@ class RichText {
|
|||||||
outline: none;
|
outline: none;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
padding: ${paddingY}px ${paddingX}px;
|
padding: ${paddingY}px ${paddingX}px;
|
||||||
|
line-height: 1.2;
|
||||||
`
|
`
|
||||||
this.textEditNode.addEventListener('click', e => {
|
this.textEditNode.addEventListener('click', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@ -253,6 +234,7 @@ class RichText {
|
|||||||
const targetNode = customInnerElsAppendTo || document.body
|
const targetNode = customInnerElsAppendTo || document.body
|
||||||
targetNode.appendChild(this.textEditNode)
|
targetNode.appendChild(this.textEditNode)
|
||||||
}
|
}
|
||||||
|
this.addNodeTextStyleToTextEditNode(node)
|
||||||
this.textEditNode.style.marginLeft = `-${paddingX * scaleX}px`
|
this.textEditNode.style.marginLeft = `-${paddingX * scaleX}px`
|
||||||
this.textEditNode.style.marginTop = `-${paddingY * scaleY}px`
|
this.textEditNode.style.marginTop = `-${paddingY * scaleY}px`
|
||||||
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
this.textEditNode.style.zIndex = nodeTextEditZIndex
|
||||||
@ -277,13 +259,8 @@ class RichText {
|
|||||||
const isEmptyText = isUndef(nodeText)
|
const isEmptyText = isUndef(nodeText)
|
||||||
// 是否是非空的非富文本
|
// 是否是非空的非富文本
|
||||||
const noneEmptyNoneRichText = !node.getData('richText') && !isEmptyText
|
const noneEmptyNoneRichText = !node.getData('richText') && !isEmptyText
|
||||||
// 如果是空文本,那么设置为丢失样式状态,否则输入不会带上样式
|
|
||||||
if (isEmptyText) {
|
|
||||||
this.lostStyle = true
|
|
||||||
}
|
|
||||||
if (isFromKeyDown && autoEmptyTextWhenKeydownEnterEdit) {
|
if (isFromKeyDown && autoEmptyTextWhenKeydownEnterEdit) {
|
||||||
this.textEditNode.innerHTML = ''
|
this.textEditNode.innerHTML = ''
|
||||||
this.lostStyle = true
|
|
||||||
} else if (noneEmptyNoneRichText) {
|
} else if (noneEmptyNoneRichText) {
|
||||||
// 还不是富文本
|
// 还不是富文本
|
||||||
let text = String(nodeText).split(/\n/gim).join('<br>')
|
let text = String(nodeText).split(/\n/gim).join('<br>')
|
||||||
@ -303,10 +280,6 @@ class RichText {
|
|||||||
this.focus(
|
this.focus(
|
||||||
isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null
|
isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null
|
||||||
)
|
)
|
||||||
if (noneEmptyNoneRichText) {
|
|
||||||
// 如果是非富文本的情况,需要手动应用文本样式
|
|
||||||
this.setTextStyleIfNotRichText(node)
|
|
||||||
}
|
|
||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,6 +298,14 @@ class RichText {
|
|||||||
: '0 0 20px rgba(0,0,0,.5)'
|
: '0 0 20px rgba(0,0,0,.5)'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将指定节点的文本样式添加到编辑框元素上
|
||||||
|
addNodeTextStyleToTextEditNode(node) {
|
||||||
|
const style = getNodeRichTextStyles(node)
|
||||||
|
Object.keys(style).forEach(prop => {
|
||||||
|
this.textEditNode.style[prop] = style[prop]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 更新文本编辑框的大小和位置
|
// 更新文本编辑框的大小和位置
|
||||||
updateTextEditNode() {
|
updateTextEditNode() {
|
||||||
if (!this.node) return
|
if (!this.node) return
|
||||||
@ -346,20 +327,6 @@ class RichText {
|
|||||||
targetNode.removeChild(this.textEditNode)
|
targetNode.removeChild(this.textEditNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是非富文本的情况,需要手动应用文本样式
|
|
||||||
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.pureFormatAllText(style)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前正在编辑的内容
|
// 获取当前正在编辑的内容
|
||||||
getEditText() {
|
getEditText() {
|
||||||
// https://github.com/slab/quill/issues/4509
|
// https://github.com/slab/quill/issues/4509
|
||||||
@ -374,18 +341,6 @@ class RichText {
|
|||||||
// return html.replace(/<p><br><\/p>$/, '')
|
// return html.replace(/<p><br><\/p>$/, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给html字符串中的节点样式按样式名首字母排序
|
|
||||||
sortHtmlNodeStyles(html) {
|
|
||||||
return html.replace(/(<[^<>]+\s+style=")([^"]+)("\s*>)/g, (_, a, b, c) => {
|
|
||||||
let arr = b.match(/[^:]+:[^:]+;/g) || []
|
|
||||||
arr = arr.map(item => {
|
|
||||||
return item.trim()
|
|
||||||
})
|
|
||||||
arr.sort()
|
|
||||||
return a + arr.join('') + c
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏文本编辑控件,即完成编辑
|
// 隐藏文本编辑控件,即完成编辑
|
||||||
hideEditText(nodes) {
|
hideEditText(nodes) {
|
||||||
if (!this.showTextEdit) {
|
if (!this.showTextEdit) {
|
||||||
@ -395,8 +350,7 @@ class RichText {
|
|||||||
if (typeof beforeHideRichTextEdit === 'function') {
|
if (typeof beforeHideRichTextEdit === 'function') {
|
||||||
beforeHideRichTextEdit(this)
|
beforeHideRichTextEdit(this)
|
||||||
}
|
}
|
||||||
let html = this.getEditText()
|
const html = this.getEditText()
|
||||||
html = this.sortHtmlNodeStyles(html)
|
|
||||||
const list = nodes && nodes.length > 0 ? nodes : [this.node]
|
const list = nodes && nodes.length > 0 ? nodes : [this.node]
|
||||||
const node = this.node
|
const node = this.node
|
||||||
this.textEditNode.style.display = 'none'
|
this.textEditNode.style.display = 'none'
|
||||||
@ -536,18 +490,6 @@ class RichText {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.quill.on('text-change', () => {
|
this.quill.on('text-change', () => {
|
||||||
let contents = this.quill.getContents()
|
|
||||||
let len = contents.ops.length
|
|
||||||
// 如果编辑过程中删除所有字符,那么会丢失主题的样式
|
|
||||||
if (len <= 0 || (len === 1 && contents.ops[0].insert === '\n')) {
|
|
||||||
this.lostStyle = true
|
|
||||||
// 需要删除节点的样式数据
|
|
||||||
this.syncFormatToNodeConfig(null, true)
|
|
||||||
} else if (this.lostStyle && !this.isCompositing) {
|
|
||||||
// 如果处于样式丢失状态,那么需要进行格式化加回样式
|
|
||||||
this.setTextStyleIfNotRichText(this.node)
|
|
||||||
this.lostStyle = false
|
|
||||||
}
|
|
||||||
this.mindMap.emit('node_text_edit_change', {
|
this.mindMap.emit('node_text_edit_change', {
|
||||||
node: this.node,
|
node: this.node,
|
||||||
text: this.getEditText(),
|
text: this.getEditText(),
|
||||||
@ -638,10 +580,6 @@ class RichText {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.isCompositing = false
|
this.isCompositing = false
|
||||||
if (!this.lostStyle) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.setTextStyleIfNotRichText(this.node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选中全部
|
// 选中全部
|
||||||
@ -651,16 +589,15 @@ class RichText {
|
|||||||
|
|
||||||
// 聚焦
|
// 聚焦
|
||||||
focus(start) {
|
focus(start) {
|
||||||
let len = this.quill.getLength()
|
const len = this.quill.getLength()
|
||||||
this.quill.setSelection(typeof start === 'number' ? start : len, len)
|
this.quill.setSelection(typeof start === 'number' ? start : len, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化当前选中的文本
|
// 格式化当前选中的文本
|
||||||
formatText(config = {}, clear = false, pure = false) {
|
formatText(config = {}, clear = false) {
|
||||||
if (!this.range && !this.lastRange) return
|
if (!this.range && !this.lastRange) return
|
||||||
if (!pure) this.syncFormatToNodeConfig(config, clear)
|
const rangeLost = !this.range
|
||||||
let rangeLost = !this.range
|
const range = rangeLost ? this.lastRange : this.range
|
||||||
let range = rangeLost ? this.lastRange : this.range
|
|
||||||
clear
|
clear
|
||||||
? this.quill.removeFormat(range.index, range.length)
|
? this.quill.removeFormat(range.index, range.length)
|
||||||
: this.quill.formatText(range.index, range.length, config)
|
: this.quill.formatText(range.index, range.length, config)
|
||||||
@ -671,56 +608,25 @@ class RichText {
|
|||||||
|
|
||||||
// 清除当前选中文本的样式
|
// 清除当前选中文本的样式
|
||||||
removeFormat() {
|
removeFormat() {
|
||||||
// 先移除全部样式
|
|
||||||
this.formatText({}, true)
|
this.formatText({}, true)
|
||||||
// 再将样式恢复为当前主题改节点的默认样式
|
|
||||||
const style = {}
|
|
||||||
if (this.node) {
|
|
||||||
this.supportStyleProps.forEach(key => {
|
|
||||||
style[key] = this.node.style.merge(key)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const config = this.normalStyleToRichTextStyle(style)
|
|
||||||
this.formatText(config, false, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化指定范围的文本
|
// 格式化指定范围的文本
|
||||||
formatRangeText(range, config = {}) {
|
formatRangeText(range, config = {}) {
|
||||||
if (!range) return
|
if (!range) return
|
||||||
this.syncFormatToNodeConfig(config)
|
|
||||||
this.quill.formatText(range.index, range.length, config)
|
this.quill.formatText(range.index, range.length, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化所有文本
|
// 格式化所有文本
|
||||||
formatAllText(config = {}) {
|
formatAllText(config = {}) {
|
||||||
this.syncFormatToNodeConfig(config)
|
|
||||||
this.pureFormatAllText(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 纯粹的格式化所有文本
|
|
||||||
pureFormatAllText(config = {}) {
|
|
||||||
this.quill.formatText(0, this.quill.getLength(), config)
|
this.quill.formatText(0, this.quill.getLength(), config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步格式化到节点样式配置
|
|
||||||
syncFormatToNodeConfig(config, clear) {
|
|
||||||
if (!this.node) return
|
|
||||||
if (clear) {
|
|
||||||
// 清除文本样式
|
|
||||||
this.supportStyleProps.forEach(prop => {
|
|
||||||
delete this.node.nodeData.data[prop]
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let data = this.richTextStyleToNormalStyle(config)
|
|
||||||
this.mindMap.execCommand('SET_NODE_DATA', this.node, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将普通节点样式对象转换成富文本样式对象
|
// 将普通节点样式对象转换成富文本样式对象
|
||||||
normalStyleToRichTextStyle(style) {
|
normalStyleToRichTextStyle(style) {
|
||||||
let config = {}
|
const config = {}
|
||||||
Object.keys(style).forEach(prop => {
|
Object.keys(style).forEach(prop => {
|
||||||
let value = style[prop]
|
const value = style[prop]
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
case 'fontFamily':
|
case 'fontFamily':
|
||||||
config.font = value
|
config.font = value
|
||||||
@ -750,9 +656,9 @@ class RichText {
|
|||||||
|
|
||||||
// 将富文本样式对象转换成普通节点样式对象
|
// 将富文本样式对象转换成普通节点样式对象
|
||||||
richTextStyleToNormalStyle(config) {
|
richTextStyleToNormalStyle(config) {
|
||||||
let data = {}
|
const data = {}
|
||||||
Object.keys(config).forEach(prop => {
|
Object.keys(config).forEach(prop => {
|
||||||
let value = config[prop]
|
const value = config[prop]
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
case 'font':
|
case 'font':
|
||||||
data.fontFamily = value
|
data.fontFamily = value
|
||||||
@ -787,39 +693,50 @@ class RichText {
|
|||||||
const keys = Object.keys(obj)
|
const keys = Object.keys(obj)
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const key = keys[i]
|
const key = keys[i]
|
||||||
if (this.supportStyleProps.includes(key)) {
|
if (richTextSupportStyleList.includes(key)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给未激活的节点设置富文本样式
|
|
||||||
setNotActiveNodeStyle(node, style) {
|
|
||||||
const config = this.normalStyleToRichTextStyle(style)
|
|
||||||
if (Object.keys(config).length > 0) {
|
|
||||||
this.showEditText({ node })
|
|
||||||
this.formatAllText(config)
|
|
||||||
this.hideEditText([node])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查指定节点是否存在自定义的富文本样式
|
// 检查指定节点是否存在自定义的富文本样式
|
||||||
checkNodeHasCustomRichTextStyle(node) {
|
checkNodeHasCustomRichTextStyle(node) {
|
||||||
const nodeData = node instanceof MindMapNode ? node.getData() : node
|
const nodeData = node instanceof MindMapNode ? node.getData() : node
|
||||||
for (let i = 0; i < this.supportStyleProps.length; i++) {
|
for (let i = 0; i < richTextSupportStyleList.length; i++) {
|
||||||
if (nodeData[this.supportStyleProps[i]] !== undefined) {
|
if (nodeData[richTextSupportStyleList[i]] !== undefined) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 转换数据后的渲染操作
|
||||||
|
afterHandleData() {
|
||||||
|
// 清空历史数据,并且触发数据变化
|
||||||
|
this.mindMap.command.clearHistory()
|
||||||
|
this.mindMap.command.addHistory()
|
||||||
|
this.mindMap.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插件实例化时处理思维导图数据,转换为富文本数据
|
||||||
|
handleDataToRichTextOnInit() {
|
||||||
|
// 处理数据,转成富文本格式
|
||||||
|
if (this.mindMap.renderer.renderTree) {
|
||||||
|
// 如果已经存在渲染树了,那么直接更新渲染树,并且触发重新渲染
|
||||||
|
this.handleSetData(this.mindMap.renderer.renderTree)
|
||||||
|
this.afterHandleData()
|
||||||
|
} else if (this.mindMap.opt.data) {
|
||||||
|
this.handleSetData(this.mindMap.opt.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 将所有节点转换成非富文本节点
|
// 将所有节点转换成非富文本节点
|
||||||
transformAllNodesToNormalNode() {
|
transformAllNodesToNormalNode() {
|
||||||
if (!this.mindMap.renderer.renderTree) return
|
const renderTree = this.mindMap.renderer.renderTree
|
||||||
|
if (!renderTree) return
|
||||||
walk(
|
walk(
|
||||||
this.mindMap.renderer.renderTree,
|
renderTree,
|
||||||
null,
|
null,
|
||||||
node => {
|
node => {
|
||||||
if (node.data.richText) {
|
if (node.data.richText) {
|
||||||
@ -840,15 +757,12 @@ class RichText {
|
|||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
// 清空历史数据,并且触发数据变化
|
this.afterHandleData()
|
||||||
this.mindMap.command.clearHistory()
|
|
||||||
this.mindMap.command.addHistory()
|
|
||||||
this.mindMap.render(null, CONSTANTS.TRANSFORM_TO_NORMAL_NODE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理导入数据
|
// 处理导入数据
|
||||||
handleSetData(data) {
|
handleSetData(data) {
|
||||||
let walk = root => {
|
const walk = root => {
|
||||||
if (root.data && !root.data.richText) {
|
if (root.data && !root.data.richText) {
|
||||||
root.data.richText = true
|
root.data.richText = true
|
||||||
root.data.resetRichText = true
|
root.data.resetRichText = true
|
||||||
@ -857,8 +771,10 @@ class RichText {
|
|||||||
if (root.data) {
|
if (root.data) {
|
||||||
const generalizationList = formatGetNodeGeneralization(root.data)
|
const generalizationList = formatGetNodeGeneralization(root.data)
|
||||||
generalizationList.forEach(item => {
|
generalizationList.forEach(item => {
|
||||||
item.richText = true
|
if (!item.richText) {
|
||||||
item.resetRichText = true
|
item.richText = true
|
||||||
|
item.resetRichText = true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (root.children && root.children.length > 0) {
|
if (root.children && root.children.length > 0) {
|
||||||
|
|||||||
@ -228,7 +228,7 @@ class Search {
|
|||||||
const keep = replaceText.includes(this.searchText)
|
const keep = replaceText.includes(this.searchText)
|
||||||
const text = this.getReplacedText(currentNode, this.searchText, replaceText)
|
const text = this.getReplacedText(currentNode, this.searchText, replaceText)
|
||||||
this.notResetSearchText = true
|
this.notResetSearchText = true
|
||||||
currentNode.setText(text, currentNode.getData('richText'), true)
|
currentNode.setText(text, currentNode.getData('richText'))
|
||||||
if (keep) {
|
if (keep) {
|
||||||
this.updateMatchNodeList(this.matchNodeList)
|
this.updateMatchNodeList(this.matchNodeList)
|
||||||
return
|
return
|
||||||
@ -258,18 +258,15 @@ class Search {
|
|||||||
// 如果当前搜索文本是替换文本的子串,那么该节点还是符合搜索结果的
|
// 如果当前搜索文本是替换文本的子串,那么该节点还是符合搜索结果的
|
||||||
const keep = replaceText.includes(this.searchText)
|
const keep = replaceText.includes(this.searchText)
|
||||||
this.notResetSearchText = true
|
this.notResetSearchText = true
|
||||||
const hasRichTextPlugin = this.mindMap.renderer.hasRichTextPlugin()
|
|
||||||
this.matchNodeList.forEach(node => {
|
this.matchNodeList.forEach(node => {
|
||||||
const text = this.getReplacedText(node, this.searchText, replaceText)
|
const text = this.getReplacedText(node, this.searchText, replaceText)
|
||||||
if (this.isNodeInstance(node)) {
|
if (this.isNodeInstance(node)) {
|
||||||
const data = {
|
const data = {
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
if (hasRichTextPlugin) data.resetRichText = !!node.getData('richText')
|
|
||||||
this.mindMap.renderer.setNodeDataRender(node, data, true)
|
this.mindMap.renderer.setNodeDataRender(node, data, true)
|
||||||
} else {
|
} else {
|
||||||
node.data.text = text
|
node.data.text = text
|
||||||
if (hasRichTextPlugin) node.data.resetRichText = !!node.data.richText
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.mindMap.render()
|
this.mindMap.render()
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import {
|
import {
|
||||||
nodeDataNoStylePropList,
|
nodeDataNoStylePropList,
|
||||||
selfCloseTagList
|
selfCloseTagList,
|
||||||
|
richTextSupportStyleList
|
||||||
} from '../constants/constant'
|
} from '../constants/constant'
|
||||||
import MersenneTwister from './mersenneTwister'
|
import MersenneTwister from './mersenneTwister'
|
||||||
import { ForeignObject } from '@svgdotjs/svg.js'
|
import { ForeignObject } from '@svgdotjs/svg.js'
|
||||||
@ -481,7 +482,7 @@ export const loadImage = imgFile => {
|
|||||||
|
|
||||||
// 移除字符串中的html实体
|
// 移除字符串中的html实体
|
||||||
export const removeHTMLEntities = str => {
|
export const removeHTMLEntities = str => {
|
||||||
;[[' ', ' ']].forEach(item => {
|
[[' ', ' ']].forEach(item => {
|
||||||
str = str.replaceAll(item[0], item[1])
|
str = str.replaceAll(item[0], item[1])
|
||||||
})
|
})
|
||||||
return str
|
return str
|
||||||
@ -949,6 +950,12 @@ export const selectAllInput = el => {
|
|||||||
|
|
||||||
// 给指定的节点列表树数据添加附加数据,会修改原数据
|
// 给指定的节点列表树数据添加附加数据,会修改原数据
|
||||||
export const addDataToAppointNodes = (appointNodes, data = {}) => {
|
export const addDataToAppointNodes = (appointNodes, data = {}) => {
|
||||||
|
data = { ...data }
|
||||||
|
const alreadyIsRichText = data && data.richText
|
||||||
|
// 如果指定的数据就是富文本格式,那么不需要重新创建
|
||||||
|
if (alreadyIsRichText && data.resetRichText) {
|
||||||
|
delete data.resetRichText
|
||||||
|
}
|
||||||
const walk = list => {
|
const walk = list => {
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
node.data = {
|
node.data = {
|
||||||
@ -1027,7 +1034,7 @@ export const generateColorByContent = str => {
|
|||||||
|
|
||||||
// html转义
|
// html转义
|
||||||
export const htmlEscape = str => {
|
export const htmlEscape = str => {
|
||||||
;[
|
[
|
||||||
['&', '&'],
|
['&', '&'],
|
||||||
['<', '<'],
|
['<', '<'],
|
||||||
['>', '>']
|
['>', '>']
|
||||||
@ -1639,3 +1646,16 @@ export const mergeTheme = (dest, source) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取节点实例的文本样式数据
|
||||||
|
export const getNodeRichTextStyles = node => {
|
||||||
|
const res = {}
|
||||||
|
richTextSupportStyleList.forEach(prop => {
|
||||||
|
let value = node.style.merge(prop)
|
||||||
|
if (prop === 'fontSize') {
|
||||||
|
value = value + 'px'
|
||||||
|
}
|
||||||
|
res[prop] = value
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user