当前位置: 首页 > Web前端 > HTML

如何用draftjs设计消息框

时间:2023-03-28 14:59:29 HTML

draftjs介绍draftjs是react的一个富文本编辑器框架,它不是开箱即用的,但是它提供了很多开发富文本的api。基于此,开发者可以构建定制化的富文本编辑器。draftjs有几个重要的概念:EditorState、Entity、SelectionState、CompositeDecorator。EditorStateEditorState是编辑器的顶级状态对象。它是一个不可变的数据,代表了Draft编辑器的整个状态,包括:当前文本内容状态(ContentState)当前选择状态(SelectionState)内容(Decorator)的装饰器(Decorator)撤销/重做栈对内容所做的最新类型更改(EditorChangeType)draftjs是基于不可变(immutable)数据的,所以对编辑器的任何修改都需要生成一个新的EditorState对象,传入编辑器中,实现数据更新。EntityEntity用于用元数据来描述文本,使得一段文本可以承载任何类型的数据,并提供更丰富的功能。链接、提及和嵌入内容都可以通过实体实现。Entitystructure{type:'string',//表示Entity的类型;例如:'LINK'、'TOKEN'、'PHOTO'、'IMAGE'可变性:'MUTABLE'|'不可变'|'SEGMENTED',//该属性表示在编辑器中编辑文本范围时,使用该实体对象注释的文本范围的行为。data:'object',//实体元数据;用于存储Entity中你想存储的任何信息}属性Mutability的三个值的含义分别是:Immutable:这个Entity作为一个整体,一个Delete会删除整个文本,文本不能被删除改变了;可变的:编辑器中的实体文本可以自由修改,比如链接文本;Segmented:与Immutable类似,不同的是可以删除部分文字;SelectionStateSelectionState表示编辑器中的选择范围。选择范围有两个点:锚点(起点)和焦点(终点)。锚点位置===焦点位置,没有选中文字;锚点位置>焦点位置,从右到左选择文本;锚点位置<焦点位置,从左到右选择的文本;CompositeDecoratorDecorator概念基于扫描给定的ContentBlock,根据定义的策略定位匹配位置,并使用指定的React组件渲染它们。实现一个消息框,首先明确需求:有长度限制,暂定200个字符;当提到(@)时,突出显示,当用户输入@符号时,@符号后面的文字被突出显示;插入链接;首先实现一个基本的编辑器:importReactfrom'react'import{Editor,EditorState}from'draft-js';导入“草稿-js/dist/Draft.css”;导入'./App.css';functionMyEditor(){const[editorState,setEditorState]=React.useState(()=>EditorState.createEmpty(),);常量handleEditorChange=(newEditorState)=>{setEditorState(newEditorState);}return(submit

);}exportdefaultMyEditor;可以看到没有带工具栏的文本框,而是生成一个可编辑区域,然后我们就给他赋予独特的功能。需求一:限制消息长度编辑器有两种输入形式:键盘输入和粘贴。我们可以通过maxLength来限制通用输入框。draftjs没有这个属性,但是提供了handleBeforeInput和handlePastedText。handleBeforeInputhandleBeforeInput?:(chars:string,//输入内容editorState:EditorState,//编辑器文本内容状态eventTimeStamp:number,)=>'handled'|'not-handled'handleBeforeInput返回handled时默认的输入行为会被阻塞,handlePastedText同理。handlePastedTexthandlePastedText?:(text:string,html?:string,editorState:EditorState,)=>'处理'|'not-handled'接下来,修改我们的代码:constMAX_LENGTH=200;functionMyEditor(){const[editorState,setEditorState]=React.useState(()=>EditorState.createEmpty(),);常量handleEditorChange=(newEditorState)=>{setEditorState(newEditorState);}consthandleBeforeInput=(_,editorState)=>{//获取编辑器文本内容的状态constcurrentContent=editorState.getCurrentContent();//获取编辑器的文本长度,getPlainText返回当前编辑器的文本内容,字符串类型constcurrentContentLength=currentContent.getPlainText('').length;if(currentContentLength>MAX_LENGTH-1){//当当前文本长度大于最大长度时阻止输入,否则允许输入return'handled';}返回“未处理”;}return(submit
);}这里可能会有疑问:为什么要将MAX_LENGTH减一?原因是handleBeforeInput在输入前触发,所以getPlainText返回编辑器内容改变前的内容长度+输入内容长度<最大长度,因为是键盘输入,所以输入内容长度一直为1。这还没完yet,选择文本内容然后输入的情况还没有处理。这需要使用SelectionState。添加getLengthOfSelectedText函数:constgetLengthOfSelectedText=()=>{//获取编辑器的选择状态constcurrentSelection=editorState.getSelection();//返回选择状态,anchor和focus的offset相同(没有选择)和anchor点和focus的block_key相同时返回trueconstisCollapsed=currentSelection.isCollapsed();让长度=0;if(!isCollapsed){constcurrentContent=editorState.getCurrentContent();//获取选择范围的起始位置block_keyconststartKey=currentSelection.getStartKey();//获取选择范围的结束位置block_keyconstendKey=currentSelection.getEndKey();if(startKey===endKey){//选择范围在同一块,则选择长度=结束偏移量-起始偏移量+=currentSelection.getEndOffset()-currentSelection.getStartOffset();}else{conststartBlockTextLength=currentContent.getBlockForKey(startKey).getLength();//起始块选择长度=起始块长度-起始偏移量conststartSelectedTextLength=startBlockTextLength-currentSelection.getStartOffset();//结束点在结束块中的偏移量constendSelectedTextLength=currentSelection.getEndOffset();//getKeyAfter返回具有指定键的块之后的块的键constkeyAfterEnd=currentContent.getKeyAfter(endKey);让currentKey=startKey;//累加起始块和结束块之间块的选定长度while(currentKey&¤tKey!==keyAfterEnd){if(currentKey===startKey){length+=startSelectedTextLength+1;}elseif(currentKey===endKey){length+=endSelectedTextLength;}else{length+=currentContent.getBlockForKey(currentKey).getLength()+1;}currentKey=currentContent.getKeyAfter(currentKey);}}}返回长度;};draftjs中的几个API和block的概念有点复杂,但是目的很简单,就是获取选区的长度现在让我们转换handleBeforeInput:consthandleBeforeInput=(_,editorState)=>{constcurrentContent=editorState.getCurrentContent();constcurrentContentLength=currentContent.getPlainText('').length;//实际长度=当前内容的长度-选择的长度(替换长度)if(currentContentLength-getLengthOfSelectedText()>MAX_LENGTH-1){return'handled';}返回“未处理”;在其他情况下,有更多的pastedText(粘贴文本)参数。consthandlePastedText=(pastedText)=>{constcurrentContent=editorState.getCurrentContent();constcurrentContentLength=currentContent.getPlainText('').length;constselectedTextLength=getLengthOfSelectedText();if(currentContentle.LengthLength>ngthedText-pasted1){return'handled';}返回“未处理”;};为了有更好的用户体验,可以在编辑器右下角添加当前内容长度/最大长度的提示。修改handleEditorChange方法以将当前文本长度存储在状态中。consthandleEditorChange=(newEditorState)=>{constcurrentContent=newEditorState.getCurrentContent();constcurrentContentLength=currentContent.getPlainText('').length;设置长度(当前内容长度);setEditorState(newEditorState);}调整样式,看效果:至此我们完成了第一个需求。需求二:提及时高亮显示(@)一般提及会改变@符号后面文字的颜色,以示区别。我们可以使用正则表达式来匹配@符号和后面的文字,然后替换成我们自己定义的ReactNode就可以高亮显示,这时候Decorator就派上用场了,我们只需要创建一个CompositeDecorator实例,传递给createEmpty编辑器初始化时。constHANDLE_REGEX=/@[\w]+/g;constcompositeDecorator=newCompositeDecorator([{strategy:(contentBlock,callback)=>{//每次编辑器更改时都会触发此函数以获取内容文本。consttext=contentBlock.getText();让matchArr,开始;while((matchArr=HANDLE_REGEX.exec(text))!==null){//获取匹配值的起始位置和偏移量,并在回调后替换为这个装饰器的组件start=matchArr.index;回调(开始,开始+matchArr[0].length);}},component:(props)=>{return({props.children});},},]);const[editorState,setEditorState]=React.useState(()=>EditorState.createEmpty(compositeDecorator),);看效果:需求三:插入链接显示文字,移动鼠标提示url。纯文本已经无法描述这条信息,这就需要使用Entity。添加insertEntity函数:constinsertEntity=(entityData)=>{letcontentState=editorState.getCurrentContent();//创建实体contentState=contentState.createEntity('LINK','IMMUTABLE',entityData);constentityKey=contentState.getLastCreatedEntityKey();让selection=editorState.getSelection();//判断是替换还是插入}else{contentState=Modifier.replaceText(contentState,selection,entityData.name+'',undefined,entityKey,);}让结束;//获取实体在编辑器中显示的范围,目的是让插入实体后的光标停留在实体的末尾contentState.getFirstBlock().findEntityRanges((character)=>character.getEntity()===entityKey,(_,_end)=>{end=_end;});让newEditorState=EditorState.set(editorState,{currentContent:contentState});选择=selection.merge({anchorOffset:end,focusOffset:end,});newEditorState=EditorState.forceSelection(newEditorState,selection);handleEditorChange(newEditorState);};看效果:完成!完整代码由于完整代码占用空间较大,请关注公众号“全享云低码”获取完整代码,回复“评论区完整代码”获取参考内容draftjs:https://draftjs.org/公众号:QuanxiangCloudLowCodeGitHub:https://github.com/quanxiang-...