Feat:支持直接在富文本编辑框中编辑数学公式

This commit is contained in:
街角小林 2024-06-04 11:45:31 +08:00
parent 8152fab185
commit f996ec9bae
3 changed files with 94 additions and 3 deletions

View File

@ -332,5 +332,11 @@ export const defaultOpt = {
// 添加附加的节点前置内容,前置内容指和文本同一行的区域中的前置内容,不包括节点图片部分
createNodePrefixContent: null,
// 添加附加的节点后置内容,后置内容指和文本同一行的区域中的后置内容,不包括节点图片部分
createNodePostfixContent: null
createNodePostfixContent: null,
// 是否开启在富文本编辑框中直接编辑数学公式
enableEditFormulaInRichTextEdit: true,
// 转换富文本内容,当进入富文本编辑时,可以通过该参数传递一个函数,函数接收文本内容,需要返回你处理后的文本内容
transformRichTextOnEnterEdit: null,
// 可以传递一个函数即将结束富文本编辑前会执行该函数函数接收richText实例所以你可以在此时机更新quill文档数据
beforeHideRichTextEdit: null
}

View File

@ -10,9 +10,18 @@ class Formula {
this.opt = opt
this.mindMap = opt.mindMap
window.katex = katex
this.init()
this.extendQuill()
}
init() {
if (this.mindMap.opt.enableEditFormulaInRichTextEdit) {
this.mindMap.opt.transformRichTextOnEnterEdit =
this.latexRichToText.bind(this)
this.mindMap.opt.beforeHideRichTextEdit = this.formatLatex.bind(this)
}
}
// 获取katex配置
getKatexConfig() {
const config = {
@ -59,6 +68,74 @@ class Formula {
richTextPlugin.setTextStyleIfNotRichText(richTextPlugin.node)
richTextPlugin.hideEditText([node])
}
// 将公式富文本转换为公式源码
latexRichToText(nodeText) {
if (nodeText.indexOf('class="ql-formula"') !== -1) {
const parser = new DOMParser()
const doc = parser.parseFromString(nodeText, 'text/html')
const els = doc.getElementsByClassName('ql-formula')
for (const el of els)
nodeText = nodeText.replace(
el.outerHTML,
`\$${el
.getAttribute('data-value')
.replaceAll('&', '&')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')}\$`
)
}
return nodeText
}
// 使用格式化的 latex 字符串内容更新 quill 内容:输入 $*****$
formatLatex(richText) {
const contents = richText.quill.getContents()
const ops = contents.ops
let mod = false
for (let i = ops.length - 1; i >= 0; i--) {
const op = ops[i]
const insert = op.insert
if (insert && typeof insert !== 'object' && insert !== '\n') {
if (/\$.+?\$/g.test(insert)) {
const m = [...insert.matchAll(/\$.+?\$/g)]
const arr = insert.split(/\$.+?\$/g)
for (let j = m.length - 1; j >= 0; j--) {
const exp = m[j]?.[0].slice(1, -1) ?? null // $...$ 之间的表达式
if (exp !== null && exp.trim().length > 0) {
const isLegal = this.checkFormulaIsLegal(exp)
if (isLegal) {
arr.splice(j + 1, 0, { insert: { formula: exp } }) // 添加到对应位置之后
mod = true
} else {
arr.splice(j + 1, 0, '')
}
} else arr.splice(j + 1, 0, '') // 表达式为空时,占位
}
while (arr.length > 0) {
let v = arr.pop()
if (typeof v === 'string') {
if (v.length < 1) continue
v = { insert: v }
}
v['attributes'] = ops[i]['attributes']
ops.splice(i + 1, 0, v)
}
ops.splice(i, 1) // 删除原来的字符串
}
}
}
if (mod) richText.quill.setContents(contents)
}
checkFormulaIsLegal(str) {
try {
katex.renderToString(str)
return true
} catch (e) {
return false
}
}
}
Formula.instanceName = 'formula'

View File

@ -171,7 +171,8 @@ class RichText {
customInnerElsAppendTo,
nodeTextEditZIndex,
textAutoWrapWidth,
selectTextOnEnterEditText
selectTextOnEnterEditText,
transformRichTextOnEnterEdit
} = this.mindMap.opt
this.node = node
this.isInserting = isInserting
@ -241,7 +242,10 @@ class RichText {
}
}
// 节点文本内容
const nodeText = node.getData('text')
let nodeText = node.getData('text')
if (typeof transformRichTextOnEnterEdit === 'function') {
nodeText = transformRichTextOnEnterEdit(nodeText)
}
// 是否是空文本
const isEmptyText = isUndef(nodeText)
// 是否是非空的非富文本
@ -325,6 +329,10 @@ class RichText {
if (!this.showTextEdit) {
return
}
const { beforeHideRichTextEdit } = this.mindMap.opt
if (typeof beforeHideRichTextEdit === 'function') {
beforeHideRichTextEdit(this)
}
let html = this.getEditText()
let list =
nodes && nodes.length > 0 ? nodes : this.mindMap.renderer.activeNodeList