gu-plugin-formula
Version:
138 lines (110 loc) • 3.78 kB
text/typescript
/**
* @description insert formula menu
* @author wangfupeng
*/
import { IModalMenu } from '@wangeditor/editor'
import {
DomEditor,
IDomEditor,
SlateNode,
SlateRange,
t,
genModalTextareaElems,
genModalButtonElems,
} from '@wangeditor/editor'
import { SIGMA_SVG } from '../../constants/icon-svg'
import $, { Dom7Array, DOMElement } from '../../utils/dom'
import { genRandomStr } from '../../utils/util'
import { FormulaElement } from '../custom-types'
import { normalizeForKaTeX, extractAllBetweenBrackets } from './utils'
/**
* 生成唯一的 DOM ID
*/
function genDomID(): string {
return genRandomStr('w-e-insert-formula')
}
class InsertFormulaMenu implements IModalMenu {
readonly title = t('formula.insert')
readonly iconSvg = SIGMA_SVG
readonly tag = 'button'
readonly showModal = true // 点击 button 时显示 modal
readonly modalWidth = 300
private $content: Dom7Array | null = null
private readonly textareaId = genDomID()
private readonly buttonId = genDomID()
getValue(editor: IDomEditor): string | boolean {
// 插入菜单,不需要 value
return ''
}
isActive(editor: IDomEditor): boolean {
// 任何时候,都不用激活 menu
return false
}
exec(editor: IDomEditor, value: string | boolean) {
// 点击菜单时,弹出 modal 之前,不需要执行其他代码
// 此处空着即可
}
isDisabled(editor: IDomEditor): boolean {
const { selection } = editor
if (selection == null) return true
if (SlateRange.isExpanded(selection)) return true // 选区非折叠,禁用
const selectedElems = DomEditor.getSelectedElems(editor)
const hasVoidElem = selectedElems.some(elem => editor.isVoid(elem))
if (hasVoidElem) return true // 选中了 void 元素,禁用
const hasPreElem = selectedElems.some(elem => DomEditor.getNodeType(elem) === 'pre')
if (hasPreElem) return true // 选中了 pre 原则,禁用
return false
}
getModalPositionNode(editor: IDomEditor): SlateNode | null {
return null // modal 依据选区定位
}
getModalContentElem(editor: IDomEditor): DOMElement {
const { textareaId, buttonId } = this
const [textareaContainerElem, textareaElem] = genModalTextareaElems(
t('formula.formula'),
textareaId,
t('formula.placeholder')
)
const $textarea = $(textareaElem)
const [buttonContainerElem] = genModalButtonElems(buttonId, t('formula.ok'))
if (this.$content == null) {
// 第一次渲染
const $content = $('<div></div>')
// 绑定事件(第一次渲染时绑定,不要重复绑定)
$content.on('click', `#${buttonId}`, e => {
e.preventDefault()
let value = $content.find(`#${textareaId}`).val().trim()
value = extractAllBetweenBrackets(value)
this.insertFormula(editor, value)
editor.hidePanelOrModal() // 隐藏 modal
})
// 记录属性,重要
this.$content = $content
}
const $content = this.$content
$content.html('') // 先清空内容
// append textarea and button
$content.append(textareaContainerElem)
$content.append(buttonContainerElem)
// 设置 input val
$textarea.val('')
// focus 一个 input(异步,此时 DOM 尚未渲染)
setTimeout(() => {
$textarea.focus()
})
return $content[0]
}
private insertFormula(editor: IDomEditor, value: string) {
if (!value) return
// 还原选区
editor.restoreSelection()
if (this.isDisabled(editor)) return
const formulaElem: FormulaElement = {
type: 'formula',
value,
children: [{ text: '' }], // void node 需要有一个空 text
}
editor.insertNode(formulaElem)
}
}
export default InsertFormulaMenu