From 8d9926f4e873b6d095242b7782a24f2c937b2bcf Mon Sep 17 00:00:00 2001 From: fantasticit Date: Mon, 25 Apr 2022 21:12:49 +0800 Subject: [PATCH] tiptap: use lru-cache in insert menu --- .../client/src/tiptap/menus/insert/index.tsx | 227 ++++++++++++------ 1 file changed, 153 insertions(+), 74 deletions(-) diff --git a/packages/client/src/tiptap/menus/insert/index.tsx b/packages/client/src/tiptap/menus/insert/index.tsx index 47612cc0..d8c3bc76 100644 --- a/packages/client/src/tiptap/menus/insert/index.tsx +++ b/packages/client/src/tiptap/menus/insert/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Editor } from '@tiptap/core'; import { Button, Dropdown, Popover } from '@douyinfe/semi-ui'; import { IconPlus } from '@douyinfe/semi-icons'; @@ -11,17 +11,149 @@ import { IconCodeBlock, IconLink, IconStatus, - IconInfo, IconAttachment, IconMath, IconCountdown, IconCallout, } from 'components/icons'; import { GridSelect } from 'components/grid-select'; +import { useToggle } from 'hooks/use-toggle'; +import { createKeysLocalStorageLRUCache } from 'helpers/lru-cache'; import { isTitleActive } from '../../utils/is-active'; import { createCountdown } from '../countdown/service'; +const insertMenuLRUCache = createKeysLocalStorageLRUCache('TIPTAP_INSERT_MENU', 3); + +const COMMANDS = [ + { + title: '通用', + }, + { + icon: , + label: '表格', + custom: (editor, runCommand) => ( + + { + return runCommand({ + label: '表格', + action: () => editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run(), + })(); + }} + /> + + } + > + + + 表格 + + + ), + }, + { + icon: , + label: '代码块', + action: (editor) => editor.chain().focus().toggleCodeBlock().run(), + }, + { + icon: , + label: '图片', + action: (editor) => editor.chain().focus().setEmptyImage().run(), + }, + { + icon: , + label: '附件', + action: (editor) => editor.chain().focus().setAttachment().run(), + }, + { + icon: , + label: '倒计时', + action: (editor) => createCountdown(editor), + }, + { + icon: , + label: '外链', + action: (editor) => editor.chain().focus().setIframe({ url: '' }).run(), + }, + { + title: '卡片', + }, + { + icon: , + label: '思维导图', + action: (editor) => editor.chain().focus().setMind().run(), + }, + { + icon: , + label: '数学公式', + action: (editor) => editor.chain().focus().setKatex({ defaultShowPicker: true }).run(), + }, + { + icon: , + label: '状态', + action: (editor) => editor.chain().focus().setStatus({ defaultShowPicker: true }).run(), + }, + { + icon: , + label: '高亮块', + action: (editor) => editor.chain().focus().setCallout().run(), + }, + { + title: '内容引用', + }, + { + icon: , + label: '文档', + action: (editor) => editor.chain().focus().setDocumentReference().run(), + }, + { + icon: , + label: '子文档', + action: (editor) => editor.chain().focus().setDocumentChildren().run(), + }, +]; + export const Insert: React.FC<{ editor: Editor }> = ({ editor }) => { + const [recentUsed, setRecentUsed] = useState([]); + const [visible, toggleVisible] = useToggle(false); + + const renderedCommands = useMemo( + () => (recentUsed.length ? [{ title: '最近使用' }, ...recentUsed, ...COMMANDS] : COMMANDS), + [recentUsed] + ); + + const transformToCommands = useCallback((data: string[]) => { + return data + .map((label) => { + return COMMANDS.find((command) => command.label && command.label === label); + }) + .filter(Boolean); + }, []); + + const runCommand = useCallback( + (command) => { + return () => { + insertMenuLRUCache.put(command.label); + setRecentUsed(transformToCommands(insertMenuLRUCache.get() as string[])); + command.action(editor); + toggleVisible(false); + }; + }, + [editor, toggleVisible] + ); + + useEffect(() => { + if (!visible) return; + insertMenuLRUCache.syncFromStorage(); + setRecentUsed(transformToCommands(insertMenuLRUCache.get() as string[])); + }, [visible]); + if (!editor) { return null; } @@ -31,80 +163,27 @@ export const Insert: React.FC<{ editor: Editor }> = ({ editor }) => { zIndex={10000} trigger="click" position="bottomLeft" + visible={visible} + onVisibleChange={toggleVisible} + style={{ + minWidth: 132, + maxHeight: 'calc(90vh - 120px)', + overflowY: 'auto', + }} render={ - 通用 - - - { - return editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run(); - }} - /> - - } - > - - 表格 - - - - editor.chain().focus().toggleCodeBlock().run()}> - 代码块 - - - editor.chain().focus().setEmptyImage().run()}> - - 图片 - - - editor.chain().focus().setAttachment().run()}> - - 附件 - - - createCountdown(editor)}> - 倒计时 - - - editor.chain().focus().setIframe({ url: '' }).run()}> - 外链 - - - editor.chain().focus().setMind().run()}> - 思维导图 - - - editor.chain().focus().setKatex({ defaultShowPicker: true }).run()}> - 数学公式 - - - - 卡片 - - editor.chain().focus().setStatus({ defaultShowPicker: true }).run()}> - 状态 - - - editor.chain().focus().setCallout().run()}> - 高亮块 - - - - 文档 - - editor.chain().focus().setDocumentReference().run()}> - 文档 - - - editor.chain().focus().setDocumentChildren().run()}> - 子文档 - + {renderedCommands.map((command) => { + return command.title ? ( + {command.title} + ) : command.custom ? ( + command.custom(editor, runCommand) + ) : ( + + {command.icon} + {command.label} + + ); + })} } >