reactjs-tiptap-editor-pro 0.2.28 → 0.2.30

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 (333) hide show
  1. package/package.json +4 -60
  2. package/src/components/ActionButton.tsx +103 -0
  3. package/src/components/ActionMenuButton.tsx +76 -0
  4. package/src/components/BubbleMenu.tsx +93 -0
  5. package/src/components/CharactorCount.tsx +50 -0
  6. package/src/components/ColorPicker.tsx +272 -0
  7. package/src/components/RichTextEditor.tsx +212 -0
  8. package/src/components/SizeSetter/SizeSetter.tsx +102 -0
  9. package/src/components/Toolbar.tsx +108 -0
  10. package/src/components/icons/Activity.tsx +19 -0
  11. package/src/components/icons/Animas.tsx +24 -0
  12. package/src/components/icons/AspectRatio.tsx +13 -0
  13. package/src/components/icons/Blockquote.tsx +17 -0
  14. package/src/components/icons/ColumnAddLeft.tsx +7 -0
  15. package/src/components/icons/ColumnAddRight.tsx +7 -0
  16. package/src/components/icons/DeleteColumn.tsx +26 -0
  17. package/src/components/icons/DeleteRow.tsx +26 -0
  18. package/src/components/icons/Direction.tsx +7 -0
  19. package/src/components/icons/Excalidraw.tsx +7 -0
  20. package/src/components/icons/ExportPdf.tsx +8 -0
  21. package/src/components/icons/ExportWord.tsx +24 -0
  22. package/src/components/icons/FileWordOutline.tsx +13 -0
  23. package/src/components/icons/Flag.tsx +19 -0
  24. package/src/components/icons/Food.tsx +20 -0
  25. package/src/components/icons/GIfIcon.tsx +10 -0
  26. package/src/components/icons/Icon.tsx +30 -0
  27. package/src/components/icons/ImportWord.tsx +23 -0
  28. package/src/components/icons/LeftToRight.tsx +7 -0
  29. package/src/components/icons/LineHeight.tsx +13 -0
  30. package/src/components/icons/MenuDown.tsx +24 -0
  31. package/src/components/icons/Mermaid.tsx +13 -0
  32. package/src/components/icons/NoFill.tsx +7 -0
  33. package/src/components/icons/Object.tsx +24 -0
  34. package/src/components/icons/RightToLeft.tsx +7 -0
  35. package/src/components/icons/SizeL.tsx +9 -0
  36. package/src/components/icons/SizeM.tsx +13 -0
  37. package/src/components/icons/SizeS.tsx +13 -0
  38. package/src/components/icons/Symbol.tsx +19 -0
  39. package/src/components/icons/Travel.tsx +24 -0
  40. package/src/components/icons/Twitter.tsx +7 -0
  41. package/src/components/icons/icons.ts +212 -0
  42. package/src/components/icons/index.ts +12 -0
  43. package/src/components/index.ts +9 -0
  44. package/src/components/menus/bubble.ts +395 -0
  45. package/src/components/menus/components/BubbleMenuContent.tsx +15 -0
  46. package/src/components/menus/components/BubbleMenuDrawer.tsx +128 -0
  47. package/src/components/menus/components/BubbleMenuExcalidraw.tsx +91 -0
  48. package/src/components/menus/components/BubbleMenuIframe.tsx +143 -0
  49. package/src/components/menus/components/BubbleMenuKatex.tsx +136 -0
  50. package/src/components/menus/components/BubbleMenuLink.tsx +99 -0
  51. package/src/components/menus/components/BubbleMenuMedia.tsx +235 -0
  52. package/src/components/menus/components/BubbleMenuMermaid.tsx +128 -0
  53. package/src/components/menus/components/BubbleMenuText.tsx +102 -0
  54. package/src/components/menus/components/BubbleMenuTwitter.tsx +91 -0
  55. package/src/components/menus/components/ColumnsBubbleMenu.tsx +59 -0
  56. package/src/components/menus/components/ContentMenu.tsx +396 -0
  57. package/src/components/menus/components/TableBubbleMenu.tsx +362 -0
  58. package/src/components/menus/index.ts +7 -0
  59. package/src/components/ui/button.tsx +56 -0
  60. package/src/components/ui/checkbox.tsx +30 -0
  61. package/src/components/ui/dialog.tsx +128 -0
  62. package/src/components/ui/dropdown-menu.tsx +203 -0
  63. package/src/components/ui/emoji-picker.tsx +166 -0
  64. package/src/components/ui/index.ts +14 -0
  65. package/src/components/ui/input.tsx +25 -0
  66. package/src/components/ui/label.tsx +26 -0
  67. package/src/components/ui/popover.tsx +31 -0
  68. package/src/components/ui/select.tsx +162 -0
  69. package/src/components/ui/separator.tsx +31 -0
  70. package/src/components/ui/switch.tsx +29 -0
  71. package/src/components/ui/tabs.tsx +55 -0
  72. package/src/components/ui/textarea.tsx +24 -0
  73. package/src/components/ui/toast.tsx +129 -0
  74. package/src/components/ui/toaster.tsx +44 -0
  75. package/src/components/ui/toggle.tsx +45 -0
  76. package/src/components/ui/tooltip.tsx +30 -0
  77. package/src/components/ui/use-toast.ts +197 -0
  78. package/src/constants/index.ts +223 -0
  79. package/src/constants/resetCSS.ts +139 -0
  80. package/src/extension-bundle.ts +2 -0
  81. package/src/extensions/Attachment/Attachment.ts +144 -0
  82. package/src/extensions/Attachment/components/NodeViewAttachment/FileIcon.tsx +69 -0
  83. package/src/extensions/Attachment/components/NodeViewAttachment/FileIconString.ts +28 -0
  84. package/src/extensions/Attachment/components/NodeViewAttachment/NodeViewAttachment.tsx +155 -0
  85. package/src/extensions/Attachment/components/NodeViewAttachment/index.module.scss +23 -0
  86. package/src/extensions/Attachment/index.ts +1 -0
  87. package/src/extensions/BaseKit.ts +253 -0
  88. package/src/extensions/Blockquote/Blockquote.ts +31 -0
  89. package/src/extensions/Blockquote/index.ts +1 -0
  90. package/src/extensions/Bold/Bold.ts +26 -0
  91. package/src/extensions/Bold/index.ts +1 -0
  92. package/src/extensions/BulletList/BulletList.ts +28 -0
  93. package/src/extensions/BulletList/index.ts +1 -0
  94. package/src/extensions/Clear/Clear.ts +24 -0
  95. package/src/extensions/Clear/index.ts +1 -0
  96. package/src/extensions/Code/Code.ts +26 -0
  97. package/src/extensions/Code/index.ts +1 -0
  98. package/src/extensions/CodeBlock/CodeBlock.ts +54 -0
  99. package/src/extensions/CodeBlock/components/CodeBlockActiveButton.tsx +66 -0
  100. package/src/extensions/CodeBlock/components/NodeViewCodeBlock/NodeViewCodeBlock.tsx +89 -0
  101. package/src/extensions/CodeBlock/components/NodeViewCodeBlock/index.module.scss +81 -0
  102. package/src/extensions/CodeBlock/highlighter.ts +132 -0
  103. package/src/extensions/CodeBlock/index.ts +1 -0
  104. package/src/extensions/CodeBlock/shiki-plugin.ts +213 -0
  105. package/src/extensions/Color/Color.ts +52 -0
  106. package/src/extensions/Color/components/ColorActionButton.tsx +104 -0
  107. package/src/extensions/Color/index.ts +1 -0
  108. package/src/extensions/Document/Document.ts +8 -0
  109. package/src/extensions/Document/index.ts +1 -0
  110. package/src/extensions/Drawer/Drawer.ts +177 -0
  111. package/src/extensions/Drawer/components/ControlDrawer/ControlDrawer.module.scss +85 -0
  112. package/src/extensions/Drawer/components/ControlDrawer/ControlDrawer.tsx +598 -0
  113. package/src/extensions/Drawer/components/ControlDrawer/icon.tsx +500 -0
  114. package/src/extensions/Drawer/components/DrawerActiveButton.tsx +239 -0
  115. package/src/extensions/Drawer/components/EditDrawerBlock.tsx +238 -0
  116. package/src/extensions/Drawer/components/NodeViewDrawer/NodeViewDrawer.tsx +260 -0
  117. package/src/extensions/Drawer/index.ts +1 -0
  118. package/src/extensions/Emoji/Emoji.ts +146 -0
  119. package/src/extensions/Emoji/components/EmojiList/EmojiList.tsx +103 -0
  120. package/src/extensions/Emoji/components/EmojiList/emojis.ts +1858 -0
  121. package/src/extensions/Emoji/components/EmojiPicker/EmojiPicker.tsx +61 -0
  122. package/src/extensions/Emoji/index.ts +1 -0
  123. package/src/extensions/Excalidraw/Excalidraw.ts +123 -0
  124. package/src/extensions/Excalidraw/components/ExcalidrawActiveButton.tsx +138 -0
  125. package/src/extensions/Excalidraw/components/NodeViewExcalidraw/NodeViewExcalidraw.tsx +178 -0
  126. package/src/extensions/Excalidraw/components/NodeViewExcalidraw/index.module.scss +43 -0
  127. package/src/extensions/Excalidraw/index.ts +1 -0
  128. package/src/extensions/ExportPdf/ExportPdf.ts +25 -0
  129. package/src/extensions/ExportPdf/index.ts +1 -0
  130. package/src/extensions/ExportWord/ExportWord.ts +87 -0
  131. package/src/extensions/ExportWord/index.ts +1 -0
  132. package/src/extensions/FontFamily/FontFamily.ts +64 -0
  133. package/src/extensions/FontFamily/components/FontFamilyButton.tsx +97 -0
  134. package/src/extensions/FontFamily/index.ts +1 -0
  135. package/src/extensions/FontSize/FontSize.ts +119 -0
  136. package/src/extensions/FontSize/components/FontSizeMenuButton.tsx +84 -0
  137. package/src/extensions/FontSize/index.ts +1 -0
  138. package/src/extensions/FormatPainter/FormatPainter.ts +121 -0
  139. package/src/extensions/FormatPainter/index.ts +1 -0
  140. package/src/extensions/Heading/Heading.ts +57 -0
  141. package/src/extensions/Heading/components/HeadingButton.tsx +96 -0
  142. package/src/extensions/Heading/index.ts +1 -0
  143. package/src/extensions/Highlight/Highlight.ts +36 -0
  144. package/src/extensions/Highlight/components/HighlightActionButton.tsx +108 -0
  145. package/src/extensions/Highlight/index.ts +1 -0
  146. package/src/extensions/History/History.ts +39 -0
  147. package/src/extensions/History/components/HistoryActionButton.tsx +74 -0
  148. package/src/extensions/History/index.ts +1 -0
  149. package/src/extensions/HorizontalRule/HorizontalRule.ts +42 -0
  150. package/src/extensions/HorizontalRule/index.ts +1 -0
  151. package/src/extensions/Iframe/Iframe.ts +140 -0
  152. package/src/extensions/Iframe/components/IframeNodeView.tsx +92 -0
  153. package/src/extensions/Iframe/components/index.module.scss +40 -0
  154. package/src/extensions/Iframe/embed.ts +487 -0
  155. package/src/extensions/Iframe/index.ts +1 -0
  156. package/src/extensions/Image/Image.ts +303 -0
  157. package/src/extensions/Image/components/ActionImageButton.tsx +186 -0
  158. package/src/extensions/Image/components/ImageCropper.tsx +198 -0
  159. package/src/extensions/Image/components/ImageView.tsx +271 -0
  160. package/src/extensions/Image/index.ts +1 -0
  161. package/src/extensions/Image/store.ts +15 -0
  162. package/src/extensions/ImageGif/ImageGif.ts +176 -0
  163. package/src/extensions/ImageGif/components/ImageGifActionButton.tsx +138 -0
  164. package/src/extensions/ImageGif/components/ImageGifView.tsx +260 -0
  165. package/src/extensions/ImageGif/index.ts +1 -0
  166. package/src/extensions/ImportWord/ImportWord.ts +52 -0
  167. package/src/extensions/ImportWord/components/ImportWordButton.tsx +151 -0
  168. package/src/extensions/ImportWord/index.ts +1 -0
  169. package/src/extensions/Indent/Indent.ts +110 -0
  170. package/src/extensions/Indent/index.ts +1 -0
  171. package/src/extensions/Italic/Italic.ts +29 -0
  172. package/src/extensions/Italic/index.ts +1 -0
  173. package/src/extensions/Katex/Katex.ts +109 -0
  174. package/src/extensions/Katex/components/KatexActiveButton.tsx +117 -0
  175. package/src/extensions/Katex/components/KatexWrapper.tsx +53 -0
  176. package/src/extensions/Katex/index.ts +1 -0
  177. package/src/extensions/LineHeight/LineHeight.ts +76 -0
  178. package/src/extensions/LineHeight/components/LineHeightDropdown.tsx +93 -0
  179. package/src/extensions/LineHeight/index.ts +1 -0
  180. package/src/extensions/Link/Link.ts +92 -0
  181. package/src/extensions/Link/components/LinkEditBlock.tsx +110 -0
  182. package/src/extensions/Link/components/LinkEditPopover.tsx +46 -0
  183. package/src/extensions/Link/components/LinkViewBlock.tsx +54 -0
  184. package/src/extensions/Link/index.ts +1 -0
  185. package/src/extensions/ListItem/ListItem.ts +1 -0
  186. package/src/extensions/ListItem/index.ts +1 -0
  187. package/src/extensions/Mention/Mention.ts +100 -0
  188. package/src/extensions/Mention/components/NodeViewMentionList/NodeViewMentionList.tsx +94 -0
  189. package/src/extensions/Mention/components/NodeViewMentionList/index.module.scss +38 -0
  190. package/src/extensions/Mention/index.ts +1 -0
  191. package/src/extensions/Mermaid/Mermaid.ts +177 -0
  192. package/src/extensions/Mermaid/components/EditMermaidBlock.tsx +155 -0
  193. package/src/extensions/Mermaid/components/MermaidActiveButton.tsx +151 -0
  194. package/src/extensions/Mermaid/components/NodeViewMermaid/NodeViewMermaid.tsx +260 -0
  195. package/src/extensions/Mermaid/index.ts +1 -0
  196. package/src/extensions/MoreMark/MoreMark.ts +102 -0
  197. package/src/extensions/MoreMark/components/ActionMoreButton.tsx +97 -0
  198. package/src/extensions/MoreMark/index.ts +1 -0
  199. package/src/extensions/MultiColumn/Column.ts +36 -0
  200. package/src/extensions/MultiColumn/MultiColumn.ts +111 -0
  201. package/src/extensions/MultiColumn/components/ColumnActionButton.ts +22 -0
  202. package/src/extensions/MultiColumn/index.ts +3 -0
  203. package/src/extensions/OrderedList/OrderedList.ts +28 -0
  204. package/src/extensions/OrderedList/index.ts +1 -0
  205. package/src/extensions/SearchAndReplace/SearchAndReplace.ts +395 -0
  206. package/src/extensions/SearchAndReplace/components/SearchAndReplaceButton.tsx +190 -0
  207. package/src/extensions/SearchAndReplace/index.ts +1 -0
  208. package/src/extensions/Selection/Selection.ts +32 -0
  209. package/src/extensions/Selection/index.ts +1 -0
  210. package/src/extensions/SlashCommand/SlashCommand.ts +255 -0
  211. package/src/extensions/SlashCommand/components/CommandsList.tsx +180 -0
  212. package/src/extensions/SlashCommand/groups.ts +183 -0
  213. package/src/extensions/SlashCommand/index.ts +1 -0
  214. package/src/extensions/SlashCommand/types.ts +24 -0
  215. package/src/extensions/Strike/Strike.ts +26 -0
  216. package/src/extensions/Strike/index.ts +1 -0
  217. package/src/extensions/Subscript/Subscript.ts +88 -0
  218. package/src/extensions/Subscript/index.ts +1 -0
  219. package/src/extensions/Table/Cell.ts +131 -0
  220. package/src/extensions/Table/Header.ts +93 -0
  221. package/src/extensions/Table/Row.ts +8 -0
  222. package/src/extensions/Table/Table.ts +60 -0
  223. package/src/extensions/Table/cell-background.ts +112 -0
  224. package/src/extensions/Table/components/CreateTablePopover.tsx +132 -0
  225. package/src/extensions/Table/components/TableActionButton.tsx +42 -0
  226. package/src/extensions/Table/index.ts +6 -0
  227. package/src/extensions/Table/utils.ts +352 -0
  228. package/src/extensions/TableOfContent/TableOfContent.ts +124 -0
  229. package/src/extensions/TableOfContent/components/NodeViewTableOfContent.tsx +116 -0
  230. package/src/extensions/TableOfContent/components/TableOfContentActionButton.tsx +27 -0
  231. package/src/extensions/TableOfContent/components/index.module.scss +40 -0
  232. package/src/extensions/TableOfContent/index.ts +1 -0
  233. package/src/extensions/TaskList/TaskList.ts +46 -0
  234. package/src/extensions/TaskList/index.ts +1 -0
  235. package/src/extensions/TextAlign/TextAlign.ts +68 -0
  236. package/src/extensions/TextAlign/components/TextAlignMenuButton.tsx +103 -0
  237. package/src/extensions/TextAlign/index.ts +1 -0
  238. package/src/extensions/TextBubble/TextBubble.ts +22 -0
  239. package/src/extensions/TextBubble/components/TextDropdown.tsx +146 -0
  240. package/src/extensions/TextBubble/index.ts +1 -0
  241. package/src/extensions/TextDirection/TextDirection.ts +97 -0
  242. package/src/extensions/TextDirection/components/TextDirectionButton.tsx +103 -0
  243. package/src/extensions/TextDirection/index.ts +1 -0
  244. package/src/extensions/TrailingNode/TrailingNode.ts +71 -0
  245. package/src/extensions/TrailingNode/index.ts +1 -0
  246. package/src/extensions/Twitter/Twitter.ts +161 -0
  247. package/src/extensions/Twitter/components/FormEditLinkTwitter.tsx +68 -0
  248. package/src/extensions/Twitter/components/NodeViewTweet.tsx +30 -0
  249. package/src/extensions/Twitter/components/TwitterActiveButton.tsx +41 -0
  250. package/src/extensions/Twitter/index.ts +1 -0
  251. package/src/extensions/UnderLine/Underline.ts +30 -0
  252. package/src/extensions/UnderLine/index.ts +1 -0
  253. package/src/extensions/Video/Video.ts +204 -0
  254. package/src/extensions/Video/components/ActiveVideoButton.tsx +191 -0
  255. package/src/extensions/Video/index.ts +1 -0
  256. package/src/extensions/Video/store.ts +15 -0
  257. package/src/extensions/index.ts +122 -0
  258. package/src/hooks/useActive.tsx +24 -0
  259. package/src/hooks/useAttributes.tsx +45 -0
  260. package/src/hooks/useCopy.tsx +20 -0
  261. package/src/hooks/useEditorState.tsx +23 -0
  262. package/src/hooks/useExtension.tsx +29 -0
  263. package/src/index.ts +8 -0
  264. package/src/lib/utils.ts +7 -0
  265. package/src/locale-bundle.ts +3 -0
  266. package/src/locales/en.ts +173 -0
  267. package/src/locales/hu.ts +173 -0
  268. package/src/locales/index.tsx +163 -0
  269. package/src/locales/pt-br.ts +173 -0
  270. package/src/locales/vi.ts +173 -0
  271. package/src/locales/zh-cn.ts +173 -0
  272. package/src/plugins/DragHandle/index.ts +375 -0
  273. package/src/plugins/DragHandle/range.ts +114 -0
  274. package/src/plugins/DragHandle/utils.ts +80 -0
  275. package/src/plugins/image-upload.ts +160 -0
  276. package/src/store/ProviderRichText.tsx +53 -0
  277. package/src/store/editableEditor.ts +15 -0
  278. package/src/store/fast-context.tsx +70 -0
  279. package/src/store/store.ts +35 -0
  280. package/src/styles/ProseMirror.scss +176 -0
  281. package/src/styles/columns.scss +23 -0
  282. package/src/styles/editor.scss +411 -0
  283. package/src/styles/global.scss +87 -0
  284. package/src/styles/index.scss +5 -0
  285. package/src/styles/mention.scss +6 -0
  286. package/src/theme/theme.ts +15 -0
  287. package/src/types.ts +271 -0
  288. package/src/utils/_event.ts +55 -0
  289. package/src/utils/color.ts +67 -0
  290. package/src/utils/columns.ts +142 -0
  291. package/src/utils/customEvents/customEvents.ts +18 -0
  292. package/src/utils/customEvents/events.constant.ts +11 -0
  293. package/src/utils/delete-node.ts +46 -0
  294. package/src/utils/dom-dataset.ts +121 -0
  295. package/src/utils/download.ts +17 -0
  296. package/src/utils/dynamicCSS.ts +192 -0
  297. package/src/utils/editor-container-size.ts +28 -0
  298. package/src/utils/file.ts +112 -0
  299. package/src/utils/getRenderContainer.ts +41 -0
  300. package/src/utils/indent.ts +99 -0
  301. package/src/utils/is-mobile.ts +57 -0
  302. package/src/utils/json.ts +18 -0
  303. package/src/utils/line-height.ts +109 -0
  304. package/src/utils/lru-cache.ts +145 -0
  305. package/src/utils/mitt.ts +114 -0
  306. package/src/utils/node.ts +92 -0
  307. package/src/utils/pdf.ts +72 -0
  308. package/src/utils/plateform.ts +49 -0
  309. package/src/utils/shortId.ts +5 -0
  310. package/src/utils/storage.ts +18 -0
  311. package/src/utils/utils.ts +71 -0
  312. package/src/vite-env.d.ts +3 -0
  313. package/lib/RichTextEditor-BC85Dn21.js +0 -8826
  314. package/lib/RichTextEditor-Cxm0yJdr.cjs +0 -141
  315. package/lib/extension-bundle.cjs +0 -33
  316. package/lib/extension-bundle.d.cts +0 -947
  317. package/lib/extension-bundle.d.ts +0 -947
  318. package/lib/extension-bundle.js +0 -5755
  319. package/lib/index-DV-nXpU1.cjs +0 -1
  320. package/lib/index-M6H3FoBi.js +0 -1147
  321. package/lib/index.cjs +0 -1
  322. package/lib/index.d.cts +0 -511
  323. package/lib/index.d.ts +0 -511
  324. package/lib/index.js +0 -16
  325. package/lib/locale-bundle.cjs +0 -1
  326. package/lib/locale-bundle.d.cts +0 -1140
  327. package/lib/locale-bundle.d.ts +0 -1140
  328. package/lib/locale-bundle.js +0 -9
  329. package/lib/style.css +0 -1
  330. package/lib/tiptap-DkWHMWDt.js +0 -6061
  331. package/lib/tiptap-gBG-1T-V.cjs +0 -116
  332. package/lib/vendor-BJ0Yf78E.cjs +0 -8114
  333. package/lib/vendor-Cpa6z-M0.js +0 -67575
@@ -0,0 +1,375 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
2
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
3
+ import { type Editor } from '@tiptap/core';
4
+ import { type Node } from '@tiptap/pm/model';
5
+ import { PluginKey, Plugin } from '@tiptap/pm/state';
6
+ import { type Transaction, type EditorState } from '@tiptap/pm/state';
7
+ import type { EditorView } from '@tiptap/pm/view';
8
+ import debounce from 'lodash-es/debounce';
9
+ import tippy from 'tippy.js';
10
+ import { type Props as TippyProps } from 'tippy.js';
11
+ // import { ySyncPluginKey, absolutePositionToRelativePosition } from 'y-prosemirror';
12
+
13
+ import { getSelectionRanges, NodeRangeSelection } from './range';
14
+ import { cloneElement, getComputedStyles, minMax, removeNode, findElementNextToCoords } from './utils';
15
+
16
+ function getSelectionRangesNearCursor(event: any, editor: any) {
17
+ const { doc: editorDocument } = editor.view.state,
18
+ nearestElement = findElementNextToCoords({ editor: editor, x: event.clientX, y: event.clientY, direction: 'right' });
19
+ if (!nearestElement.resultNode || null === nearestElement.pos) return [];
20
+ const mouseXPosition = event.clientX,
21
+ cursorPosition = (function (editor, mouseX, mouseY) {
22
+ const editorPaddingLeft = Number.parseInt(getComputedStyles(editor.dom, 'paddingLeft'), 10),
23
+ editorPaddingRight = Number.parseInt(getComputedStyles(editor.dom, 'paddingRight'), 10),
24
+ editorBorderLeft = Number.parseInt(getComputedStyles(editor.dom, 'borderLeftWidth'), 10),
25
+ editorBorderRight = Number.parseInt(getComputedStyles(editor.dom, 'borderLeftWidth'), 10),
26
+ editorRect = editor.dom.getBoundingClientRect();
27
+ return {
28
+ left: minMax(mouseX, editorRect.left + editorPaddingLeft + editorBorderLeft,
29
+ editorRect.right - editorPaddingRight - editorBorderRight),
30
+ top: mouseY
31
+ };
32
+ })(editor.view, mouseXPosition, event.clientY),
33
+ positionAtCoords = editor.view.posAtCoords(cursorPosition);
34
+ if (!positionAtCoords) return [];
35
+ const { pos: documentPosition } = positionAtCoords;
36
+ if (!editorDocument.resolve(documentPosition).parent) return [];
37
+ const startResolvedPosition = editorDocument.resolve(nearestElement.pos),
38
+ endResolvedPosition = editorDocument.resolve(nearestElement.pos + 1);
39
+ return getSelectionRanges(startResolvedPosition, endResolvedPosition, 0);
40
+ }
41
+ function getPreviousNodeStartPosition (document: any, position: any) {
42
+ const resolvedPosition = document.resolve(position),
43
+ { depth: nodeDepth } = resolvedPosition;
44
+ if (0 === nodeDepth) return position;
45
+ return resolvedPosition.pos - resolvedPosition.parentOffset - 1;
46
+ }
47
+ function getAncestorNodeAtDepth (document: any, position: any) {
48
+ const currentNode = document.nodeAt(position),
49
+ resolvedPosition = document.resolve(position);
50
+ let { depth: currentDepth } = resolvedPosition,
51
+ ancestorNode = currentNode;
52
+ for (; currentDepth > 0;) {
53
+ const parentNode = resolvedPosition.node(currentDepth);
54
+ currentDepth -= 1;
55
+ if (0 === currentDepth) {
56
+ ancestorNode = parentNode;
57
+ }
58
+ }
59
+ return ancestorNode;
60
+ }
61
+ // function getOuterNode (doc: any, pos: any) {
62
+ // const n = ySyncPluginKey.getState(doc);
63
+ // return n ? absolutePositionToRelativePosition(pos, n.type, n.binding.mapping) : null;
64
+ // }
65
+ function getOuterNodePos (e: any, t: any) {
66
+ let n = t;
67
+ for (; n && n.parentNode && n.parentNode !== e.dom;) n = n.parentNode;
68
+ return n;
69
+ }
70
+
71
+ const dragHandlePluginDefaultKey = new PluginKey('dragHandle');
72
+
73
+ function DragHandlePlugin ({
74
+ pluginKey: customPluginKey = dragHandlePluginDefaultKey,
75
+ element: dragHandleElement,
76
+ editor,
77
+ tippyOptions,
78
+ onNodeChange,
79
+ }: {
80
+ pluginKey?: PluginKey | string
81
+ element: HTMLElement
82
+ editor: Editor
83
+ tippyOptions?: Partial<TippyProps>
84
+ onNodeChange?: (data: { editor: Editor; node: Node | null; pos: number }) => void
85
+ }) {
86
+ const dragHandleContainer = document.createElement('div');
87
+ let tippyInstance: any = null;
88
+ let isDragLocked = false;
89
+ // eslint-disable-next-line @typescript-eslint/no-inferrable-types
90
+ let selectedNode: null = null;
91
+ let selectedNodePosition = -1;
92
+
93
+ dragHandleElement.addEventListener('dragstart', event => {
94
+ const { view: editorView } = editor;
95
+ if (!event.dataTransfer) return;
96
+ const { empty: isSelectionEmpty, $from: selectionStart, $to: selectionEnd } = editorView.state.selection;
97
+ const nearCursorRanges = getSelectionRangesNearCursor(event, editor);
98
+ const currentSelectionRanges = getSelectionRanges(selectionStart, selectionEnd, 0);
99
+ const hasOverlappingRanges = currentSelectionRanges.some(range =>
100
+ nearCursorRanges.find(nearRange => nearRange.$from === range.$from && nearRange.$to === range.$to));
101
+ const selectedRanges = isSelectionEmpty || !hasOverlappingRanges ? nearCursorRanges : currentSelectionRanges;
102
+
103
+ if (selectedRanges.length === 0) return;
104
+ const { tr: transaction } = editorView.state;
105
+ const dragPreviewElement = document.createElement('div');
106
+ const startPos = selectedRanges[0].$from.pos;
107
+ const endPos = selectedRanges[selectedRanges.length - 1].$to.pos;
108
+ const nodeSelection = NodeRangeSelection.create(editorView.state.doc, startPos, endPos);
109
+ const selectionSlice = nodeSelection.content();
110
+
111
+ selectedRanges.forEach(range => {
112
+ const clonedNode = cloneElement(editorView.nodeDOM(range.$from.pos) as HTMLElement);
113
+ dragPreviewElement.append(clonedNode);
114
+ });
115
+ dragPreviewElement.style.position = 'absolute';
116
+ dragPreviewElement.style.top = '-10000px';
117
+ document.body.append(dragPreviewElement);
118
+ event.dataTransfer.clearData();
119
+ event.dataTransfer.setDragImage(dragPreviewElement, 0, 0);
120
+ editorView.dragging = { slice: selectionSlice, move: true };
121
+ transaction.setSelection(nodeSelection);
122
+ editorView.dispatch(transaction);
123
+ document.addEventListener('drop', () => removeNode(dragPreviewElement), { once: true });
124
+ setTimeout(() => {
125
+ dragHandleElement && (dragHandleElement.style.pointerEvents = 'none');
126
+ }, 0);
127
+ });
128
+ dragHandleElement.addEventListener('dragend', () => {
129
+ dragHandleElement && (dragHandleElement.style.pointerEvents = 'auto');
130
+ });
131
+ return new Plugin({
132
+ key: typeof customPluginKey === 'string' ? new PluginKey(customPluginKey) : customPluginKey,
133
+ state: {
134
+ init: () => ({ locked: false }),
135
+ apply(transaction: Transaction, oldState: { locked: boolean }): { locked: boolean } {
136
+ const isLocked = transaction.getMeta('lockDragHandle');
137
+ const shouldHide = transaction.getMeta('hideDragHandle');
138
+ if ((undefined !== isLocked && (isDragLocked = isLocked), shouldHide && tippyInstance)) {
139
+ tippyInstance.hide();
140
+ isDragLocked = false;
141
+ selectedNode = null;
142
+ selectedNodePosition = -1;
143
+ onNodeChange?.({ editor: editor, node: null, pos: -1 });
144
+ return oldState;
145
+ }
146
+ if (transaction.docChanged && -1 !== selectedNodePosition && dragHandleElement && tippyInstance) {
147
+ const newPos = transaction.mapping.map(selectedNodePosition);
148
+ if (newPos !== selectedNodePosition) {
149
+ selectedNodePosition = newPos;
150
+ // relativePosition = getOuterNode(editorState.doc, selectedNodePosition);
151
+ }
152
+ }
153
+ return oldState;
154
+ },
155
+ },
156
+ view: editorView => {
157
+ initializeDragHandle();
158
+
159
+ setupDragHandleContainer(editor.view.dom);
160
+
161
+ tippyInstance = tippy(editorView.dom, {
162
+ ...getDefaultTippyConfig(),
163
+ ...tippyOptions,
164
+ });
165
+
166
+ return {
167
+ update(editorView, oldState) {
168
+ if (!canUpdateDragHandle(dragHandleElement, tippyInstance)) {
169
+ return;
170
+ }
171
+
172
+ dragHandleElement.draggable = !isDragLocked;
173
+
174
+ if (!shouldUpdatePosition(editorView, oldState)) {
175
+ return;
176
+ }
177
+
178
+ const targetElement = getValidTargetElement(editorView);
179
+ if (!targetElement) {
180
+ return;
181
+ }
182
+
183
+ updateNodeInformation(editorView, targetElement);
184
+ },
185
+ destroy() {
186
+ cleanup();
187
+ }
188
+ };
189
+
190
+ function initializeDragHandle() {
191
+ dragHandleElement.draggable = true;
192
+ dragHandleElement.style.pointerEvents = 'auto';
193
+ }
194
+
195
+ function setupDragHandleContainer(parentElement: any) {
196
+ parentElement.parentElement?.appendChild(dragHandleContainer);
197
+ dragHandleContainer.appendChild(dragHandleElement);
198
+
199
+ Object.assign(dragHandleContainer.style, {
200
+ pointerEvents: 'none',
201
+ position: 'absolute',
202
+ top: '0',
203
+ left: '0'
204
+ });
205
+ }
206
+
207
+ function getDefaultTippyConfig(): Partial<TippyProps> {
208
+ return {
209
+ getReferenceClientRect: null,
210
+ interactive: true,
211
+ trigger: 'manual',
212
+ placement: 'left-start',
213
+ hideOnClick: false,
214
+ duration: 100,
215
+ zIndex: 10,
216
+ appendTo: dragHandleContainer,
217
+ content: dragHandleElement,
218
+ popperOptions: {
219
+ modifiers: [
220
+ { name: 'flip', enabled: false },
221
+ {
222
+ name: 'preventOverflow',
223
+ options: {
224
+ rootBoundary: 'document',
225
+ mainAxis: false
226
+ }
227
+ },
228
+ ],
229
+ }
230
+ };
231
+ }
232
+
233
+ function canUpdateDragHandle(element: HTMLElement | null, tippy: any) {
234
+ return element && tippy;
235
+ }
236
+
237
+ function shouldUpdatePosition(view: EditorView, oldState: EditorState) {
238
+ return !view.state.doc.eq(oldState.doc) && selectedNodePosition !== -1;
239
+ }
240
+
241
+ function getValidTargetElement(view: EditorView) {
242
+ let targetElement: any = view.nodeDOM(selectedNodePosition);
243
+ targetElement = getOuterNodePos(view, targetElement);
244
+
245
+ if (targetElement === view.dom || targetElement?.nodeType !== 1) {
246
+ return null;
247
+ }
248
+
249
+ return targetElement;
250
+ }
251
+
252
+ function updateNodeInformation(view: EditorView, targetElement: HTMLElement) {
253
+ const domPosition = view.posAtDOM(targetElement, 0);
254
+ const ancestorNode = getAncestorNodeAtDepth(editor.state.doc, domPosition);
255
+
256
+ if (ancestorNode === selectedNode) {
257
+ return;
258
+ }
259
+
260
+ const previousNodePos = getPreviousNodeStartPosition(editor.state.doc, domPosition);
261
+
262
+ selectedNode = ancestorNode;
263
+ selectedNodePosition = previousNodePos;
264
+ // relativePosition = getOuterNode(editor.state.doc, selectedNodePosition);
265
+
266
+ onNodeChange?.({
267
+ editor,
268
+ node: selectedNode,
269
+ pos: selectedNodePosition
270
+ });
271
+
272
+ updateTippyPosition(targetElement);
273
+ }
274
+
275
+ function updateTippyPosition(element: HTMLElement) {
276
+ tippyInstance.setProps({
277
+ getReferenceClientRect: () => element.getBoundingClientRect()
278
+ });
279
+ tippyInstance.show();
280
+ }
281
+
282
+ function cleanup() {
283
+ tippyInstance?.destroy();
284
+ dragHandleElement && removeNode(dragHandleContainer);
285
+ }
286
+ },
287
+ props: {
288
+ handleDOMEvents: {
289
+ mouseleave: (editorView, event: any) => {
290
+ if (isDragLocked) return false;
291
+ if (!shouldHideDragHandle(event)) return false;
292
+
293
+ hideDragHandle();
294
+ resetSelectedState();
295
+ return false;
296
+
297
+ function shouldHideDragHandle(event: any) {
298
+ return event.target && !dragHandleContainer.contains(event.relatedTarget);
299
+ }
300
+
301
+ function hideDragHandle() {
302
+ tippyInstance?.hide();
303
+ }
304
+
305
+ function resetSelectedState() {
306
+ selectedNode = null;
307
+ selectedNodePosition = -1;
308
+ onNodeChange?.({
309
+ editor: editor,
310
+ node: null,
311
+ pos: -1
312
+ });
313
+ }
314
+ },
315
+ mousemove: debounce((editorView: EditorView, mouseEvent: MouseEvent) => {
316
+ if (!dragHandleElement || !tippyInstance || isDragLocked) {
317
+ return false;
318
+ }
319
+
320
+ const nearestElement = findElementNextToCoords({
321
+ x: mouseEvent.clientX,
322
+ y: mouseEvent.clientY,
323
+ direction: 'right',
324
+ editor: editor
325
+ });
326
+
327
+ if (!nearestElement.resultElement) {
328
+ return false;
329
+ }
330
+
331
+ if (nearestElement.resultElement === editorView.dom) {
332
+ return false;
333
+ }
334
+
335
+ const targetElement = getOuterNodePos(editorView, nearestElement.resultElement);
336
+
337
+ if (targetElement === editorView.dom) {
338
+ return false;
339
+ }
340
+
341
+ if (targetElement?.nodeType !== 1) {
342
+ return false;
343
+ }
344
+
345
+ const domPosition = editorView.posAtDOM(targetElement, 0);
346
+ const ancestorNode = getAncestorNodeAtDepth(editor.state.doc, domPosition);
347
+
348
+ if (ancestorNode === selectedNode) {
349
+ return false;
350
+ }
351
+
352
+ const previousNodePosition = getPreviousNodeStartPosition(editor.state.doc, domPosition);
353
+ selectedNode = ancestorNode;
354
+ selectedNodePosition = previousNodePosition;
355
+ // relativePosition = getOuterNode(editorView.state, selectedNodePosition);
356
+
357
+ onNodeChange?.({
358
+ editor: editor,
359
+ node: selectedNode,
360
+ pos: selectedNodePosition
361
+ });
362
+
363
+ tippyInstance.setProps({
364
+ getReferenceClientRect: () => targetElement.getBoundingClientRect()
365
+ });
366
+ tippyInstance.show();
367
+
368
+ return false;
369
+ }, 100),
370
+ },
371
+ },
372
+ });
373
+ }
374
+
375
+ export { DragHandlePlugin, dragHandlePluginDefaultKey };
@@ -0,0 +1,114 @@
1
+ import { Selection, SelectionRange } from '@tiptap/pm/state';
2
+ import { NodeRange, ResolvedPos, Node as ProseMirrorNode } from '@tiptap/pm/model';
3
+ import { Mapping, Mappable } from '@tiptap/pm/transform';
4
+
5
+ export function getSelectionRanges(state: ResolvedPos, range: ResolvedPos, depth?: number): SelectionRange[] {
6
+ const ranges: SelectionRange[] = [];
7
+ const root = state.node(0);
8
+ depth =
9
+ typeof depth === 'number' && depth >= 0
10
+ ? depth
11
+ : state.sameParent(range)
12
+ ? Math.max(0, state.sharedDepth(range.pos) - 1)
13
+ : state.sharedDepth(range.pos);
14
+ const nodeRange = new NodeRange(state, range, depth);
15
+ const startIndex = nodeRange.depth === 0 ? 0 : root.resolve(nodeRange.start).posAtIndex(0);
16
+
17
+ nodeRange.parent.forEach((size, offset) => {
18
+ const from = startIndex + offset;
19
+ const to = from + size.nodeSize;
20
+ if (from < nodeRange.start || from >= nodeRange.end) return;
21
+ const selectionRange = new SelectionRange(root.resolve(from), root.resolve(to));
22
+ ranges.push(selectionRange);
23
+ });
24
+
25
+ return ranges;
26
+ }
27
+
28
+ class NodeRangeBookmark {
29
+ anchor: number;
30
+ head: number;
31
+ constructor(anchor: number, head: number) {
32
+ this.anchor = anchor;
33
+ this.head = head;
34
+ }
35
+ map(mapping: Mappable) {
36
+ return new NodeRangeBookmark(mapping.map(this.anchor), mapping.map(this.head));
37
+ }
38
+ resolve(doc: ProseMirrorNode) {
39
+ const e = doc.resolve(this.anchor);
40
+ const o = doc.resolve(this.head);
41
+ return new NodeRangeSelection(e, o);
42
+ }
43
+ }
44
+
45
+ export class NodeRangeSelection extends Selection {
46
+ depth: number | undefined;
47
+ constructor(from: ResolvedPos, to: ResolvedPos, depth?: number, bias = 1) {
48
+ const { doc } = from;
49
+ const isSamePosition = from === to;
50
+ const isAtDocEnd = from.pos === doc.content.size && to.pos === doc.content.size;
51
+ const resolvedTo = isSamePosition && !isAtDocEnd ? doc.resolve(to.pos + (bias > 0 ? 1 : -1)) : to;
52
+ const resolvedFrom = isSamePosition && isAtDocEnd ? doc.resolve(from.pos - (bias > 0 ? 1 : -1)) : from;
53
+ const selectionRanges = getSelectionRanges(resolvedFrom.min(resolvedTo), resolvedFrom.max(resolvedTo), depth);
54
+ super(
55
+ resolvedTo.pos >= from.pos ? selectionRanges[0].$from : selectionRanges[selectionRanges.length - 1].$to,
56
+ resolvedTo.pos >= from.pos ? selectionRanges[selectionRanges.length - 1].$to : selectionRanges[0].$from,
57
+ selectionRanges
58
+ );
59
+ this.depth = depth;
60
+ }
61
+ get $to() {
62
+ return this.ranges[this.ranges.length - 1].$to;
63
+ }
64
+ eq(other: Selection) {
65
+ return other instanceof NodeRangeSelection && other.$from.pos === this.$from.pos && other.$to.pos === this.$to.pos;
66
+ }
67
+ map(doc: ProseMirrorNode, mapping: Mapping) {
68
+ const newFrom = doc.resolve(mapping.map(this.anchor));
69
+ const newTo = doc.resolve(mapping.map(this.head));
70
+ return new NodeRangeSelection(newFrom, newTo);
71
+ }
72
+ toJSON() {
73
+ return { type: 'nodeRange', anchor: this.anchor, head: this.head };
74
+ }
75
+ get isForwards() {
76
+ return this.head >= this.anchor;
77
+ }
78
+ get isBackwards() {
79
+ return !this.isForwards;
80
+ }
81
+ extendBackwards() {
82
+ const { doc } = this.$from;
83
+ if (this.isForwards && this.ranges.length > 1) {
84
+ const remainingRanges = this.ranges.slice(0, -1);
85
+ const newFrom = remainingRanges[0].$from;
86
+ const newTo = remainingRanges[remainingRanges.length - 1].$to;
87
+ return new NodeRangeSelection(newFrom, newTo, this.depth);
88
+ }
89
+ const currentRange = this.ranges[0];
90
+ const newPosition = doc.resolve(Math.max(0, currentRange.$from.pos - 1));
91
+ return new NodeRangeSelection(this.$anchor, newPosition, this.depth);
92
+ }
93
+ extendForwards() {
94
+ const { doc } = this.$from;
95
+ if (this.isBackwards && this.ranges.length > 1) {
96
+ const remainingRanges = this.ranges.slice(1);
97
+ const newFrom = remainingRanges[0].$from;
98
+ const newTo = remainingRanges[remainingRanges.length - 1].$to;
99
+ return new NodeRangeSelection(newTo, newFrom, this.depth);
100
+ }
101
+ const currentRange = this.ranges[this.ranges.length - 1];
102
+ const newPosition = doc.resolve(Math.min(doc.content.size, currentRange.$to.pos + 1));
103
+ return new NodeRangeSelection(this.$anchor, newPosition, this.depth);
104
+ }
105
+ static fromJSON(doc: ProseMirrorNode, json: any): NodeRangeSelection {
106
+ return new NodeRangeSelection(doc.resolve(json.anchor), doc.resolve(json.head));
107
+ }
108
+ static create(doc: ProseMirrorNode, anchor: number, head: number, depth?: number, bias = 1) {
109
+ return new this(doc.resolve(anchor), doc.resolve(head), depth, bias);
110
+ }
111
+ getBookmark() {
112
+ return new NodeRangeBookmark(this.anchor, this.head);
113
+ }
114
+ }
@@ -0,0 +1,80 @@
1
+ /* eslint-disable @typescript-eslint/prefer-for-of */
2
+ import { type Editor } from '@tiptap/core';
3
+
4
+ export function cloneElement(node: HTMLElement) {
5
+ const clonedNode: any = node.cloneNode(true);
6
+ const originalElements = [node, ...Array.from(node.getElementsByTagName('*'))];
7
+ const clonedElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))];
8
+ originalElements.forEach((element, index) => {
9
+ clonedElements[index].style.cssText = (function (element) {
10
+ let styles = '';
11
+ const computedStyles = getComputedStyle(element);
12
+ for (let i = 0; i < computedStyles.length; i += 1) {
13
+ styles += `${computedStyles[i]}:${computedStyles.getPropertyValue(computedStyles[i])};`;
14
+ }
15
+ return styles;
16
+ })(element);
17
+ });
18
+ return clonedNode;
19
+ }
20
+ export function getComputedStyles(element: Element, styleProperty: any) {
21
+ return window.getComputedStyle(element)[styleProperty];
22
+ }
23
+
24
+ export function minMax(value = 0, minimum = 0, maximum = 0) {
25
+ return Math.min(Math.max(value, minimum), maximum);
26
+ }
27
+
28
+ export function removeNode(node: HTMLElement) {
29
+ if (node.parentNode !== null && node.parentNode !== undefined) {
30
+ node.parentNode.removeChild(node);
31
+ }
32
+ }
33
+ export type FindElementNextToCoords = {
34
+ x: number
35
+ y: number
36
+ direction?: 'left' | 'right'
37
+ editor: Editor
38
+ };
39
+ export function findElementNextToCoords (options: FindElementNextToCoords) {
40
+ const { x, y, direction, editor } = options;
41
+ let targetElement: any = null;
42
+ let targetNode: any = null;
43
+ let documentPosition: any = null;
44
+ let currentX = x;
45
+
46
+ for (; null === targetNode && currentX < window.innerWidth && currentX > 0;) {
47
+ const elementsAtPoint = document.elementsFromPoint(currentX, y);
48
+ const proseMirrorIndex = elementsAtPoint.findIndex(el => el.classList.contains('ProseMirror'));
49
+ const relevantElements = elementsAtPoint.slice(0, proseMirrorIndex);
50
+
51
+ if (relevantElements.length > 0) {
52
+ const currentElement = relevantElements[0];
53
+ targetElement = currentElement;
54
+ documentPosition = editor.view.posAtDOM(currentElement, 0);
55
+
56
+ if (documentPosition >= 0) {
57
+ targetNode = editor.state.doc.nodeAt(Math.max(documentPosition - 1, 0));
58
+ if (targetNode === null || targetNode.isText) {
59
+ targetNode = editor.state.doc.nodeAt(Math.max(documentPosition - 1, 0));
60
+ }
61
+ if (!targetNode) {
62
+ targetNode = editor.state.doc.nodeAt(Math.max(documentPosition, 0));
63
+ }
64
+ break;
65
+ }
66
+ }
67
+
68
+ if (direction === 'left') {
69
+ currentX -= 1;
70
+ } else {
71
+ currentX += 1;
72
+ }
73
+ }
74
+
75
+ return {
76
+ resultElement: targetElement,
77
+ resultNode: targetNode,
78
+ pos: documentPosition !== null ? documentPosition : null
79
+ };
80
+ }