import { DomEditor, IDomEditor } from '@wangeditor/editor'
import $, { DOMElement } from '../utils/dom'
import { Editor } from 'slate'
import autoComplete from './components/AutoComplete'
import { formulaRender } from '../utils/util'
import { KatexOptions } from 'katex'
export const IS_MAC = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent)
export interface IFormulaConfig {
formulaConfig: {
katexOptions?: KatexOptions
formulaRender?: (value: string, el: HTMLElement) => void
}
}
export function getFormulaConfig(editor: IDomEditor): IFormulaConfig['formulaConfig'] {
const { EXTEND_CONF } = editor.getConfig()
const { formulaConfig = {} } = (EXTEND_CONF || {}) as IFormulaConfig
return formulaConfig
}
export function formulaRenderWithEditor(editor: IDomEditor, value: string, elem: HTMLElement) {
const { katexOptions, formulaRender: katexRender } = getFormulaConfig(editor)
formulaRender(value, elem, { katexOptions, katexRender })
}
const prefixClassName = (className: string) => `w-e-formula-${className}`
export function isMenuDisabled(editor: IDomEditor, mark?: string): boolean {
if (editor.selection == null) return true
const [match] = Editor.nodes(editor, {
match: n => {
const type = DomEditor.getNodeType(n)
if (type === 'pre') return true // 代码块
if (Editor.isVoid(editor, n)) return true // void node
return false
},
universal: true,
})
// 命中,则禁用
if (match) return true
return false
}
export const stringToHtml = (s: string): string => {
return s
.replace(/\n$/g, '_65a5ba9e52a3761bd68eb531e9794ae12c1d34c167f071a6b239c855b5cf57b2') //最后一个换行符
.replace(/\n/g, '_dafd41284316e72de3a0b07bd1262cd142151708353024244238eec3699e22ae') //普通换行符
.replace(/\s/g, '_e613de3e0d3b707ade3d6a289abbab35c67d4476dc12211865d70a493d7e144c') //空格
.replace(
/(\+|-|\*|\/|=|>|<|!|\^|\(|\)|%)/g,
'$1'
)
.replace(/(\\{2})/g, '$1')
.replace(/(\\[a-zA-Z]+)/g, '$1')
.replace(
/(\\begin|\\end)<\/span>/g,
'$1'
)
.replace(/([{}])/g, '$1')
.replace(/_dafd41284316e72de3a0b07bd1262cd142151708353024244238eec3699e22ae/g, '
')
.replace(/_e613de3e0d3b707ade3d6a289abbab35c67d4476dc12211865d70a493d7e144c/g, ' ')
.replace(
/_65a5ba9e52a3761bd68eb531e9794ae12c1d34c167f071a6b239c855b5cf57b2/g,
"
_"
)
}
function insertText(textarea, text) {
const currentPosition = textarea.selectionStart
const startValue = textarea.value.substring(0, currentPosition)
const startPos = Math.max(0, startValue.lastIndexOf('\\'))
// 插入文本
textarea.value =
textarea.value.substring(0, startPos) + text + textarea.value.substring(currentPosition)
// 计算新的光标位置并设置
const index = text.lastIndexOf('{}')
const newPos = index === -1 ? startPos + text.length : startPos + index + 1
textarea.selectionStart = textarea.selectionEnd = newPos
}
/**
* 生成 modal textarea elems
* @param labelText label text
* @param textareaId input dom id
* @param placeholder input placeholder
* @returns [$container, $textarea, $textareaBox]
*/
export function genModalTextareaElems(
editor: IDomEditor,
labelText: string,
textareaId: string,
placeholder?: string
) {
const $container = $('')
$container.append(`${labelText}
`)
const $textareaBox = $(``)
const $textarea = $(
``
)
const $textareaContent = $(
``
)
const $textareaCursor = $(
``
)
const $render = $('')
const renderLatex = (str: string) => {
formulaRenderWithEditor(editor, str, $render[0] as any)
}
$textareaBox.append($textarea)
$textareaBox.append($textareaContent)
$textareaBox.append($textareaCursor)
$container.append($textareaBox)
let keyword = ''
let isShowAutoComplete = false
const cursorElem = document.createElement('span')
cursorElem.textContent = '|'
$textarea.on('input', (e: any) => {
const cursorPosition = e.target.selectionStart
const cursorBeforeText = ($textarea.val() || '').slice(0, cursorPosition)
$textareaCursor[0].textContent = cursorBeforeText
$textareaCursor.append(cursorElem)
// 输入字符和删除单个字符
if (['insertText', 'deleteContentBackward'].includes(e.inputType)) {
if (e.data === '\\' && !isShowAutoComplete) {
// 输入 \ 触发
keyword = e.data
isShowAutoComplete = true
autoComplete
.open({
input: $textarea[0],
target: cursorElem,
editor,
})
.then(symbol => {
insertText($textarea[0], symbol)
setTextareaValue($textarea.val())
isShowAutoComplete = false
})
.catch(() => {
isShowAutoComplete = false
})
} else {
if (e.inputType === 'deleteContentBackward') {
keyword = keyword.slice(0, -1)
} else {
if (keyword.startsWith('\\')) {
keyword = `\\${cursorBeforeText.split('\\').pop()}`
}
}
}
} else {
keyword = ''
}
const value = $textarea.val()
const html = stringToHtml(value)
$textareaContent[0].innerHTML = html
renderLatex($textarea.val())
$textarea[0].setAttribute('data-cursor-value', keyword)
})
$textarea.on('scroll', e => {
$textareaContent[0].scrollLeft = (e.target as any).scrollLeft
$textareaContent[0].scrollTop = (e.target as any).scrollTop
})
const setTextareaValue = (value: string) => {
$textarea.val(value)
renderLatex(value)
const html = stringToHtml(value)
$textareaContent[0].innerHTML = html
}
setTimeout(() => {
$textarea.focus()
})
return { textareaContainerElem: $container[0], setTextareaValue, renderElem: $render[0] }
}