如何在React Lexical编辑器中实现类Gmail折叠内容功能?
类Gmail折叠引用块实现方案(React + Lexical)
这个功能通用名称是可折叠引用块(Collapsible Quote Block),以下是具体实现思路和代码示例:
1. 自定义可折叠引用节点
Lexical默认的QuoteNode不支持折叠状态,需要扩展它来添加状态管理:
import { QuoteNode } from '@lexical/rich-text'; import { createCommand, LexicalNode } from 'lexical'; // 定义切换折叠状态的自定义命令 export const TOGGLE_QUOTE_COLLAPSE = createCommand('TOGGLE_QUOTE_COLLAPSE'); export class CollapsibleQuoteNode extends QuoteNode { constructor(isCollapsed = false) { super(); this.__isCollapsed = isCollapsed; } // 克隆节点时保留折叠状态 clone() { return new CollapsibleQuoteNode(this.__isCollapsed); } // 序列化节点,保存折叠状态到JSON exportJSON() { return { ...super.exportJSON(), type: 'collapsible-quote', isCollapsed: this.__isCollapsed, }; } // 从JSON反序列化节点 static importJSON(serializedNode) { const node = new CollapsibleQuoteNode(serializedNode.isCollapsed); node.setFormat(serializedNode.format); return node; } // 获取折叠状态 isCollapsed() { return this.__isCollapsed; } // 切换折叠状态 toggleCollapse() { this.__isCollapsed = !this.__isCollapsed; this.getWritable().setDirty(); } } // 注册节点类型 export function registerCollapsibleQuoteNode(editor) { editor.registerNode(CollapsibleQuoteNode, { replace: QuoteNode, importJSON: CollapsibleQuoteNode.importJSON, }); }
2. 实现React渲染组件
为自定义节点编写渲染逻辑,添加折叠触发按钮:
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { useEffect } from 'react'; import { $getNearestNodeOfType } from 'lexical'; import { CollapsibleQuoteNode, TOGGLE_QUOTE_COLLAPSE } from './CollapsibleQuoteNode'; export default function CollapsibleQuoteNodeComponent({ node }) { const [editor] = useLexicalComposerContext(); const isCollapsed = node.isCollapsed(); // 处理折叠按钮点击 const handleToggle = () => { editor.dispatchCommand(TOGGLE_QUOTE_COLLAPSE, node.getKey()); }; // 监听自定义命令,更新节点状态 useEffect(() => { return editor.registerCommand( TOGGLE_QUOTE_COLLAPSE, (nodeKey) => { editor.update(() => { const quoteNode = $getNearestNodeOfType(nodeKey, CollapsibleQuoteNode); if (quoteNode) { quoteNode.toggleCollapse(); } }); return true; }, 1 ); }, [editor]); return ( <div className="collapsible-quote"> <button onClick={handleToggle} className="quote-toggle-btn" aria-label={isCollapsed ? "展开引用" : "折叠引用"} > {isCollapsed ? "..." : "▼"} </button> <blockquote className={`quote-content ${isCollapsed ? 'collapsed' : ''}`} style={{ display: isCollapsed ? 'none' : 'block' }} > <slot /> </blockquote> </div> ); } // 注册渲染组件 export function registerCollapsibleQuoteNodeComponent(editor) { editor.registerComponent(CollapsibleQuoteNode, CollapsibleQuoteNodeComponent); }
3. 集成到编辑器初始化流程
在编辑器初始化时注册自定义节点和组件:
import { LexicalComposer } from '@lexical/react/LexicalComposer'; import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; import { ContentEditable } from '@lexical/react/LexicalContentEditable'; import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'; import { useLexicalComposerContext, useEffect } from 'react'; import { registerCollapsibleQuoteNode, registerCollapsibleQuoteNodeComponent } from './CollapsibleQuote'; const initialConfig = { namespace: 'email-editor', nodes: [CollapsibleQuoteNode], onError: (error) => console.error(error), }; function EmailEditor() { const [editor] = useLexicalComposerContext(); useEffect(() => { registerCollapsibleQuoteNode(editor); registerCollapsibleQuoteNodeComponent(editor); }, [editor]); return ( <LexicalComposer initialConfig={initialConfig}> <RichTextPlugin contentEditable={<ContentEditable />} placeholder={<div>写邮件...</div>} /> <HistoryPlugin /> {/* 其他插件 */} </LexicalComposer> ); }
4. 回复时插入可折叠引用块
点击回复按钮时,创建CollapsibleQuoteNode并插入到编辑器(默认设为折叠状态):
function handleReply(originalEmail) { const editor = getEditor(); // 获取编辑器实例 editor.update(() => { // 创建引用内容节点 const quoteContent = $createParagraphNode(); quoteContent.append($createTextNode(`From: ${originalEmail.sender}\nDate: ${originalEmail.date}\n\n${originalEmail.content}`)); // 创建可折叠引用节点,默认折叠 const collapsibleQuote = new CollapsibleQuoteNode(true); collapsibleQuote.append(quoteContent); // 插入到编辑器开头 const root = $getRoot(); root.prepend(collapsibleQuote); }); }
样式参考
添加CSS美化折叠按钮和引用块:
.collapsible-quote { position: relative; margin: 12px 0; } .quote-toggle-btn { position: absolute; left: -20px; top: 4px; border: none; background: transparent; cursor: pointer; font-size: 16px; color: #666; } .quote-content { margin-left: 20px; padding-left: 12px; border-left: 3px solid #ccc; color: #555; font-style: normal; }
内容的提问来源于stack exchange,提问作者hashi
相关产品推荐
相关产品推荐

