@lobehub/editor 4.15.2 → 4.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/es/editor-kernel/react/useDecorators.js +14 -8
  2. package/es/headless.d.ts +2 -0
  3. package/es/headless.js +710 -46
  4. package/es/index.d.ts +4 -3
  5. package/es/index.js +4 -3
  6. package/es/locale/index.d.ts +2 -0
  7. package/es/locale/index.js +5 -1
  8. package/es/plugins/auto-complete/plugin/index.js +3 -3
  9. package/es/plugins/block/index.d.ts +1 -1
  10. package/es/plugins/block/plugin/index.js +78 -2
  11. package/es/plugins/block/react/ReactBlockPlugin.js +172 -16
  12. package/es/plugins/block/react/drag/drag-utils.js +37 -10
  13. package/es/plugins/block/service/i-block-menu-service.d.ts +18 -1
  14. package/es/plugins/block/service/i-block-menu-service.js +24 -0
  15. package/es/plugins/block/service/index.d.ts +1 -1
  16. package/es/plugins/codeblock/plugin/index.js +25 -1
  17. package/es/plugins/common/plugin/register.js +2 -2
  18. package/es/plugins/litexml/plugin/index.js +8 -2
  19. package/es/plugins/slash/plugin/index.js +1 -1
  20. package/es/plugins/slash/react/ReactSlashPlugin.js +4 -4
  21. package/es/plugins/table/command/index.d.ts +13 -1
  22. package/es/plugins/table/command/index.js +220 -39
  23. package/es/plugins/table/index.d.ts +3 -2
  24. package/es/plugins/table/node/index.d.ts +2 -0
  25. package/es/plugins/table/node/index.js +130 -2
  26. package/es/plugins/table/plugin/index.d.ts +6 -0
  27. package/es/plugins/table/plugin/index.js +193 -4
  28. package/es/plugins/table/react/TableActionMenu/ActionMenu.js +82 -6
  29. package/es/plugins/table/react/TableActionMenu/index.js +9 -4
  30. package/es/plugins/table/react/TableColController.js +354 -0
  31. package/es/plugins/table/react/TableController/hooks.js +201 -0
  32. package/es/plugins/table/react/TableController/style.js +264 -0
  33. package/es/plugins/table/react/TableController/utils.js +25 -0
  34. package/es/plugins/table/react/TableControllerButton.js +81 -0
  35. package/es/plugins/table/react/TableControllerMenu.js +123 -0
  36. package/es/plugins/table/react/TableInsertButton.js +25 -0
  37. package/es/plugins/table/react/TableResize/index.js +153 -78
  38. package/es/plugins/table/react/TableRowController.js +349 -0
  39. package/es/plugins/table/react/hooks.js +77 -0
  40. package/es/plugins/table/react/index.js +139 -16
  41. package/es/plugins/table/react/style.js +89 -8
  42. package/es/plugins/table/react/type.d.ts +2 -0
  43. package/es/plugins/table/react/useAutoFitPastedTable.js +189 -0
  44. package/es/plugins/table/service/i-table-controller-menu-service.d.ts +44 -0
  45. package/es/plugins/table/service/i-table-controller-menu-service.js +31 -0
  46. package/es/plugins/table/service/index.d.ts +1 -0
  47. package/es/plugins/table/utils/autoFitColumnWidth.js +87 -0
  48. package/es/plugins/table/utils/distributeColumnWidth.js +37 -0
  49. package/es/plugins/table/utils/index.js +102 -2
  50. package/es/react/EditorProvider/index.d.ts +2 -2
  51. package/es/renderer/LexicalDiff.d.ts +2 -2
  52. package/package.json +1 -1
package/es/index.d.ts CHANGED
@@ -20,7 +20,7 @@ import { ReactAutoCompletePlugin } from "./plugins/auto-complete/react/ReactAuto
20
20
  import { BlockMovePayload, MOVE_BLOCK_COMMAND, registerBlockMoveCommand } from "./plugins/block/command/index.js";
21
21
  import { BlockPlugin, BlockPluginOptions } from "./plugins/block/plugin/index.js";
22
22
  import { BlockDragTarget } from "./plugins/block/react/core/types.js";
23
- import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService } from "./plugins/block/service/i-block-menu-service.js";
23
+ import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler } from "./plugins/block/service/i-block-menu-service.js";
24
24
  import { ReactBlockPlugin, ReactBlockPluginProps } from "./plugins/block/react/ReactBlockPlugin.js";
25
25
  import { INSERT_CODEINLINE_COMMAND } from "./plugins/code/command/index.js";
26
26
  import { CodePlugin } from "./plugins/code/plugin/index.js";
@@ -97,9 +97,10 @@ import { MenuRenderProps, ReactSlashOptionProps, ReactSlashPluginProps, SlashMen
97
97
  import { SlashMenu } from "./plugins/slash/react/components/SlashMenu.js";
98
98
  import { ReactSlashOption } from "./plugins/slash/react/ReactSlashOption.js";
99
99
  import { ReactSlashPlugin } from "./plugins/slash/react/ReactSlashPlugin.js";
100
- import { INSERT_TABLE_COMMAND, SELECT_TABLE_COMMAND } from "./plugins/table/command/index.js";
100
+ import { INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, SELECT_TABLE_COMMAND } from "./plugins/table/command/index.js";
101
101
  import { TablePlugin, TablePluginOptions } from "./plugins/table/plugin/index.js";
102
102
  import { ReactTablePlugin } from "./plugins/table/react/index.js";
103
+ import { ITableControllerMenuActionItem, ITableControllerMenuItem, ITableControllerMenuRenderContext, ITableControllerMenuSeparatorItem, ITableControllerMenuService, TableControllerMenuAxis, TableControllerMenuService } from "./plugins/table/service/i-table-controller-menu-service.js";
103
104
  import { HIDE_TOOLBAR_COMMAND, SHOW_TOOLBAR_COMMAND, ToolbarCommandOptions, registerToolbarCommand } from "./plugins/toolbar/command/index.js";
104
105
  import { ReactToolbarPlugin } from "./plugins/toolbar/react/index.js";
105
106
  import { UploadPlugin, UploadPluginOptions } from "./plugins/upload/plugin/index.js";
@@ -121,4 +122,4 @@ declare function enableHotReload(): void;
121
122
  */
122
123
  declare function disableHotReload(): void;
123
124
  //#endregion
124
- export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, BlockDragTarget, BlockMenuService, BlockMovePayload, BlockPlugin, BlockPluginOptions, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodeblockPluginOptions, CodemirrorPlugin, CodemirrorPluginOptions, CommonPlugin, CommonPluginOptions, ContentBlock, ContentBlocksDataSource, ContentBlocksPlugin, ContentBlocksPluginOptions, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, ExtractContentBlocksOptions, FileContentBlock, FileListItem, FilePlugin, FilePluginOptions, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HRPluginOptions, HeadlessDocumentType, HeadlessEditor, HeadlessEditorExport, HeadlessEditorExportOptions, HeadlessEditorHydrationInput, HeadlessEditorOptions, HeadlessLiteXMLBatchOperation, HeadlessLiteXMLInsertOperation, HeadlessLiteXMLOperation, HeadlessLiteXMLRemoveOperation, HeadlessLiteXMLReplaceOperation, HotkeyEnum, HotkeyId, HotkeyItem, HotkeyScopeEnum, HotkeyScopeId, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, type IEditor, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodePluginOptions, INodeService, ISlashMenuOption, ISlashOption, ISlashService, ITriggerContext, IUploadService, ImageContentBlock, ImageListItem, ImagePlugin, ImagePluginOptions, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkHighlightPluginOptions, LinkPlugin, LinkPluginOptions, ListPlugin, ListPluginOptions, LitexmlDataSource, LitexmlPlugin, LitexmlPluginOptions, LitexmlService, MARKDOWN_READER_LEVEL, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MediaLists, MentionPlugin, MentionPluginOptions, MenuRenderProps, ModifierCombination, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactBlockPluginProps, ReactCodePlugin, ReactCodeblockPlugin, ReactCodeblockPluginProps, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactEditorContentProps, ReactFilePlugin, ReactFilePluginProps, ReactHRPlugin, ReactHRPluginProps, ReactImagePlugin, ReactImagePluginProps, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactLinkPluginProps, ReactListPlugin, ReactListPluginProps, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactMentionPluginProps, ReactNodePlugin, ReactPlainText, ReactPlainTextProps, ReactSlashOption, ReactSlashOptionProps, ReactSlashPlugin, ReactSlashPluginProps, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SerializedMentionNode, SlashMenu, SlashMenuProps, SlashOptions, SlashPlugin, SlashPluginOptions, TablePlugin, TablePluginOptions, TextContentBlock, ToolbarCommandOptions, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, UploadPluginOptions, VirtualBlockPlugin, VirtualBlockPluginOptions, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
125
+ export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, BlockDragTarget, BlockMenuService, BlockMovePayload, BlockPlugin, BlockPluginOptions, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodeblockPluginOptions, CodemirrorPlugin, CodemirrorPluginOptions, CommonPlugin, CommonPluginOptions, ContentBlock, ContentBlocksDataSource, ContentBlocksPlugin, ContentBlocksPluginOptions, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, ExtractContentBlocksOptions, FileContentBlock, FileListItem, FilePlugin, FilePluginOptions, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HRPluginOptions, HeadlessDocumentType, HeadlessEditor, HeadlessEditorExport, HeadlessEditorExportOptions, HeadlessEditorHydrationInput, HeadlessEditorOptions, HeadlessLiteXMLBatchOperation, HeadlessLiteXMLInsertOperation, HeadlessLiteXMLOperation, HeadlessLiteXMLRemoveOperation, HeadlessLiteXMLReplaceOperation, HotkeyEnum, HotkeyId, HotkeyItem, HotkeyScopeEnum, HotkeyScopeId, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler, type IEditor, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodePluginOptions, INodeService, ISlashMenuOption, ISlashOption, ISlashService, ITableControllerMenuActionItem, ITableControllerMenuItem, ITableControllerMenuRenderContext, ITableControllerMenuSeparatorItem, ITableControllerMenuService, ITriggerContext, IUploadService, ImageContentBlock, ImageListItem, ImagePlugin, ImagePluginOptions, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkHighlightPluginOptions, LinkPlugin, LinkPluginOptions, ListPlugin, ListPluginOptions, LitexmlDataSource, LitexmlPlugin, LitexmlPluginOptions, LitexmlService, MARKDOWN_READER_LEVEL, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MediaLists, MentionPlugin, MentionPluginOptions, MenuRenderProps, ModifierCombination, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactBlockPluginProps, ReactCodePlugin, ReactCodeblockPlugin, ReactCodeblockPluginProps, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactEditorContentProps, ReactFilePlugin, ReactFilePluginProps, ReactHRPlugin, ReactHRPluginProps, ReactImagePlugin, ReactImagePluginProps, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactLinkPluginProps, ReactListPlugin, ReactListPluginProps, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactMentionPluginProps, ReactNodePlugin, ReactPlainText, ReactPlainTextProps, ReactSlashOption, ReactSlashOptionProps, ReactSlashPlugin, ReactSlashPluginProps, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SerializedMentionNode, SlashMenu, SlashMenuProps, SlashOptions, SlashPlugin, SlashPluginOptions, TableControllerMenuAxis, TableControllerMenuService, TablePlugin, TablePluginOptions, TextContentBlock, ToolbarCommandOptions, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, UploadPluginOptions, VirtualBlockPlugin, VirtualBlockPluginOptions, XMLReaderFunc, XMLReaderRecord, XMLWriterFunc, XMLWriterRecord, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
package/es/index.js CHANGED
@@ -43,14 +43,15 @@ import { ILinkService } from "./plugins/link/service/i-link-service.js";
43
43
  import { LinkPlugin } from "./plugins/link/plugin/index.js";
44
44
  import { INSERT_CHECK_LIST_COMMAND } from "./plugins/list/plugin/checkList.js";
45
45
  import { ListPlugin } from "./plugins/list/plugin/index.js";
46
- import { INSERT_TABLE_COMMAND, SELECT_TABLE_COMMAND } from "./plugins/table/command/index.js";
46
+ import { BlockMenuService, IBlockMenuService } from "./plugins/block/service/i-block-menu-service.js";
47
+ import { INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, SELECT_TABLE_COMMAND } from "./plugins/table/command/index.js";
48
+ import { ITableControllerMenuService, TableControllerMenuService } from "./plugins/table/service/i-table-controller-menu-service.js";
47
49
  import { TablePlugin } from "./plugins/table/plugin/index.js";
48
50
  import { extractMediaFromEditorState } from "./headless/extract-media-from-editor-state.js";
49
51
  import { DEFAULT_HEADLESS_EDITOR_PLUGINS, HeadlessEditor, createHeadlessEditor } from "./headless/index.js";
50
52
  import { AutoCompletePlugin } from "./plugins/auto-complete/plugin/index.js";
51
53
  import ReactAutoCompletePlugin from "./plugins/auto-complete/react/ReactAutoCompletePlugin.js";
52
54
  import { MOVE_BLOCK_COMMAND, registerBlockMoveCommand } from "./plugins/block/command/index.js";
53
- import { BlockMenuService, IBlockMenuService } from "./plugins/block/service/i-block-menu-service.js";
54
55
  import { BlockPlugin } from "./plugins/block/plugin/index.js";
55
56
  import ReactBlockPlugin from "./plugins/block/react/ReactBlockPlugin.js";
56
57
  import ReactCodePlugin from "./plugins/code/react/CodeReactPlugin.js";
@@ -126,4 +127,4 @@ function disableHotReload() {
126
127
  }
127
128
  }
128
129
  //#endregion
129
- export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, BlockMenuService, BlockPlugin, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodemirrorPlugin, CommonPlugin, ContentBlocksDataSource, ContentBlocksPlugin, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, FilePlugin, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HeadlessEditor, HotkeyEnum, HotkeyScopeEnum, IBlockMenuService, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodeService, IUploadService, ImagePlugin, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkPlugin, ListPlugin, LitexmlDataSource, LitexmlPlugin, LitexmlService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MentionPlugin, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactCodePlugin, ReactCodeblockPlugin, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactFilePlugin, ReactHRPlugin, ReactImagePlugin, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactListPlugin, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactNodePlugin, ReactPlainText, ReactSlashOption, ReactSlashPlugin, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SlashMenu, SlashPlugin, TablePlugin, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, VirtualBlockPlugin, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
130
+ export { $closest, $closestNodeType, $createCursorNode, $getNearestNodeFromDOMNode, $getNodeFromDOMNode, $isCardLikeElementNode, $isCursorNode, AutoCompletePlugin, BlockMenuService, BlockPlugin, CONTENT_BLOCKS_DATA_TYPE, CardLikeElementNode, CodePlugin, CodeblockPlugin, CodemirrorPlugin, CommonPlugin, ContentBlocksDataSource, ContentBlocksPlugin, DEFAULT_HEADLESS_EDITOR_PLUGINS, DOM_DOCUMENT_FRAGMENT_TYPE, DOM_DOCUMENT_TYPE, DOM_ELEMENT_TYPE, DOM_TEXT_TYPE, DataSource, DiffAction, EDITOR_THEME_KEY, FilePlugin, GET_MARKDOWN_SELECTION_COMMAND, HIDE_TOOLBAR_COMMAND, HOVER_COMMAND, HRPlugin, HeadlessEditor, HotkeyEnum, HotkeyScopeEnum, IBlockMenuService, ILinkService, ILitexmlService, IMarkdownShortCutService, INSERT_CHECK_LIST_COMMAND, INSERT_CODEINLINE_COMMAND, INSERT_CODEMIRROR_COMMAND, INSERT_FILE_COMMAND, INSERT_HEADING_COMMAND, INSERT_HORIZONTAL_RULE_COMMAND, INSERT_IMAGE_COMMAND, INSERT_LINK_COMMAND, INSERT_LINK_HIGHLIGHT_COMMAND, INSERT_MARKDOWN_COMMAND, INSERT_MATH_COMMAND, INSERT_MENTION_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_QUOTE_COMMAND, INSERT_TABLE_COLUMN_COMMAND, INSERT_TABLE_COMMAND, INSERT_TABLE_ROW_COMMAND, INSERT_UNORDERED_LIST_COMMAND, INodePlugin, INodeService, ITableControllerMenuService, IUploadService, ImagePlugin, Kernel, KeyEnum, LITEXML_APPLY_COMMAND, LITEXML_DIFFNODE_ALL_COMMAND, LITEXML_DIFFNODE_COMMAND, LITEXML_INSERT_COMMAND, LITEXML_MODIFY_COMMAND, LITEXML_REMOVE_COMMAND, LexicalErrorBoundary, LexicalPortalContainer, LinkHighlightPlugin, LinkPlugin, ListPlugin, LitexmlDataSource, LitexmlPlugin, LitexmlService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, MOVE_BLOCK_COMMAND, MarkdownPlugin, MathPlugin, MentionPlugin, REMOVE_LIST_COMMAND, ReactAutoCompletePlugin, ReactBlockPlugin, ReactCodePlugin, ReactCodeblockPlugin, ReactCodemirrorPlugin, ReactEditor, ReactEditorContent, ReactFilePlugin, ReactHRPlugin, ReactImagePlugin, ReactLinkHighlightPlugin, ReactLinkPlugin, ReactListPlugin, ReactLiteXmlPlugin, ReactMarkdownPlugin, ReactMathPlugin, ReactMentionPlugin, ReactNodePlugin, ReactPlainText, ReactSlashOption, ReactSlashPlugin, ReactTablePlugin, ReactToolbarPlugin, ReactVirtualBlockPlugin, SELECT_AFTER_CODEMIRROR_COMMAND, SELECT_BEFORE_CODEMIRROR_COMMAND, SELECT_TABLE_COMMAND, SHOW_TOOLBAR_COMMAND, SlashMenu, SlashPlugin, TableControllerMenuService, TablePlugin, UPDATE_CODEBLOCK_LANG, UPDATE_LIST_START_COMMAND, UPLOAD_PRIORITY_HIGH, UPLOAD_PRIORITY_LOW, UPLOAD_PRIORITY_MEDIUM, UploadPlugin, VirtualBlockPlugin, assert, browserDebug, bundledLanguagesInfo, compareNodeOrder, createDebugLogger, createEmptyEditorState, createHeadlessEditor, cursorNodeSerialized, debugLogger, debugLoggers, detectCodeLanguage, detectLanguage, devConsole, disableHotReload, enableHotReload, extractContentBlocks, extractMediaFromEditorState, extractMediaLists, genServiceId, generateEditorId, getHotkeyById, getKernelFromEditor, getKernelFromEditorConfig, getNodeKeyFromDOMNode, getParentElement, isDOMNode, isDocumentFragment, isPunctuationChar, isPureUrl, isValidUrl, moment, noop, prodSafeLogger, reconcileDecorator, registerBlockMoveCommand, registerEditorKernel, registerLinkHighlightCommand, registerToolbarCommand, resetRandomKey, scrollIntoView, unregisterEditorKernel, useHasDiffNode, useLexicalComposerContext, useLexicalEditor };
@@ -1,7 +1,9 @@
1
1
  //#region src/locale/index.d.ts
2
2
  declare const _default: {
3
3
  block: {
4
+ copy: string;
4
5
  delete: string;
6
+ select: string;
5
7
  };
6
8
  cancel: string;
7
9
  codemirror: {
@@ -3,7 +3,11 @@ import { __esmMin } from "../_virtual/_rolldown/runtime.js";
3
3
  var locale_default;
4
4
  var init_locale = __esmMin((() => {
5
5
  locale_default = {
6
- block: { delete: "Delete block" },
6
+ block: {
7
+ copy: "Copy",
8
+ delete: "Delete block",
9
+ select: "Select"
10
+ },
7
11
  cancel: "Cancel",
8
12
  codemirror: {
9
13
  copySuccess: "Code copied to clipboard",
@@ -2,7 +2,7 @@ import { createDebugLogger, init_debug } from "../../../utils/debug.js";
2
2
  import { KernelPlugin, init_plugin } from "../../../editor-kernel/plugin.js";
3
3
  import { IMarkdownShortCutService } from "../../markdown/service/shortcut.js";
4
4
  import { PlaceholderBlockNode, PlaceholderNode } from "../node/placeholderNode.js";
5
- import { $getSelection, $isRangeSelection, $nodesOfType, $setSelection, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_HIGH, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND } from "lexical";
5
+ import { $getSelection, $isRangeSelection, $nodesOfType, $setSelection, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_HIGH, HISTORIC_TAG, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND } from "lexical";
6
6
  //#region src/plugins/auto-complete/plugin/index.ts
7
7
  init_plugin();
8
8
  init_debug();
@@ -337,7 +337,7 @@ const AutoCompletePlugin = class extends KernelPlugin {
337
337
  this.placeholderSelectionSnapshot = saveSel;
338
338
  this.markdownService.insertIRootNode(editor, nodes, selection);
339
339
  $setSelection(saveSel);
340
- });
340
+ }, { tag: HISTORIC_TAG });
341
341
  }
342
342
  clearPlaceholderNodes(editor, options) {
343
343
  const shouldRestoreSelection = options?.restoreSelection ?? true;
@@ -361,7 +361,7 @@ const AutoCompletePlugin = class extends KernelPlugin {
361
361
  for (const node of $nodesOfType(PlaceholderNode)) node.remove();
362
362
  for (const node of $nodesOfType(PlaceholderBlockNode)) node.remove();
363
363
  if (shouldRestoreSelection && restoreSelection) $setSelection(restoreSelection);
364
- });
364
+ }, { tag: HISTORIC_TAG });
365
365
  }
366
366
  applySuggestion(editor) {
367
367
  if (!this.currentSuggestion) return;
@@ -1,5 +1,5 @@
1
1
  import { BlockMovePayload, MOVE_BLOCK_COMMAND, registerBlockMoveCommand } from "./command/index.js";
2
2
  import { BlockPlugin, BlockPluginOptions } from "./plugin/index.js";
3
3
  import { BlockDragTarget } from "./react/core/types.js";
4
- import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService } from "./service/i-block-menu-service.js";
4
+ import { BlockMenuService, IBlockActionButton, IBlockActionButtonIcon, IBlockMenuItem, IBlockMenuRenderContext, IBlockMenuService, IBlockSelectHandler } from "./service/i-block-menu-service.js";
5
5
  import { ReactBlockPlugin, ReactBlockPluginProps } from "./react/ReactBlockPlugin.js";
@@ -1,10 +1,48 @@
1
1
  import { KernelPlugin, init_plugin } from "../../../editor-kernel/plugin.js";
2
- import { registerBlockMoveCommand } from "../command/index.js";
3
2
  import { BlockMenuService, IBlockMenuService } from "../service/i-block-menu-service.js";
4
- import { $getNodeByKey, ParagraphNode } from "lexical";
3
+ import { registerBlockMoveCommand } from "../command/index.js";
4
+ import { $createNodeSelection, $createRangeSelection, $getNodeByKey, $isElementNode, $isTextNode, $setSelection, ParagraphNode } from "lexical";
5
+ import { $getClipboardDataFromSelection, copyToClipboard } from "@lexical/clipboard";
5
6
  //#region src/plugins/block/plugin/index.ts
6
7
  init_plugin();
7
8
  const PATCHED_NODE_TYPES = /* @__PURE__ */ new Set();
9
+ const getBlockClipboardData = (node) => {
10
+ if ($isElementNode(node)) {
11
+ const selection = $createRangeSelection();
12
+ const parent = node.getParent();
13
+ if (parent) {
14
+ const index = node.getIndexWithinParent();
15
+ selection.anchor.set(parent.getKey(), index, "element");
16
+ selection.focus.set(parent.getKey(), index + 1, "element");
17
+ } else {
18
+ selection.anchor.set(node.getKey(), 0, "element");
19
+ selection.focus.set(node.getKey(), node.getChildrenSize(), "element");
20
+ }
21
+ return $getClipboardDataFromSelection(selection);
22
+ }
23
+ if ($isTextNode(node)) {
24
+ const selection = $createRangeSelection();
25
+ selection.setTextNodeRange(node, 0, node, node.getTextContentSize());
26
+ return $getClipboardDataFromSelection(selection);
27
+ }
28
+ const selection = $createNodeSelection();
29
+ selection.add(node.getKey());
30
+ return $getClipboardDataFromSelection(selection);
31
+ };
32
+ const selectBlockNode = (node) => {
33
+ if ($isElementNode(node)) {
34
+ node.select(0, node.getChildrenSize());
35
+ return true;
36
+ }
37
+ if ($isTextNode(node)) {
38
+ node.select(0, node.getTextContentSize());
39
+ return true;
40
+ }
41
+ const selection = $createNodeSelection();
42
+ selection.add(node.getKey());
43
+ $setSelection(selection);
44
+ return true;
45
+ };
8
46
  const resolveNodeClass = (node) => {
9
47
  if (typeof node === "function") return node;
10
48
  if (typeof node === "object" && node && typeof node.replace === "function") return node.replace;
@@ -63,6 +101,41 @@ const BlockPlugin = class extends KernelPlugin {
63
101
  onInit(editor) {
64
102
  const blockMenuService = this.kernel.requireService(IBlockMenuService);
65
103
  if (blockMenuService) {
104
+ const unregisterDefaultSelectHandler = blockMenuService.registerSelectHandler({
105
+ key: "__block_default_select_handler",
106
+ onSelect: selectBlockNode,
107
+ order: 999
108
+ });
109
+ const unregisterCopyMenu = blockMenuService.registerMenu({
110
+ key: "__block_default_copy",
111
+ label: (context) => context.editor.t("block.copy"),
112
+ onClick: (context) => {
113
+ const lexicalEditor = context.editor.getLexicalEditor();
114
+ if (!lexicalEditor) return;
115
+ let clipboardData;
116
+ lexicalEditor.read(() => {
117
+ const target = $getNodeByKey(context.blockId);
118
+ if (!target) return;
119
+ clipboardData = getBlockClipboardData(target);
120
+ });
121
+ if (clipboardData) copyToClipboard(lexicalEditor, null, clipboardData);
122
+ },
123
+ order: 998
124
+ });
125
+ const unregisterSelectMenu = blockMenuService.registerMenu({
126
+ key: "__block_default_select",
127
+ label: (context) => context.editor.t("block.select"),
128
+ onClick: (context) => {
129
+ const lexicalEditor = context.editor.getLexicalEditor();
130
+ if (!lexicalEditor) return;
131
+ lexicalEditor.update(() => {
132
+ const target = $getNodeByKey(context.blockId);
133
+ if (!target) return;
134
+ blockMenuService.selectNode(target);
135
+ });
136
+ },
137
+ order: 997
138
+ });
66
139
  const unregisterDeleteMenu = blockMenuService.registerMenu({
67
140
  key: "__block_default_delete",
68
141
  label: (context) => context.editor.t("block.delete"),
@@ -77,6 +150,9 @@ const BlockPlugin = class extends KernelPlugin {
77
150
  },
78
151
  order: 999
79
152
  });
153
+ this.register(unregisterDefaultSelectHandler);
154
+ this.register(unregisterCopyMenu);
155
+ this.register(unregisterSelectMenu);
80
156
  this.register(unregisterDeleteMenu);
81
157
  }
82
158
  this.register(registerBlockMoveCommand(editor));
@@ -5,7 +5,7 @@ import { useLexicalEditor } from "../../../editor-kernel/react/useLexicalEditor.
5
5
  import { IBlockMenuService } from "../service/i-block-menu-service.js";
6
6
  import { BlockPlugin } from "../plugin/index.js";
7
7
  import { createRuntimeContext } from "./core/runtime-context.js";
8
- import { collectDragBlocks } from "./drag/drag-utils.js";
8
+ import { collectDragBlocks, getBlockMeasureRect, getTableBlockRect, isTableBlockElement } from "./drag/drag-utils.js";
9
9
  import { startBlockDragSession } from "./drag/drag-session.js";
10
10
  import { styles } from "./style.js";
11
11
  import { Icon } from "@lobehub/ui";
@@ -14,11 +14,22 @@ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
14
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
15
  import { cx } from "antd-style";
16
16
  import { Button as Button$1, Dropdown as Dropdown$1, theme } from "antd";
17
+ import { $findTableNode, $isTableSelection } from "@lexical/table";
18
+ import { $getNodeByKey, $getSelection, $isRangeSelection } from "lexical";
17
19
  import { createPortal } from "react-dom";
18
20
  //#region src/plugins/block/react/ReactBlockPlugin.tsx
19
21
  init_debug();
20
22
  const logger = createDebugLogger("plugin", "block-react");
21
23
  const OPERATION_MENU_OVERLAY_CLASS = "lobe-block-operation-dropdown";
24
+ const TABLE_FOCUSED_MENU_OFFSET = 8;
25
+ const getTableMenuAnchorRect = (element) => {
26
+ const rect = getTableBlockRect(element);
27
+ if (!rect) return null;
28
+ return {
29
+ left: rect.left,
30
+ top: rect.top
31
+ };
32
+ };
22
33
  const ReactBlockPlugin = (props) => {
23
34
  const { token } = theme.useToken();
24
35
  const [editor] = useLexicalComposerContext();
@@ -28,6 +39,7 @@ const ReactBlockPlugin = (props) => {
28
39
  const dragLayerRef = useRef(null);
29
40
  const contextRef = useRef(createRuntimeContext());
30
41
  const [hoveredBlock, setHoveredBlock] = useState(null);
42
+ const [layoutVersion, setLayoutVersion] = useState(0);
31
43
  const [menuVersion, setMenuVersion] = useState(0);
32
44
  const [menuPosition, setMenuPosition] = useState({});
33
45
  const [operationMenuOpen, setOperationMenuOpen] = useState(false);
@@ -35,7 +47,9 @@ const ReactBlockPlugin = (props) => {
35
47
  const [dragIndicator, setDragIndicator] = useState(null);
36
48
  const [dragLayerContainer, setDragLayerContainer] = useState(null);
37
49
  const [isDragging, setIsDragging] = useState(false);
50
+ const [focusedTableBlockId, setFocusedTableBlockId] = useState(null);
38
51
  const [blockMenuService, setBlockMenuService] = useState(null);
52
+ const blockMenuSuppressed = blockMenuService?.isMenuSuppressed() ?? false;
39
53
  useLayoutEffect(() => {
40
54
  if (locale) editor.registerLocale(locale);
41
55
  editor.registerPlugin(BlockPlugin, {
@@ -68,6 +82,30 @@ const ReactBlockPlugin = (props) => {
68
82
  setMenuVersion((v) => v + 1);
69
83
  });
70
84
  }, [blockMenuService]);
85
+ useEffect(() => {
86
+ if (!blockMenuSuppressed) return;
87
+ setHoveredBlock(null);
88
+ setOperationMenuOpen(false);
89
+ setOperationMenuContext(null);
90
+ contextRef.current.operationMenuAnchorBlockId = null;
91
+ }, [blockMenuSuppressed]);
92
+ useLexicalEditor((lexicalEditor) => {
93
+ return lexicalEditor.registerUpdateListener(({ editorState }) => {
94
+ editorState.read(() => {
95
+ const selection = $getSelection();
96
+ if ($isTableSelection(selection)) {
97
+ setFocusedTableBlockId(selection.tableKey);
98
+ return;
99
+ }
100
+ if ($isRangeSelection(selection)) {
101
+ const anchorNode = $getNodeByKey(selection.anchor.key);
102
+ setFocusedTableBlockId((anchorNode ? $findTableNode(anchorNode) : null)?.getKey() ?? null);
103
+ return;
104
+ }
105
+ setFocusedTableBlockId(null);
106
+ });
107
+ });
108
+ }, [editor]);
71
109
  useEffect(() => {
72
110
  return () => {
73
111
  contextRef.current.dragCleanup?.();
@@ -134,20 +172,31 @@ const ReactBlockPlugin = (props) => {
134
172
  if (paddingLeft <= 0) return false;
135
173
  return clientX <= rootRect.left + paddingLeft;
136
174
  };
175
+ const resolveCurrentBlockElement = (root, blockId) => {
176
+ const currentBlock = Array.from(root.querySelectorAll("[data-block-id]")).find((element) => element.dataset.blockId === blockId);
177
+ return currentBlock && root.contains(currentBlock) ? currentBlock : null;
178
+ };
137
179
  const getHoveredBlock = (target, clientX, clientY) => {
138
180
  const root = editor.getRootElement();
139
181
  if (!root || !(target instanceof Node)) return null;
140
182
  const targetElement = target instanceof Element ? target : target.parentElement;
141
183
  if (!targetElement) return null;
142
- if (menuRef.current?.contains(targetElement)) return contextRef.current.hoveredBlock;
143
- if (targetElement.closest(`.${OPERATION_MENU_OVERLAY_CLASS}`)) return contextRef.current.hoveredBlock;
184
+ if (menuRef.current?.contains(targetElement)) return contextRef.current.hoveredBlock ? {
185
+ ...contextRef.current.hoveredBlock,
186
+ source: "existing"
187
+ } : null;
188
+ if (targetElement.closest(`.${OPERATION_MENU_OVERLAY_CLASS}`)) return contextRef.current.hoveredBlock ? {
189
+ ...contextRef.current.hoveredBlock,
190
+ source: "existing"
191
+ } : null;
144
192
  const blockElement = targetElement.closest("[data-block-id]");
145
193
  if (blockElement instanceof HTMLElement && root.contains(blockElement)) {
146
194
  const blockId = blockElement.dataset.blockId;
147
195
  if (!blockId) return null;
148
196
  return {
149
197
  blockElement,
150
- blockId
198
+ blockId,
199
+ source: "direct"
151
200
  };
152
201
  }
153
202
  if (!root.contains(targetElement)) return null;
@@ -156,12 +205,19 @@ const ReactBlockPlugin = (props) => {
156
205
  if (rects.length === 0) return null;
157
206
  const entry = resolveBlockByY(rects, clientY);
158
207
  if (!entry) return null;
208
+ const currentBlockElement = resolveCurrentBlockElement(root, entry.blockId);
209
+ if (!currentBlockElement) {
210
+ markBlockRectsDirty();
211
+ return null;
212
+ }
159
213
  return {
160
- blockElement: entry.block,
161
- blockId: entry.blockId
214
+ blockElement: currentBlockElement,
215
+ blockId: entry.blockId,
216
+ source: "padding"
162
217
  };
163
218
  };
164
219
  const processHover = () => {
220
+ if (blockMenuSuppressed) return;
165
221
  if (!latestPointer) return;
166
222
  if (contextRef.current.draggingSource) return;
167
223
  const { clientX, clientY, target } = latestPointer;
@@ -173,7 +229,10 @@ const ReactBlockPlugin = (props) => {
173
229
  }
174
230
  setHoveredBlock((current) => {
175
231
  if (!current) return next;
176
- if (next.blockId === current.blockId && next.blockElement === current.blockElement) return current;
232
+ if (next.blockId === current.blockId && next.blockElement === current.blockElement) {
233
+ if (next.source === "padding") setLayoutVersion((version) => version + 1);
234
+ return current;
235
+ }
177
236
  return next;
178
237
  });
179
238
  return;
@@ -192,6 +251,7 @@ const ReactBlockPlugin = (props) => {
192
251
  });
193
252
  };
194
253
  const handleMouseMove = (event) => {
254
+ if (blockMenuSuppressed) return;
195
255
  const root = editor.getRootElement();
196
256
  const target = event.target;
197
257
  if (!root || !(target instanceof Node)) {
@@ -216,25 +276,97 @@ const ReactBlockPlugin = (props) => {
216
276
  };
217
277
  const handleViewportChange = () => {
218
278
  markBlockRectsDirty();
279
+ if (blockMenuSuppressed) return;
219
280
  scheduleHoverProcess();
220
281
  };
282
+ const clearMenuState = () => {
283
+ if (contextRef.current.hideTimer !== null) {
284
+ window.clearTimeout(contextRef.current.hideTimer);
285
+ contextRef.current.hideTimer = null;
286
+ }
287
+ latestPointer = null;
288
+ contextRef.current.operationMenuAnchorBlockId = null;
289
+ setHoveredBlock(null);
290
+ setOperationMenuOpen(false);
291
+ setOperationMenuContext(null);
292
+ };
293
+ const isInsideMenu = (target) => {
294
+ if (!(target instanceof Element)) return false;
295
+ return Boolean(menuRef.current?.contains(target)) || Boolean(target.closest(`.${OPERATION_MENU_OVERLAY_CLASS}`));
296
+ };
297
+ const handlePointerDown = (event) => {
298
+ const rootElement = editor.getRootElement();
299
+ const target = event.target;
300
+ if (!(target instanceof Node)) {
301
+ clearMenuState();
302
+ return;
303
+ }
304
+ if (rootElement?.contains(target) || isInsideMenu(target)) return;
305
+ clearMenuState();
306
+ };
307
+ const handleFocusOut = () => {
308
+ window.requestAnimationFrame(() => {
309
+ const rootElement = editor.getRootElement();
310
+ const activeElement = rootElement?.ownerDocument.activeElement;
311
+ if (!activeElement) {
312
+ clearMenuState();
313
+ return;
314
+ }
315
+ if (rootElement?.contains(activeElement) || isInsideMenu(activeElement)) return;
316
+ clearMenuState();
317
+ });
318
+ };
221
319
  const rootResizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
222
320
  markBlockRectsDirty();
321
+ setLayoutVersion((version) => version + 1);
322
+ if (blockMenuSuppressed) return;
323
+ scheduleHoverProcess();
223
324
  }) : null;
224
325
  const root = editor.getRootElement();
326
+ const rootMutationObserver = root && typeof MutationObserver !== "undefined" ? new MutationObserver(() => {
327
+ markBlockRectsDirty();
328
+ setLayoutVersion((version) => version + 1);
329
+ if (blockMenuSuppressed) return;
330
+ scheduleHoverProcess();
331
+ }) : null;
225
332
  const unregisterUpdate = editor.getLexicalEditor()?.registerUpdateListener(() => {
226
333
  markBlockRectsDirty();
334
+ if (blockMenuSuppressed) {
335
+ setHoveredBlock(null);
336
+ setLayoutVersion((version) => version + 1);
337
+ return;
338
+ }
339
+ setHoveredBlock((current) => {
340
+ if (!current) return current;
341
+ const currentRoot = editor.getRootElement();
342
+ const currentBlockId = current.blockElement.dataset.blockId;
343
+ if (!currentRoot || !current.blockElement.isConnected || !currentRoot.contains(current.blockElement) || currentBlockId !== current.blockId) return null;
344
+ return current;
345
+ });
346
+ setLayoutVersion((version) => version + 1);
227
347
  scheduleHoverProcess();
228
348
  }) || (() => {});
229
- if (root) rootResizeObserver?.observe(root);
349
+ if (root) {
350
+ rootResizeObserver?.observe(root);
351
+ rootMutationObserver?.observe(root, {
352
+ attributes: true,
353
+ childList: true,
354
+ subtree: true
355
+ });
356
+ }
230
357
  document.addEventListener("mousemove", handleMouseMove, true);
358
+ document.addEventListener("pointerdown", handlePointerDown, true);
231
359
  window.addEventListener("resize", handleViewportChange);
232
360
  document.addEventListener("scroll", handleViewportChange, true);
361
+ root?.addEventListener("focusout", handleFocusOut);
233
362
  return () => {
234
363
  document.removeEventListener("mousemove", handleMouseMove, true);
364
+ document.removeEventListener("pointerdown", handlePointerDown, true);
235
365
  window.removeEventListener("resize", handleViewportChange);
236
366
  document.removeEventListener("scroll", handleViewportChange, true);
367
+ root?.removeEventListener("focusout", handleFocusOut);
237
368
  rootResizeObserver?.disconnect();
369
+ rootMutationObserver?.disconnect();
238
370
  unregisterUpdate();
239
371
  if (hoverRaf !== null) {
240
372
  window.cancelAnimationFrame(hoverRaf);
@@ -245,21 +377,39 @@ const ReactBlockPlugin = (props) => {
245
377
  contextRef.current.hideTimer = null;
246
378
  }
247
379
  };
248
- }, [editor, isDragging]);
249
- useEffect(() => {
380
+ }, [
381
+ blockMenuSuppressed,
382
+ editor,
383
+ isDragging
384
+ ]);
385
+ useLayoutEffect(() => {
250
386
  if (!hoveredBlock) {
251
387
  if (operationMenuOpen) return;
252
388
  setMenuPosition({});
253
389
  return;
254
390
  }
255
391
  const updateMenuPosition = () => {
256
- const rect = hoveredBlock.blockElement.getBoundingClientRect();
392
+ const blockRect = getBlockMeasureRect(hoveredBlock.blockElement);
393
+ if (!blockRect) {
394
+ setMenuPosition({});
395
+ return;
396
+ }
257
397
  const menuWidth = menuRef.current?.offsetWidth || 32;
258
398
  const gap = 8;
259
399
  const listItemOffset = hoveredBlock.blockElement.tagName === "LI" ? 16 : 0;
400
+ const isTableBlock = isTableBlockElement(hoveredBlock.blockElement);
401
+ const tableMenuOffset = focusedTableBlockId === hoveredBlock.blockId && isTableBlock ? TABLE_FOCUSED_MENU_OFFSET : 0;
402
+ const tableAnchorRect = isTableBlock ? getTableMenuAnchorRect(hoveredBlock.blockElement) : null;
403
+ const root = editor.getRootElement();
404
+ const rootRect = root?.getBoundingClientRect();
405
+ const rootPaddingLeft = root ? Number.parseFloat(window.getComputedStyle(root).paddingLeft || "0") : 0;
406
+ const minTableLeft = rootRect ? rootRect.left + rootPaddingLeft : gap;
407
+ const anchorLeft = isTableBlock && tableAnchorRect ? Math.max(tableAnchorRect.left, minTableLeft) : blockRect.left;
408
+ const rawAnchorTop = tableAnchorRect?.top ?? blockRect.top;
409
+ const anchorTop = rawAnchorTop >= blockRect.top - 1 && rawAnchorTop <= blockRect.bottom + 1 ? rawAnchorTop : blockRect.top;
260
410
  setMenuPosition({
261
- left: Math.max(gap, rect.left - menuWidth - gap - listItemOffset),
262
- top: rect.top
411
+ left: Math.max(gap, anchorLeft - menuWidth - gap - listItemOffset - tableMenuOffset),
412
+ top: anchorTop
263
413
  });
264
414
  };
265
415
  updateMenuPosition();
@@ -269,7 +419,13 @@ const ReactBlockPlugin = (props) => {
269
419
  window.removeEventListener("resize", updateMenuPosition);
270
420
  document.removeEventListener("scroll", updateMenuPosition, true);
271
421
  };
272
- }, [hoveredBlock]);
422
+ }, [
423
+ editor,
424
+ focusedTableBlockId,
425
+ hoveredBlock,
426
+ layoutVersion,
427
+ operationMenuOpen
428
+ ]);
273
429
  const menuContext = useMemo(() => {
274
430
  if (!hoveredBlock) return null;
275
431
  return {
@@ -385,8 +541,8 @@ const ReactBlockPlugin = (props) => {
385
541
  setOperationMenuContext(null);
386
542
  }
387
543
  })), [operationMenus, operationMenuContext]);
388
- const shouldRenderPortal = menuContext || dragIndicator;
389
- const menuNode = menuContext && !isDragging ? /* @__PURE__ */ jsx("div", {
544
+ const shouldRenderPortal = !blockMenuSuppressed && menuContext || dragIndicator;
545
+ const menuNode = menuContext && !isDragging && !blockMenuSuppressed ? /* @__PURE__ */ jsx("div", {
390
546
  className: styles.menu,
391
547
  ref: menuRef,
392
548
  style: menuPosition,
@@ -1,21 +1,48 @@
1
1
  //#region src/plugins/block/react/drag/drag-utils.ts
2
+ const toRectSnapshot = (rect) => ({
3
+ bottom: rect.bottom,
4
+ height: rect.height,
5
+ left: rect.left,
6
+ top: rect.top,
7
+ width: rect.width
8
+ });
9
+ const isTableBlockElement = (element) => {
10
+ return element instanceof HTMLTableElement || Boolean(element.querySelector("table.editor_table, table"));
11
+ };
12
+ const getTableBlockRect = (element) => {
13
+ const table = element instanceof HTMLTableElement ? element : element.querySelector("table.editor_table, table");
14
+ if (!(table instanceof HTMLElement)) return null;
15
+ const tableRect = table.getBoundingClientRect();
16
+ const firstCell = table.querySelector("th, td");
17
+ if (firstCell instanceof HTMLElement) {
18
+ const cellRect = firstCell.getBoundingClientRect();
19
+ if (cellRect.width > 0 && cellRect.height > 0) return {
20
+ bottom: tableRect.height > 0 ? tableRect.bottom : cellRect.bottom,
21
+ height: tableRect.height > 0 ? tableRect.height : cellRect.height,
22
+ left: cellRect.left,
23
+ top: tableRect.height > 0 ? tableRect.top : cellRect.top,
24
+ width: tableRect.width > 0 ? tableRect.width : cellRect.width
25
+ };
26
+ }
27
+ if (tableRect.height <= 0) return null;
28
+ return toRectSnapshot(tableRect);
29
+ };
30
+ const getBlockMeasureRect = (block) => {
31
+ const rect = isTableBlockElement(block) ? getTableBlockRect(block) : toRectSnapshot(block.getBoundingClientRect());
32
+ if (!rect || rect.height <= 0) return null;
33
+ return rect;
34
+ };
2
35
  const collectDragBlocks = (root) => {
3
36
  if (!root) return [];
4
37
  return Array.from(root.querySelectorAll("[data-block-id]")).reduce((acc, block) => {
5
38
  const blockId = block.dataset.blockId;
6
39
  if (!blockId) return acc;
7
- const rect = block.getBoundingClientRect();
8
- if (rect.height <= 0) return acc;
40
+ const rect = getBlockMeasureRect(block);
41
+ if (!rect) return acc;
9
42
  acc.push({
10
43
  block,
11
44
  blockId,
12
- rect: {
13
- bottom: rect.bottom,
14
- height: rect.height,
15
- left: rect.left,
16
- top: rect.top,
17
- width: rect.width
18
- }
45
+ rect
19
46
  });
20
47
  return acc;
21
48
  }, []).sort((a, b) => a.rect.top - b.rect.top);
@@ -92,4 +119,4 @@ const resolveNearestInsertionSlot = (sourceBlockId, blocks, y) => {
92
119
  return bestSlot;
93
120
  };
94
121
  //#endregion
95
- export { collectDragBlocks, getAutoScrollDelta, resolveNearestInsertionSlot, resolveScrollContainers };
122
+ export { collectDragBlocks, getAutoScrollDelta, getBlockMeasureRect, getTableBlockRect, isTableBlockElement, resolveNearestInsertionSlot, resolveScrollContainers };