BraftEditor的进阶
最近的项目需要使用富文本编辑框,调研后使用BraftEditor来实现软件的文本编辑功能,在使用中,我们会遇到一些组件暂未封装或者是我们感觉其不是那么完美的一些功能,如限制富文本编辑框输入的字符个数,插入自定义表情,插入超链接文本等功能。下面就总结其使用方法。
限制输入框字符个数
使用braft-editor扩展包(max-length)
使用方式
import BraftEditor from "braft-editor";
import MaxLength from 'braft-extensions/dist/max-length';
// defaultValue的值即为字符的个数
BraftEditor.use(MaxLength({defaultValue: 100}));
缺点
其计算的字符个数是字符串的长度,实际上我们与服务侧发送的文本长度几乎都是以字节为单位进行传输,所以该方式就不适合这种情况,另外编辑框中可能存在表情,此时,使用该种方式就不会计算表情的长度。
使用BraftEditor提供的属性
BraftEditor的常用属性
<BraftEditor
value={content}
controls={[]}
extendControls={[{
type: 'component',
key: 'btnFace',
component: getFaceComp()
}]}
contentStyle={{ height: 200 }}
// 按下回车的响应
handleReturn={(e) => {
console.log('handleReturn');
}}
// 键盘按下的响应
handleBeforeInput={(text, editorState) => {
console.log('handleBeforeInput');
}}
// 粘贴时的响应
handlePastedText={(text, html, editorState) => {
console.log('handlePastedText');
}}
// 键盘按下响应,必须使用keyBindinFn,该属性才可生效
handleKeyCommand={(e) => {
console.log('handleKeyCommand');
}}
// 键盘按下响应,绑定按键
keyBindingFn={(e) => {
console.log('keyBindingFn');
return getDefaultKeyBinding(e);
}}
// 编辑框内容变化时的响应
onChange={(editorState) => {
console.log('onChange');
}}
/>
经打日志,我们发现键盘输入文本时,这几个属性执行的先后顺序为:keyBindingFn>handleBeforeInput>onChange
使用
我们可以在keyBindingFn中计算字符长度或者文本的字节长度,超过一定数量,使用e.preventDefault()阻止向后执行。
// 字节个数
const strText = content.toText();
let byteLength = 0;
for (let index = 0; index < strText.length; index++) {
byteLength += 128 < strText.charCodeAt(index) ? 2 : 1;
}
// 字符长度
const nLength = strText.length;
console.log('输入框中的文本长度为:', byteLength);
if (100 < byteLength) {
e.preventDefault();
}
注意
1.使用扩展时,handleBeforeInput属性失效;
2.两种方式计算出的字符个数均为上一次文本框中的内容长度,因为只有执行onChange后,编辑框内容才发生变化。该问题还未解决。
插入自定义表情
使用ContentUtils.insertText
// 引用表情扩展
import Emoticon from 'braft-extensions/dist/emoticon';
import { ContentUtils } from 'braft-utils';
BraftEditor.use(Emoticon());
// 使用ContentUtils.insertText向编辑框插入表情
setContent(ContentUtils.insertText(content, ' ', null, {
type: 'EMOTICON',
mutability: 'IMMUTABLE',
data: {
src: strPath
}
}));
注意
中间必须加’ ',具体原因未知。
使用ContentUtils.insertMedias(不推荐使用)
// 使用insertMedia插入的图片,鼠标悬浮后有brafteditor内置的东西
setContent(ContentUtils.insertMedias(
content, [{
type: 'IMAGE',
url: strPath
}]
));
插入超链接文本
使用ContentUtils.insertText
setContent(ContentUtils.insertText(content, 'baidu.com', ['COLOR-00F', 'UNDERLINE'], {
type: 'LINK',
mutability: 'MUTABLE',
data: {
href: 'www.baidu.com',
target: '#'
}
问题
设置完样式后,超链接后面的样式均为该样式,暂时不知道如何解决。
使用ContentUtils.insertHTML
setContent(ContentUtils.insertHTML(content, '<a>www.baidu.com</a>'));
以下为该篇文章运行后的截图。
全部代码如下,望各位大佬能够帮忙指点迷津,多多指教,互相帮助。
import BraftEditor from "braft-editor";
import { ContentUtils } from 'braft-utils';
import { EditorState, getDefaultKeyBinding } from 'draft-js';
import React from "react";
import 'braft-editor/dist/index.css';
// import MaxLength from 'braft-extensions/dist/max-length';
// 引用表情扩展
import Emoticon from 'braft-extensions/dist/emoticon';
import { Popover } from "antd";
import face1 from '../Image/1.png';
import face2 from '../Image/2.png';
import face3 from '../Image/3.png';
import face4 from '../Image/4.png';
import face5 from '../Image/5.png';
import face6 from '../Image/6.png';
import face7 from '../Image/7.png';
import face8 from '../Image/8.png';
import face9 from '../Image/9.png';
import face10 from '../Image/10.png';
import face11 from '../Image/11.png';
import face12 from '../Image/12.png';
import face13 from '../Image/13.png';
// BraftEditor.use(MaxLength({defaultValue: 10}));
BraftEditor.use(Emoticon());
const arrFace = [face1, face2, face3, face4, face5, face6, face7, face8, face9, face10, face11, face12, face13];
export default function RichEditor() {
const [content, setContent] = React.useState(BraftEditor.createEditorState(null));
const [bVisible, setVisible] = React.useState(false);
function getFaceComp() {
let arrContent = [];
let arrRowContent = [];
for (let nIndex = 0; nIndex < arrFace.length;) {
const strPath = arrFace[nIndex++];
arrRowContent.push(
<td style={{ padding: 5 }}>
<img onClick={() => {
setContent(ContentUtils.insertText(content, ' ', null, {
type: 'EMOTICON',
mutability: 'IMMUTABLE',
data: {
src: strPath
}
}));
// 使用insertMedia插入的图片,鼠标悬浮后有brafteditor内置的东西
setContent(ContentUtils.insertMedias(
content, [{
type: 'IMAGE',
url: strPath
}]
));
setVisible(false);
}} src={strPath} width={32} height={32} alt="" /></td>
);
if (0 === nIndex % 5) {
arrContent.push(
<tr>
{arrRowContent}
</tr>
);
arrRowContent = [];
continue;
}
if (arrFace.length === nIndex && 0 !== arrRowContent.length) {
arrContent.push(
<tr>
{arrRowContent}
</tr>
);
}
}
return (
<div>
<Popover visible={bVisible} trigger={['click', 'hover']} content={arrContent}>
<button onClick={() => {
setVisible(!bVisible);
}}>表情</button>
</Popover>
</div>);
}
return (
<div style={{ width: 800, height: 500 }}>
<BraftEditor
value={content}
// controls={[]}
extendControls={[{
type: 'component',
key: 'btnFace',
component: getFaceComp()
}]}
contentStyle={{ height: 200 }}
handleReturn={(e) => {
console.log('handleReturn');
}}
handleBeforeInput={(text, editorState) => {
console.log('handleBeforeInput');
}}
handlePastedText={(text, html, editorState) => {
console.log('handlePastedText');
}}
handleKeyCommand={(e) => {
console.log('handleKeyCommand');
// return getDefaultKeyBinding(e);
}}
keyBindingFn={(e) => {
console.log('keyBindingFn');
// 字节个数
const strText = content.toText();
let byteLength = 0;
for (let index = 0; index < strText.length; index++) {
byteLength += 128 < strText.charCodeAt(index) ? 2 : 1;
}
// 字符长度
const nLength = strText.length;
console.log('输入框中的文本长度为:', byteLength);
if (100 < byteLength) {
e.preventDefault();
}
return getDefaultKeyBinding(e);
}}
onChange={(editorState) => {
console.log('onChange');
setContent(editorState);
}}
/>
<div style={{ width: 500, height: 50 }}>
<button onClick={() => {
setContent(ContentUtils.insertText(content, 'baidu.com', ['COLOR-00F', 'UNDERLINE'], {
type: 'LINK',
mutability: 'MUTABLE',
data: {
href: 'www.baidu.com',
target: '#'
}
}));
// TODO:手动设置当前选中文本,然后设置其状态
// let objText = JSON.parse(content.toRAW());
// for (const key in objText.entityMap) {
// if ('LINK' === objText.entityMap[key].type) {
// const objData = objText.blocks[0].entityRanges[key];
// let objSelection = content.getSelection();
// EditorState.forceSelection(content, objSelection.merge({
// anchorKey: objText.blocks[0].key,
// anchorOffset: objData.offset,
// focusKey: objText.blocks[0].key,
// focusOffset: objData.length
// }));
// }
// }
// content.forceSelection(content, ({
// anchorKey: blockKey,
// anchorOffset: 0,
// focusKey: blockKey,
// focusOffset: block.getLength()
// }));
// RichUtils.toggleInlineStyle(content, 'UNDERLINE');
// setContent(ContentUtils.insertHTML(content, '<a>www.baidu.com</a>'));
}}>插入超链接</button>
<button onClick={() => {
setContent(ContentUtils.clear(content));
}}>清空编辑框</button>
</div>
</div>
);
}