waic-editor/src/pages/wang/wang-editor.tsx
2023-08-04 23:50:54 +08:00

227 lines
8.6 KiB
TypeScript

import React, {MouseEvent, useRef, useState} from "react";
import './plugins/index.ts'
import {Editor} from '@wangeditor/editor-for-react'
import {IDomEditor} from "@wangeditor/editor";
import {Descendant} from "slate";
import '@wangeditor/editor/dist/css/style.css'
import './style.less'
import {PROOFREAD_TYPE_DESC, WangEditorContent, WangEditorProofreadElement} from "../../types/editor.ts";
import {useMount} from "ahooks";
import {Button} from "antd";
import * as classNames from "classnames";
import IconArrowRight from "../../components/icons/IconArrowRight.tsx";
import IconCheckFill from "../../components/icons/IconCheckFill.tsx";
import IconCancelFill from "../../components/icons/IconCancelFill.tsx";
import {ProofreadPopover} from "./components/ProofreadPopover.tsx";
// {
// "attributes": {
// replace: {
// origin: '湖北省张家界市',
// text: '湖南省张家界市',
// type: 'address',
// },
// // "bold":true,
// },
// "insert": "湖北省张家界市",
// }
const defaultData: WangEditorContent[] = [
{
"type": "paragraph",
"children": [
{"text": "《", "fontFamily": "宋体"},
{
"text": "皇帝的新装",
"bold": true,
"color": "rgb(106, 57, 201)"
},
{"text": "》的作者是", "fontFamily": "宋体"},
{
"text": "闻名世界",
"bold": true,
"italic": true,
"underline": true,
"through": true,
"fontSize": "18pt"
},
{"text": "的", "fontFamily": "宋体"},
{
type: 'proofread',
proofread: {
origin: '湖北省张家界市',
text: '湖南省张家界市',
type: 'address',
},
children: [
{"text": "湖北省张家界市"},
]
},
{
"text": "丹麦作家",
"bold": true,
"color": "rgb(54, 88, 226)"
},
{
type: 'proofread',
proofread: {
origin: '安徒声',
text: '安徒生',
type: 'words',
},
children: [
{"text": "安徒声"},
]
},
{"text": "的作品。", "fontFamily": "宋体"}
]
},
{
"type": "paragraph",
"children": [
{
"text": "故事里有一个愚蠢的笨国王。他罕少关心国家,一昧追求的就是衣着入时。",
},
{
"text": "有一天,王国里来了俩个骗子。",
"fontFamily": "宋体"
}
]
}
]
const WangEditor: React.FC = () => {
const [html, setHtml] = useState<string>()
const [content, setContent] = useState<Descendant[]>()
const [editor, setEditor] = useState<IDomEditor>() // 存储 editor 实例
const [ops, setOps] = useState<WangEditorProofreadElement[]>([])
const [currentIndex, setCurrentIndex] = useState<number>(-1);
const editorConfig = {
placeholder: '请输入内容...',
}
const onMounted = (editor: IDomEditor) => {
setEditor(editor)
editor.children = defaultData
editor.on('select', (a, b) => {
console.log('select=》a,b', a, b)
})
}
const parseDesc = (data: WangEditorProofreadElement) => {
const {type, origin, text} = data.proofread
const desc = PROOFREAD_TYPE_DESC[type];
return desc.replace(/%origin%/ig, origin).replace(/%text%/ig, text)
}
const divRef = useRef<HTMLDivElement>(null)
const handleDivClick = (e: MouseEvent<HTMLDivElement>) => {
if (divRef.current) {
const target = e.target as HTMLElement;
if (target.classList.contains('data-proofread-item')) {
target.classList.add('data-proofread-item-selected')
return;
}
const selectArr = divRef.current.querySelectorAll('.data-proofread-item-selected')
Array.from(selectArr).forEach(it => it.classList.remove('data-proofread-item-selected'))
setCurrentIndex(-1)
}
}
useMount(() => {
const _arr: WangEditorProofreadElement[] = [];
let index = 0;
defaultData.forEach((it) => {
if (it.type == 'paragraph' && it.children && it.children.length > 0) {
it.children.forEach(cit => {
if (Object.hasOwn(cit, 'type')) {
const data = cit as WangEditorProofreadElement;
if (data.type && data.type == 'proofread') {
data.proofread.id = index++;
data.proofread.description = parseDesc(data)
_arr.push(data)
}
}
})
}
})
setOps(_arr)
if (divRef.current) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
divRef.current.addEventListener('proofread-click', handleDivClick)
}
})
const setSelectIndex = (index: number) => {
setCurrentIndex(index)
const span = document.querySelector(`.data-proofread-item-${index}`)
if (span) {
if (span.classList.contains('data-proofread-item-selected')) return;
document.querySelector(`.data-proofread-item-selected`)?.classList.remove('data-proofread-item-selected')
span.classList.add('data-proofread-item-selected')
}
}
return (<div className="wang-editor-page">
<div className={"editor-container"}>
<div
ref={divRef}
onClick={handleDivClick}
className="h-full flex-1"
>
<Editor
className="wang-editor-instance h-full"
defaultConfig={editorConfig}
value={html}
onCreated={onMounted}
onChange={editor => {
const op = editor.operations[0]
if (op?.type == 'set_selection') {
// console.log('onchange->', JSON.stringify(editor.selection))
} else {
setHtml(editor.getHtml())
setContent(editor.children)
// editor.getFragment
}
}}
mode="default"
/>
</div>
<div className="operation-wrapper">
<div className="operation-container h-full">
<div style={{margin: 10}}>
<Button onClick={() => {
const elems = editor?.getElemsByTypePrefix('header')
console.log(elems)
}}></Button>
</div>
{ops.map((op, key) => (<div
className={classNames('operation-proofread-item', {selected: op.proofread.id == currentIndex})}
key={key}
onClick={() => setSelectIndex(Number(op.proofread.id))}
>
<div className="proofread-item-container">
<div className="proofread-data">
<span className="origin">{op.proofread.origin}</span>
<span className="icon"><IconArrowRight/></span>
<span className="text">{op.proofread.text}</span>
</div>
<div className="proofread-description">{op.proofread.description}</div>
</div>
<div className="proofread-action">
<ProofreadPopover content={<span></span>}>
<IconCheckFill className="process"/>
</ProofreadPopover>
<ProofreadPopover content={<span></span>}>
<IconCancelFill className="cancel"/>
</ProofreadPopover>
</div>
</div>))}
</div>
</div>
</div>
<div className="content">{JSON.stringify(content)}</div>
</div>)
}
export default WangEditor;