@lobehub/editor 1.8.2 → 1.8.4

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 (37) hide show
  1. package/es/editor-kernel/react/PortalAnchor.d.ts +5 -0
  2. package/es/editor-kernel/react/PortalAnchor.js +13 -0
  3. package/es/editor-kernel/react/useAnchor.d.ts +1 -0
  4. package/es/editor-kernel/react/useAnchor.js +23 -0
  5. package/es/plugins/link/react/ReactLinkPlugin.js +9 -104
  6. package/es/plugins/link/react/components/LinkEdit.d.ts +6 -2
  7. package/es/plugins/link/react/components/LinkEdit.js +49 -94
  8. package/es/plugins/link/react/components/LinkToolbar.d.ts +1 -4
  9. package/es/plugins/link/react/components/LinkToolbar.js +87 -21
  10. package/es/plugins/link/react/style.js +1 -1
  11. package/es/plugins/math/react/{component → components}/MathEditor.js +11 -8
  12. package/es/plugins/math/react/{component → components}/MathEditorContainer.d.ts +2 -1
  13. package/es/plugins/math/react/{component → components}/MathEditorContainer.js +27 -31
  14. package/es/plugins/math/react/{component → components}/MathEditorContent.d.ts +2 -1
  15. package/es/plugins/math/react/{component → components}/MathEditorContent.js +3 -2
  16. package/es/plugins/math/react/index.js +2 -2
  17. package/es/plugins/math/react/style.js +1 -1
  18. package/es/plugins/slash/react/components/DefaultSlashMenu.d.ts +5 -0
  19. package/es/plugins/slash/react/components/DefaultSlashMenu.js +48 -0
  20. package/es/plugins/slash/react/components/SlashMenu.d.ts +2 -2
  21. package/es/plugins/slash/react/components/SlashMenu.js +16 -36
  22. package/es/plugins/table/react/TableActionMenu/ActionMenu.d.ts +19 -0
  23. package/es/plugins/table/react/TableActionMenu/ActionMenu.js +285 -0
  24. package/es/plugins/table/react/TableActionMenu/index.d.ts +2 -3
  25. package/es/plugins/table/react/TableActionMenu/index.js +27 -329
  26. package/es/plugins/table/react/TableActionMenu/style.js +1 -1
  27. package/es/plugins/table/react/TableHoverActions/index.d.ts +3 -4
  28. package/es/plugins/table/react/TableHoverActions/index.js +7 -20
  29. package/es/plugins/table/react/index.js +8 -5
  30. package/es/utils/updatePosition.d.ts +9 -0
  31. package/es/utils/updatePosition.js +27 -0
  32. package/package.json +1 -1
  33. /package/es/plugins/math/react/{component → components}/MathEditor.d.ts +0 -0
  34. /package/es/plugins/math/react/{component → components}/MathInline.d.ts +0 -0
  35. /package/es/plugins/math/react/{component → components}/MathInline.js +0 -0
  36. /package/es/plugins/math/react/{component → components}/Placeholder.d.ts +0 -0
  37. /package/es/plugins/math/react/{component → components}/Placeholder.js +0 -0
@@ -0,0 +1,5 @@
1
+ import { PropsWithChildren } from 'react';
2
+ declare const PortalAnchor: import("react").NamedExoticComponent<PropsWithChildren<{
3
+ anchorElem?: HTMLElement | undefined;
4
+ }>>;
5
+ export default PortalAnchor;
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+
3
+ import { memo } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { useAnchor } from "./useAnchor";
6
+ var PortalAnchor = /*#__PURE__*/memo(function (_ref) {
7
+ var children = _ref.children;
8
+ var targetElement = useAnchor();
9
+ if (!targetElement) return null;
10
+ return /*#__PURE__*/createPortal(children, targetElement);
11
+ });
12
+ PortalAnchor.displayName = 'PortalAnchor';
13
+ export default PortalAnchor;
@@ -0,0 +1 @@
1
+ export declare const useAnchor: () => HTMLElement | undefined;
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+
3
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
4
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
5
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
6
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
7
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
+ import { useMemo } from 'react';
10
+ import { useLexicalComposerContext } from "./react-context";
11
+ export var useAnchor = function useAnchor() {
12
+ var _useLexicalComposerCo = useLexicalComposerContext(),
13
+ _useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
14
+ editor = _useLexicalComposerCo2[0];
15
+ // Don't render portal on server side
16
+
17
+ return useMemo(function () {
18
+ if (typeof document === 'undefined' || !editor) return;
19
+ var root = editor.getRootElement();
20
+ var anchor = root ? root.parentElement : null;
21
+ return anchor || document.body;
22
+ }, [editor]);
23
+ };
@@ -6,25 +6,17 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
6
6
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
7
7
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
8
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
- import { computePosition, flip, offset, shift } from '@floating-ui/dom';
10
- import { mergeRegister } from '@lexical/utils';
11
- import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_NORMAL } from 'lexical';
12
- import { useLayoutEffect, useRef, useState } from 'react';
13
- import { createPortal } from 'react-dom';
14
- import { useLexicalEditor } from "../../../editor-kernel/react";
9
+ import { useLayoutEffect } from 'react';
10
+ import PortalAnchor from "../../../editor-kernel/react/PortalAnchor";
15
11
  import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
16
12
  import { MarkdownPlugin } from "../../markdown";
17
- import { $isLinkNode, HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "../node/LinkNode";
18
13
  import { LinkPlugin } from "../plugin";
19
- import { getSelectedNode } from "../utils";
20
- import LinkEdit, { EDIT_LINK_COMMAND } from "./components/LinkEdit";
14
+ import LinkEdit from "./components/LinkEdit";
21
15
  import LinkToolbar from "./components/LinkToolbar";
22
16
  import { useStyles } from "./style";
23
17
  import { jsx as _jsx } from "react/jsx-runtime";
24
- import { Fragment as _Fragment } from "react/jsx-runtime";
25
18
  import { jsxs as _jsxs } from "react/jsx-runtime";
26
19
  export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
27
- var _editor$getLexicalEdi, _lexicalEditor$getRoo;
28
20
  var theme = _ref.theme,
29
21
  _ref$enableHotkey = _ref.enableHotkey,
30
22
  enableHotkey = _ref$enableHotkey === void 0 ? true : _ref$enableHotkey,
@@ -33,16 +25,6 @@ export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
33
25
  var _useLexicalComposerCo = useLexicalComposerContext(),
34
26
  _useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
35
27
  editor = _useLexicalComposerCo2[0];
36
- var _useState = useState(null),
37
- _useState2 = _slicedToArray(_useState, 2),
38
- linkNode = _useState2[0],
39
- setLinkNode = _useState2[1];
40
- var state = useRef({
41
- isLink: false
42
- });
43
- var divRef = useRef(null);
44
- var LinkRef = useRef(null);
45
- var clearTimerRef = useRef(-1);
46
28
  var _useStyles = useStyles(),
47
29
  styles = _useStyles.styles;
48
30
  useLayoutEffect(function () {
@@ -54,90 +36,13 @@ export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
54
36
  validateUrl: validateUrl
55
37
  });
56
38
  }, [attributes, enableHotkey, styles, theme, validateUrl]);
57
- useLexicalEditor(function (editor) {
58
- return mergeRegister(editor.registerUpdateListener(function () {
59
- var selection = editor.read(function () {
60
- return $getSelection();
61
- });
62
- if (!selection) return;
63
- if ($isRangeSelection(selection)) {
64
- // Update links for UI components
65
- editor.read(function () {
66
- var node = getSelectedNode(selection);
67
- var parent = node.getParent();
68
- var isLink = $isLinkNode(parent) || $isLinkNode(node);
69
- state.current.isLink = isLink;
70
- if (isLink) {
71
- var _linkNode = $isLinkNode(parent) ? parent : node;
72
- editor.dispatchCommand(EDIT_LINK_COMMAND, {
73
- linkNode: _linkNode,
74
- linkNodeDOM: editor.getElementByKey(_linkNode.getKey())
75
- });
76
- } else {
77
- editor.dispatchCommand(EDIT_LINK_COMMAND, {
78
- linkNode: null,
79
- linkNodeDOM: null
80
- });
81
- }
82
- });
83
- } else {
84
- state.current.isLink = false;
85
- }
86
- }), editor.registerCommand(HOVER_LINK_COMMAND, function (payload) {
87
- if (!payload.event.target || divRef.current === null) {
88
- return false;
89
- }
90
- // Cancel any pending hide timers when hovering a link again
91
- clearTimeout(clearTimerRef.current);
92
- setLinkNode(payload.linkNode);
93
- computePosition(payload.event.target, divRef.current, {
94
- middleware: [offset(5), flip(), shift()],
95
- placement: 'top-start'
96
- }).then(function (_ref2) {
97
- var x = _ref2.x,
98
- y = _ref2.y;
99
- if (!payload.event.target || divRef.current === null) {
100
- return false;
101
- }
102
- LinkRef.current = payload.event.target;
103
- divRef.current.style.left = "".concat(x, "px");
104
- divRef.current.style.top = "".concat(y, "px");
105
- });
106
- return false;
107
- }, COMMAND_PRIORITY_NORMAL), editor.registerCommand(HOVER_OUT_LINK_COMMAND, function () {
108
- clearTimeout(clearTimerRef.current);
109
- clearTimerRef.current = setTimeout(function () {
110
- if (divRef.current) {
111
- divRef.current.style.left = '-9999px';
112
- divRef.current.style.top = '-9999px';
113
- }
114
- }, 300);
115
- return true;
116
- }, COMMAND_PRIORITY_NORMAL));
117
- }, []);
118
-
119
- // Determine anchor element (editor inner wrapper)
120
- var lexicalEditor = (_editor$getLexicalEdi = editor.getLexicalEditor) === null || _editor$getLexicalEdi === void 0 ? void 0 : _editor$getLexicalEdi.call(editor);
121
- var root = lexicalEditor === null || lexicalEditor === void 0 || (_lexicalEditor$getRoo = lexicalEditor.getRootElement) === null || _lexicalEditor$getRoo === void 0 ? void 0 : _lexicalEditor$getRoo.call(lexicalEditor);
122
- var anchor = root ? root.parentElement : null;
123
- var targetElement = anchor || (typeof document !== 'undefined' ? document.body : null);
124
- return targetElement ? /*#__PURE__*/createPortal( /*#__PURE__*/_jsxs(_Fragment, {
39
+ return /*#__PURE__*/_jsxs(PortalAnchor, {
125
40
  children: [/*#__PURE__*/_jsx(LinkToolbar, {
126
- editor: editor.getLexicalEditor(),
127
- linkNode: linkNode,
128
- onMouseEnter: function onMouseEnter() {
129
- clearTimeout(clearTimerRef.current);
130
- },
131
- onMouseLeave: function onMouseLeave() {
132
- clearTimeout(clearTimerRef.current);
133
- if (divRef.current) {
134
- divRef.current.style.left = '-9999px';
135
- divRef.current.style.top = '-9999px';
136
- }
137
- },
138
- ref: divRef
139
- }), /*#__PURE__*/_jsx(LinkEdit, {})]
140
- }), targetElement) : null;
41
+ editor: editor.getLexicalEditor()
42
+ }), /*#__PURE__*/_jsx(LinkEdit, {
43
+ editor: editor.getLexicalEditor()
44
+ })]
45
+ });
141
46
  };
142
47
  ReactLinkPlugin.displayName = 'ReactLinkPlugin';
143
48
  export default ReactLinkPlugin;
@@ -1,8 +1,12 @@
1
- import { type FC } from 'react';
1
+ /// <reference types="react" />
2
+ import { LexicalEditor } from 'lexical';
2
3
  import { LinkNode } from '../../node/LinkNode';
3
4
  export declare const EDIT_LINK_COMMAND: import("lexical").LexicalCommand<{
4
5
  linkNode: LinkNode | null;
5
6
  linkNodeDOM: HTMLElement | null;
6
7
  }>;
7
- declare const LinkEdit: FC;
8
+ interface LinkEditProps {
9
+ editor: LexicalEditor;
10
+ }
11
+ declare const LinkEdit: import("react").NamedExoticComponent<LinkEditProps>;
8
12
  export default LinkEdit;
@@ -4,21 +4,22 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
4
4
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
- import { computePosition, flip, offset, shift } from '@floating-ui/dom';
8
7
  import { mergeRegister } from '@lexical/utils';
9
8
  import { Block, Button, Hotkey, Icon, Input, Text } from '@lobehub/ui';
10
9
  import { COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_NORMAL, KEY_ESCAPE_COMMAND, KEY_TAB_COMMAND, createCommand } from 'lexical';
11
10
  import { BaselineIcon, LinkIcon } from 'lucide-react';
12
- import { useCallback, useEffect, useRef, useState } from 'react';
11
+ import { memo, useCallback, useEffect, useRef, useState } from 'react';
13
12
  import { Flexbox } from 'react-layout-kit';
14
- import { useLexicalComposerContext, useLexicalEditor } from "../../../../editor-kernel/react";
13
+ import { useLexicalEditor } from "../../../../editor-kernel/react";
15
14
  import { useTranslation } from "../../../../editor-kernel/react/useTranslation";
15
+ import { cleanPosition, updatePosition } from "../../../../utils/updatePosition";
16
16
  import { UPDATE_LINK_TEXT_COMMAND } from "../../command";
17
17
  import { useStyles } from "../style";
18
18
  import { jsx as _jsx } from "react/jsx-runtime";
19
19
  import { jsxs as _jsxs } from "react/jsx-runtime";
20
20
  export var EDIT_LINK_COMMAND = createCommand();
21
- var LinkEdit = function LinkEdit() {
21
+ var LinkEdit = /*#__PURE__*/memo(function (_ref) {
22
+ var editor = _ref.editor;
22
23
  var divRef = useRef(null);
23
24
  var linkNodeRef = useRef(null);
24
25
  var linkInputRef = useRef(null);
@@ -35,9 +36,6 @@ var LinkEdit = function LinkEdit() {
35
36
  _useState6 = _slicedToArray(_useState5, 2),
36
37
  linkDom = _useState6[0],
37
38
  setLinkDom = _useState6[1];
38
- var _useLexicalComposerCo = useLexicalComposerContext(),
39
- _useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
40
- editor = _useLexicalComposerCo2[0];
41
39
  var t = useTranslation();
42
40
  var _useStyles = useStyles(),
43
41
  styles = _useStyles.styles,
@@ -45,66 +43,18 @@ var LinkEdit = function LinkEdit() {
45
43
 
46
44
  // 取消编辑,不保存更改
47
45
  var handleCancel = useCallback(function () {
48
- var lexicalEditor = editor.getLexicalEditor();
49
- if (!lexicalEditor) return;
50
-
51
- // 将焦点返回到编辑器
52
- lexicalEditor.focus();
53
-
54
- // 隐藏编辑面板
55
- if (divRef.current) {
56
- divRef.current.style.left = '-9999px';
57
- divRef.current.style.top = '-9999px';
58
- }
46
+ if (!editor) return;
47
+ editor.focus();
48
+ cleanPosition(divRef.current);
59
49
  linkNodeRef.current = null;
60
50
  setLinkUrl('');
61
51
  setLinkText('');
62
52
  setLinkDom(null);
63
53
  }, [editor]);
64
- useEffect(function () {
65
- if (!linkDom || !divRef.current) {
66
- return;
67
- }
68
- computePosition(linkDom, divRef.current, {
69
- middleware: [offset(8), flip(), shift()],
70
- placement: 'bottom-start'
71
- }).then(function (_ref) {
72
- var x = _ref.x,
73
- y = _ref.y;
74
- if (divRef.current) {
75
- divRef.current.style.left = "".concat(x, "px");
76
- divRef.current.style.top = "".concat(y, "px");
77
- }
78
- });
79
- }, [linkDom]);
80
-
81
- // 点击编辑器外部时关闭面板
82
- useEffect(function () {
83
- var handlePointerDown = function handlePointerDown(event) {
84
- if (!divRef.current) return;
85
- var target = event.target;
86
- if (!target) return;
87
- // 点击面板内部忽略
88
- if (divRef.current.contains(target)) return;
89
- // 面板打开时(存在 linkDom)才触发关闭
90
- if (linkDom) {
91
- handleCancel();
92
- }
93
- };
94
- document.addEventListener('mousedown', handlePointerDown);
95
- document.addEventListener('touchstart', handlePointerDown);
96
- return function () {
97
- document.removeEventListener('mousedown', handlePointerDown);
98
- document.removeEventListener('touchstart', handlePointerDown);
99
- };
100
- }, [handleCancel, linkDom]);
101
54
 
102
55
  // 提取提交逻辑到独立函数
103
56
  var handleSubmit = useCallback(function () {
104
- var lexicalEditor = editor.getLexicalEditor();
105
- if (!linkNodeRef.current || !linkInputRef.current || !linkTextInputRef.current || !lexicalEditor) {
106
- return;
107
- }
57
+ if (!linkNodeRef.current || !linkInputRef.current || !linkTextInputRef.current || !editor) return;
108
58
  var linkNode = linkNodeRef.current;
109
59
  var input = linkInputRef.current;
110
60
  var inputDOM = input.input;
@@ -112,42 +62,34 @@ var LinkEdit = function LinkEdit() {
112
62
  var textInputDOM = textInput.input;
113
63
 
114
64
  // 更新链接URL
115
- var currentURL = lexicalEditor.read(function () {
65
+ var currentURL = editor.read(function () {
116
66
  return linkNode.getURL();
117
67
  });
118
68
  if (currentURL !== inputDOM.value) {
119
- lexicalEditor.update(function () {
69
+ editor.update(function () {
120
70
  linkNode.setURL(inputDOM.value);
121
71
  });
122
72
  }
123
73
 
124
74
  // 更新链接文本
125
- var currentText = lexicalEditor.read(function () {
75
+ var currentText = editor.read(function () {
126
76
  return linkNode.getTextContent();
127
77
  });
128
78
  if (currentText !== textInputDOM.value) {
129
- lexicalEditor.dispatchCommand(UPDATE_LINK_TEXT_COMMAND, {
79
+ editor.dispatchCommand(UPDATE_LINK_TEXT_COMMAND, {
130
80
  key: linkNode.getKey(),
131
81
  text: textInputDOM.value
132
82
  });
133
83
  }
134
84
 
135
85
  // 关闭编辑器并聚焦到编辑器
136
- lexicalEditor.focus();
86
+ editor.focus();
137
87
 
138
88
  // 隐藏编辑面板
139
- if (divRef.current) {
140
- divRef.current.style.left = '-9999px';
141
- divRef.current.style.top = '-9999px';
142
- }
143
- linkNodeRef.current = null;
144
- setLinkUrl('');
145
- setLinkText('');
146
- setLinkDom(null);
147
- }, [editor, linkNodeRef, linkInputRef, linkTextInputRef]);
89
+ handleCancel();
90
+ }, [editor, linkNodeRef, linkInputRef, linkTextInputRef, handleCancel]);
148
91
  var handleKeyDown = useCallback(function (event) {
149
- var lexicalEditor = editor.getLexicalEditor();
150
- if (!linkNodeRef.current || !linkInputRef.current || !linkTextInputRef.current || !lexicalEditor) {
92
+ if (!linkNodeRef.current || !linkInputRef.current || !linkTextInputRef.current || !editor) {
151
93
  return;
152
94
  }
153
95
  var linkNode = linkNodeRef.current;
@@ -160,11 +102,11 @@ var LinkEdit = function LinkEdit() {
160
102
  {
161
103
  event.preventDefault();
162
104
  if (event.currentTarget === textInputDOM) {
163
- var currentText = lexicalEditor.read(function () {
105
+ var currentText = editor.read(function () {
164
106
  return linkNode.getTextContent();
165
107
  });
166
108
  if (currentText !== textInputDOM.value) {
167
- lexicalEditor.dispatchCommand(UPDATE_LINK_TEXT_COMMAND, {
109
+ editor.dispatchCommand(UPDATE_LINK_TEXT_COMMAND, {
168
110
  key: linkNode.getKey(),
169
111
  text: textInputDOM.value
170
112
  });
@@ -186,7 +128,7 @@ var LinkEdit = function LinkEdit() {
186
128
  if (event.currentTarget === textInputDOM) {
187
129
  inputDOM.focus();
188
130
  } else {
189
- lexicalEditor.focus();
131
+ editor.focus();
190
132
  }
191
133
  return;
192
134
  }
@@ -199,16 +141,35 @@ var LinkEdit = function LinkEdit() {
199
141
  // No default
200
142
  }
201
143
  }, [linkNodeRef, linkInputRef, handleSubmit, handleCancel]);
144
+ useEffect(function () {
145
+ updatePosition({
146
+ floating: divRef.current,
147
+ reference: linkDom
148
+ });
149
+ }, [linkDom]);
150
+
151
+ // 点击编辑器外部时关闭面板
152
+ useEffect(function () {
153
+ var handlePointerDown = function handlePointerDown(event) {
154
+ if (!divRef.current) return;
155
+ var target = event.target;
156
+ if (!target) return;
157
+ // 点击面板内部忽略
158
+ if (divRef.current.contains(target)) return;
159
+ // 面板打开时(存在 linkDom)才触发关闭
160
+ if (linkDom) handleCancel();
161
+ };
162
+ document.addEventListener('mousedown', handlePointerDown);
163
+ document.addEventListener('touchstart', handlePointerDown);
164
+ return function () {
165
+ document.removeEventListener('mousedown', handlePointerDown);
166
+ document.removeEventListener('touchstart', handlePointerDown);
167
+ };
168
+ }, [linkDom]);
202
169
  useLexicalEditor(function (editor) {
203
170
  return mergeRegister(editor.registerCommand(EDIT_LINK_COMMAND, function (payload) {
204
171
  if (!payload.linkNode || !payload.linkNodeDOM) {
205
- setLinkDom(null);
206
- setLinkUrl('');
207
- setLinkText('');
208
- if (divRef.current) {
209
- divRef.current.style.left = '-9999px';
210
- divRef.current.style.top = '-9999px';
211
- }
172
+ handleCancel();
212
173
  return false;
213
174
  }
214
175
  linkNodeRef.current = payload.linkNode;
@@ -217,14 +178,7 @@ var LinkEdit = function LinkEdit() {
217
178
  setLinkDom(payload.linkNodeDOM);
218
179
  return true;
219
180
  }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_ESCAPE_COMMAND, function () {
220
- if (divRef.current) {
221
- divRef.current.style.left = '-9999px';
222
- divRef.current.style.top = '-9999px';
223
- }
224
- linkNodeRef.current = null;
225
- setLinkUrl('');
226
- setLinkText('');
227
- setLinkDom(null);
181
+ handleCancel();
228
182
  return true;
229
183
  }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_TAB_COMMAND, function (payload) {
230
184
  if (linkNodeRef.current && linkTextInputRef.current) {
@@ -298,5 +252,6 @@ var LinkEdit = function LinkEdit() {
298
252
  })
299
253
  })]
300
254
  });
301
- };
255
+ });
256
+ LinkEdit.displayName = 'LinkEdit';
302
257
  export default LinkEdit;
@@ -1,10 +1,7 @@
1
1
  /// <reference types="react" />
2
- import { type ActionIconGroupProps } from '@lobehub/ui';
3
2
  import { LexicalEditor } from 'lexical';
4
- import { LinkNode } from '../../node/LinkNode';
5
- interface LinkToolbarProps extends Omit<ActionIconGroupProps, 'items'> {
3
+ interface LinkToolbarProps {
6
4
  editor: LexicalEditor;
7
- linkNode: LinkNode | null;
8
5
  }
9
6
  declare const LinkToolbar: import("react").NamedExoticComponent<LinkToolbarProps>;
10
7
  export default LinkToolbar;
@@ -1,32 +1,38 @@
1
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
- var _excluded = ["linkNode", "editor", "onMouseLeave"];
3
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
- function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
7
- function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
8
1
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
2
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
3
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
9
4
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
10
5
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
11
- function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
12
- function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
6
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
7
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
8
+ import { mergeRegister } from '@lexical/utils';
13
9
  import { ActionIconGroup } from '@lobehub/ui';
14
- import { $createRangeSelection, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, $setSelection } from 'lexical';
10
+ import { $createRangeSelection, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_NORMAL } from 'lexical';
15
11
  import { EditIcon, ExternalLinkIcon, UnlinkIcon } from 'lucide-react';
16
- import { memo, useCallback } from 'react';
12
+ import { memo, useCallback, useRef, useState } from 'react';
13
+ import { useLexicalEditor } from "../../../../editor-kernel/react";
17
14
  import { useTranslation } from "../../../../editor-kernel/react/useTranslation";
18
- import { $isLinkNode, TOGGLE_LINK_COMMAND } from "../../node/LinkNode";
15
+ import { getSelectedNode } from "../../utils";
16
+ import { cleanPosition, updatePosition } from "../../../../utils/updatePosition";
17
+ import { $isLinkNode, HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND, TOGGLE_LINK_COMMAND } from "../../node/LinkNode";
19
18
  import { useStyles } from "../style";
20
19
  import { EDIT_LINK_COMMAND } from "./LinkEdit";
21
20
  import { jsx as _jsx } from "react/jsx-runtime";
22
21
  var LinkToolbar = /*#__PURE__*/memo(function (_ref) {
23
- var linkNode = _ref.linkNode,
24
- editor = _ref.editor,
25
- onMouseLeave = _ref.onMouseLeave,
26
- rest = _objectWithoutProperties(_ref, _excluded);
22
+ var editor = _ref.editor;
23
+ var divRef = useRef(null);
24
+ var LinkRef = useRef(null);
27
25
  var _useStyles = useStyles(),
28
26
  styles = _useStyles.styles;
27
+ var _useState = useState(null),
28
+ _useState2 = _slicedToArray(_useState, 2),
29
+ linkNode = _useState2[0],
30
+ setLinkNode = _useState2[1];
31
+ var state = useRef({
32
+ isLink: false
33
+ });
29
34
  var t = useTranslation();
35
+ var clearTimerRef = useRef(-1);
30
36
  var handleEdit = useCallback(function () {
31
37
  if (!linkNode) return;
32
38
  editor.dispatchCommand(EDIT_LINK_COMMAND, {
@@ -34,6 +40,10 @@ var LinkToolbar = /*#__PURE__*/memo(function (_ref) {
34
40
  linkNodeDOM: editor.getElementByKey(linkNode.getKey())
35
41
  });
36
42
  }, [editor, linkNode]);
43
+ var handleCancel = useCallback(function () {
44
+ clearTimeout(clearTimerRef.current);
45
+ cleanPosition(divRef.current);
46
+ }, []);
37
47
  var handleRemove = useCallback(function () {
38
48
  if (!linkNode) return;
39
49
  editor.update(function () {
@@ -79,7 +89,56 @@ var LinkToolbar = /*#__PURE__*/memo(function (_ref) {
79
89
  });
80
90
  window.open(url, '_blank');
81
91
  }, [editor, linkNode]);
82
- return /*#__PURE__*/_jsx(ActionIconGroup, _objectSpread({
92
+ useLexicalEditor(function (editor) {
93
+ return mergeRegister(editor.registerUpdateListener(function () {
94
+ var selection = editor.read(function () {
95
+ return $getSelection();
96
+ });
97
+ if (!selection) return;
98
+ if ($isRangeSelection(selection)) {
99
+ // Update links for UI components
100
+ editor.read(function () {
101
+ var node = getSelectedNode(selection);
102
+ var parent = node.getParent();
103
+ var isLink = $isLinkNode(parent) || $isLinkNode(node);
104
+ state.current.isLink = isLink;
105
+ if (isLink) {
106
+ var _linkNode = $isLinkNode(parent) ? parent : node;
107
+ editor.dispatchCommand(EDIT_LINK_COMMAND, {
108
+ linkNode: _linkNode,
109
+ linkNodeDOM: editor.getElementByKey(_linkNode.getKey())
110
+ });
111
+ } else {
112
+ editor.dispatchCommand(EDIT_LINK_COMMAND, {
113
+ linkNode: null,
114
+ linkNodeDOM: null
115
+ });
116
+ }
117
+ });
118
+ } else {
119
+ state.current.isLink = false;
120
+ }
121
+ }), editor.registerCommand(HOVER_LINK_COMMAND, function (payload) {
122
+ if (!payload.event.target || divRef.current === null) return false;
123
+ // Cancel any pending hide timers when hovering a link again
124
+ clearTimeout(clearTimerRef.current);
125
+ setLinkNode(payload.linkNode);
126
+ updatePosition({
127
+ callback: function callback() {
128
+ LinkRef.current = payload.event.target;
129
+ },
130
+ floating: divRef.current,
131
+ offset: 4,
132
+ placement: 'top-start',
133
+ reference: payload.event.target
134
+ });
135
+ return false;
136
+ }, COMMAND_PRIORITY_NORMAL), editor.registerCommand(HOVER_OUT_LINK_COMMAND, function () {
137
+ clearTimerRef.current = setTimeout(handleCancel, 300);
138
+ return true;
139
+ }, COMMAND_PRIORITY_NORMAL));
140
+ }, []);
141
+ return /*#__PURE__*/_jsx(ActionIconGroup, {
83
142
  className: styles.linkToolbar,
84
143
  items: [{
85
144
  icon: EditIcon,
@@ -95,18 +154,25 @@ var LinkToolbar = /*#__PURE__*/memo(function (_ref) {
95
154
  icon: UnlinkIcon,
96
155
  key: 'unlink',
97
156
  label: t('link.unlink'),
98
- onClick: function onClick(e) {
157
+ onClick: function onClick() {
99
158
  handleRemove();
100
- onMouseLeave === null || onMouseLeave === void 0 || onMouseLeave(e);
159
+ handleCancel();
101
160
  }
102
161
  }],
103
- onMouseLeave: onMouseLeave,
162
+ onMouseEnter: function onMouseEnter() {
163
+ clearTimeout(clearTimerRef.current);
164
+ },
165
+ onMouseLeave: function onMouseLeave() {
166
+ handleCancel();
167
+ },
168
+ ref: divRef,
104
169
  shadow: true,
105
170
  size: {
106
171
  blockSize: 32,
107
172
  size: 16
108
173
  },
109
174
  variant: 'outlined'
110
- }, rest));
175
+ });
111
176
  });
177
+ LinkToolbar.displayName = 'LinkToolbar';
112
178
  export default LinkToolbar;
@@ -5,7 +5,7 @@ export var useStyles = createStyles(function (_ref) {
5
5
  var cx = _ref.cx,
6
6
  css = _ref.css,
7
7
  token = _ref.token;
8
- var position = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: absolute;\n z-index: 999;\n inset-block-start: -9999px;\n inset-inline-start: -9999px;\n "])));
8
+ var position = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: absolute;\n z-index: 999;\n "])));
9
9
  return {
10
10
  link: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n cursor: pointer;\n\n margin-block: 1em;\n margin-inline: 0;\n padding: 2px;\n border: none;\n "]))),
11
11
  linkEdit: cx(position, css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n min-width: 320px;\n max-width: 100%;\n background: ", ";\n "])), token.colorBgElevated)),