@lobehub/editor 1.4.7 → 1.5.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.
- package/es/editor-kernel/kernel.d.ts +4 -1
- package/es/editor-kernel/kernel.js +56 -1
- package/es/index.d.ts +2 -0
- package/es/index.js +2 -0
- package/es/locale/index.d.ts +7 -0
- package/es/locale/index.js +7 -0
- package/es/plugins/code/command/index.d.ts +3 -0
- package/es/plugins/code/command/index.js +48 -0
- package/es/plugins/code/index.d.ts +3 -0
- package/es/plugins/code/index.js +3 -0
- package/es/plugins/code/node/code.d.ts +19 -0
- package/es/plugins/{common/node/LexicalLineBreakNode.js → code/node/code.js} +93 -95
- package/es/plugins/code/plugin/index.d.ts +5 -0
- package/es/plugins/{common/node/ParagraphNode.js → code/plugin/index.js} +40 -33
- package/es/plugins/code/plugin/registry.d.ts +3 -0
- package/es/plugins/code/plugin/registry.js +48 -0
- package/es/plugins/code/react/CodeReactPlugin.d.ts +4 -0
- package/es/plugins/code/react/CodeReactPlugin.js +31 -0
- package/es/plugins/code/react/index.d.ts +1 -0
- package/es/plugins/code/react/index.js +1 -0
- package/es/plugins/code/react/style.d.ts +3 -0
- package/es/plugins/code/react/style.js +10 -0
- package/es/plugins/code/react/type.d.ts +3 -0
- package/es/plugins/code/react/type.js +1 -0
- package/es/plugins/common/data-source/json-data-source.js +42 -5
- package/es/plugins/common/data-source/text-data-source.js +2 -2
- package/es/plugins/common/index.d.ts +1 -0
- package/es/plugins/common/index.js +1 -0
- package/es/plugins/common/node/ElementDOMSlot.d.ts +3 -5
- package/es/plugins/common/node/ElementDOMSlot.js +29 -56
- package/es/plugins/common/node/cursor.d.ts +12 -0
- package/es/plugins/common/node/cursor.js +303 -0
- package/es/plugins/common/plugin/index.js +5 -9
- package/es/plugins/common/plugin/register.d.ts +3 -1
- package/es/plugins/common/plugin/register.js +46 -13
- package/es/plugins/link/command/index.d.ts +5 -1
- package/es/plugins/link/command/index.js +21 -3
- package/es/plugins/link/react/ReactLinkPlugin.js +6 -9
- package/es/plugins/link/react/components/LinkEdit.d.ts +2 -1
- package/es/plugins/link/react/components/LinkEdit.js +195 -51
- package/es/plugins/link/react/components/LinkToolbar.d.ts +10 -0
- package/es/plugins/link/react/components/LinkToolbar.js +73 -0
- package/es/plugins/link/react/style.d.ts +3 -2
- package/es/plugins/link/react/style.js +8 -5
- package/es/plugins/list/command/index.d.ts +1 -0
- package/es/plugins/list/command/index.js +1 -0
- package/es/plugins/list/plugin/checkList.d.ts +3 -0
- package/es/plugins/list/plugin/checkList.js +167 -0
- package/es/plugins/list/plugin/index.js +9 -2
- package/es/plugins/list/react/style.js +4 -2
- package/es/plugins/markdown/data-source/markdown-data-source.js +5 -1
- package/es/plugins/markdown/service/shortcut.d.ts +3 -3
- package/es/plugins/math/command/index.d.ts +13 -0
- package/es/plugins/math/command/index.js +47 -0
- package/es/plugins/math/index.d.ts +3 -0
- package/es/plugins/math/index.js +3 -0
- package/es/plugins/math/node/index.d.ts +45 -0
- package/es/plugins/math/node/index.js +259 -0
- package/es/plugins/math/plugin/index.d.ts +11 -0
- package/es/plugins/math/plugin/index.js +103 -0
- package/es/plugins/math/react/component/MathEditor.d.ts +10 -0
- package/es/plugins/math/react/component/MathEditor.js +253 -0
- package/es/plugins/math/react/component/MathEditorContainer.d.ts +14 -0
- package/es/plugins/math/react/component/MathEditorContainer.js +60 -0
- package/es/plugins/math/react/component/MathEditorContent.d.ts +26 -0
- package/es/plugins/math/react/component/MathEditorContent.js +163 -0
- package/es/plugins/math/react/component/MathInline.d.ts +10 -0
- package/es/plugins/math/react/component/MathInline.js +105 -0
- package/es/plugins/math/react/component/Placeholder.d.ts +3 -0
- package/es/plugins/math/react/component/Placeholder.js +19 -0
- package/es/plugins/math/react/index.d.ts +3 -0
- package/es/plugins/math/react/index.js +43 -0
- package/es/plugins/math/react/style.d.ts +6 -0
- package/es/plugins/math/react/style.js +16 -0
- package/es/plugins/math/react/type.d.ts +13 -0
- package/es/plugins/math/react/type.js +1 -0
- package/es/plugins/slash/react/ReactSlashPlugin.js +12 -10
- package/es/react/FloatMenu/FloatMenu.d.ts +4 -0
- package/es/react/FloatMenu/FloatMenu.js +49 -0
- package/es/react/FloatMenu/index.d.ts +2 -0
- package/es/react/FloatMenu/index.js +2 -0
- package/es/react/FloatMenu/type.d.ts +19 -0
- package/es/react/FloatMenu/type.js +1 -0
- package/es/react/SlashMenu/SlashMenu.js +32 -48
- package/es/react/SlashMenu/type.d.ts +5 -13
- package/es/react/hooks/useEditorState/index.d.ts +4 -0
- package/es/react/hooks/useEditorState/index.js +40 -4
- package/es/react/index.d.ts +1 -0
- package/es/react/index.js +1 -0
- package/es/types/global.d.ts +4 -0
- package/es/types/kernel.d.ts +26 -1
- package/package.json +2 -1
- package/es/plugins/common/node/LexicalLineBreakNode.d.ts +0 -31
- package/es/plugins/common/node/ParagraphNode.d.ts +0 -7
- package/es/plugins/link/react/components/Toolbar.d.ts +0 -7
- package/es/plugins/link/react/components/Toolbar.js +0 -63
- /package/es/react/{SlashMenu → FloatMenu}/style.d.ts +0 -0
- /package/es/react/{SlashMenu → FloatMenu}/style.js +0 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
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."); }
|
|
3
|
+
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); }
|
|
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
|
+
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
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { mergeRegister } from '@lexical/utils';
|
|
8
|
+
import { $getSelection, $isElementNode, $isNodeSelection, $isRangeSelection, $isTextNode } from 'lexical';
|
|
9
|
+
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
10
|
+
import { useLexicalComposerContext, useLexicalEditor } from "../../../../editor-kernel/react";
|
|
11
|
+
import { SELECT_MATH_SIDE_COMMAND, UPDATE_MATH_COMMAND } from "../../command";
|
|
12
|
+
import { $isMathNode, MathBlockNode } from "../../node";
|
|
13
|
+
import { MathEditorContainer } from "./MathEditorContainer";
|
|
14
|
+
import { MathEditorContent } from "./MathEditorContent";
|
|
15
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
17
|
+
var MathEdit = /*#__PURE__*/memo(function (_ref) {
|
|
18
|
+
var renderComp = _ref.renderComp;
|
|
19
|
+
var textareaRef = useRef(null);
|
|
20
|
+
var isUpdatingRef = useRef(false);
|
|
21
|
+
var _useState = useState(null),
|
|
22
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
23
|
+
mathNode = _useState2[0],
|
|
24
|
+
setMathNode = _useState2[1];
|
|
25
|
+
var _useState3 = useState(''),
|
|
26
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
27
|
+
value = _useState4[0],
|
|
28
|
+
setValue = _useState4[1];
|
|
29
|
+
var _useState5 = useState(null),
|
|
30
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
31
|
+
mathDOM = _useState6[0],
|
|
32
|
+
setMathDOM = _useState6[1];
|
|
33
|
+
var _useState7 = useState(false),
|
|
34
|
+
_useState8 = _slicedToArray(_useState7, 2),
|
|
35
|
+
prev = _useState8[0],
|
|
36
|
+
setPrev = _useState8[1];
|
|
37
|
+
var _useState9 = useState(false),
|
|
38
|
+
_useState10 = _slicedToArray(_useState9, 2),
|
|
39
|
+
isBlockMode = _useState10[0],
|
|
40
|
+
setIsBlockMode = _useState10[1];
|
|
41
|
+
var _useState11 = useState(false),
|
|
42
|
+
_useState12 = _slicedToArray(_useState11, 2),
|
|
43
|
+
isOpen = _useState12[0],
|
|
44
|
+
setIsOpen = _useState12[1];
|
|
45
|
+
var _useLexicalComposerCo = useLexicalComposerContext(),
|
|
46
|
+
_useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
|
|
47
|
+
editor = _useLexicalComposerCo2[0];
|
|
48
|
+
|
|
49
|
+
// 实时更新节点内容
|
|
50
|
+
useEffect(function () {
|
|
51
|
+
if (!mathNode) return;
|
|
52
|
+
|
|
53
|
+
// 使用防抖来避免过于频繁的更新
|
|
54
|
+
var timeoutId = setTimeout(function () {
|
|
55
|
+
// 直接更新节点内容
|
|
56
|
+
var lexicalEditor = editor.getLexicalEditor();
|
|
57
|
+
if (lexicalEditor && !isUpdatingRef.current) {
|
|
58
|
+
// 检查当前值是否与节点中的值不同,避免不必要的更新
|
|
59
|
+
var currentCode = mathNode.code;
|
|
60
|
+
if (currentCode === value) {
|
|
61
|
+
return; // 值相同,无需更新
|
|
62
|
+
}
|
|
63
|
+
isUpdatingRef.current = true;
|
|
64
|
+
lexicalEditor.update(function () {
|
|
65
|
+
var currentNode = lexicalEditor.getEditorState().read(function () {
|
|
66
|
+
return lexicalEditor.getElementByKey(mathNode.getKey());
|
|
67
|
+
});
|
|
68
|
+
if (currentNode) {
|
|
69
|
+
var writableNode = mathNode.getWritable();
|
|
70
|
+
writableNode.__code = value;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// 延迟重置更新标志,确保更新监听器不会立即触发
|
|
74
|
+
setTimeout(function () {
|
|
75
|
+
isUpdatingRef.current = false;
|
|
76
|
+
}, 100);
|
|
77
|
+
}
|
|
78
|
+
}, 100); // 增加防抖延迟
|
|
79
|
+
|
|
80
|
+
return function () {
|
|
81
|
+
return clearTimeout(timeoutId);
|
|
82
|
+
};
|
|
83
|
+
}, [value, mathNode, editor]);
|
|
84
|
+
|
|
85
|
+
// 提交逻辑
|
|
86
|
+
var handleSubmit = useCallback(function () {
|
|
87
|
+
if (!mathNode) return;
|
|
88
|
+
var lexicalEditor = editor.getLexicalEditor();
|
|
89
|
+
if (lexicalEditor) {
|
|
90
|
+
// 提交时总是使用原始的 value,不包含错误标记
|
|
91
|
+
lexicalEditor.dispatchCommand(UPDATE_MATH_COMMAND, {
|
|
92
|
+
code: value,
|
|
93
|
+
key: mathNode.getKey()
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}, [editor, mathNode, value]);
|
|
97
|
+
|
|
98
|
+
// 取消编辑,不保存更改
|
|
99
|
+
var handleCancel = useCallback(function () {
|
|
100
|
+
if (!mathNode) return;
|
|
101
|
+
var lexicalEditor = editor.getLexicalEditor();
|
|
102
|
+
if (lexicalEditor) {
|
|
103
|
+
// 使用命令来正确设置光标位置到数学节点之后
|
|
104
|
+
lexicalEditor.dispatchCommand(SELECT_MATH_SIDE_COMMAND, {
|
|
105
|
+
key: mathNode.getKey(),
|
|
106
|
+
prev: false
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// 将焦点返回到编辑器
|
|
110
|
+
lexicalEditor.focus();
|
|
111
|
+
}
|
|
112
|
+
}, [mathNode, editor]);
|
|
113
|
+
|
|
114
|
+
// 删除节点
|
|
115
|
+
var handleDelete = useCallback(function () {
|
|
116
|
+
if (!mathNode) return;
|
|
117
|
+
var lexicalEditor = editor.getLexicalEditor();
|
|
118
|
+
if (lexicalEditor) {
|
|
119
|
+
lexicalEditor.update(function () {
|
|
120
|
+
mathNode.remove();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}, [mathNode, editor]);
|
|
124
|
+
|
|
125
|
+
// 左箭头导航
|
|
126
|
+
var handleArrowLeft = useCallback(function () {
|
|
127
|
+
if (!mathNode) return;
|
|
128
|
+
editor.dispatchCommand(SELECT_MATH_SIDE_COMMAND, {
|
|
129
|
+
key: mathNode.getKey(),
|
|
130
|
+
prev: true
|
|
131
|
+
});
|
|
132
|
+
}, [mathNode, editor]);
|
|
133
|
+
|
|
134
|
+
// 右箭头导航
|
|
135
|
+
var handleArrowRight = useCallback(function () {
|
|
136
|
+
if (!mathNode) return;
|
|
137
|
+
editor.dispatchCommand(SELECT_MATH_SIDE_COMMAND, {
|
|
138
|
+
key: mathNode.getKey(),
|
|
139
|
+
prev: false
|
|
140
|
+
});
|
|
141
|
+
}, [mathNode, editor]);
|
|
142
|
+
|
|
143
|
+
// 处理焦点
|
|
144
|
+
var handleFocus = useCallback(function () {
|
|
145
|
+
var _textareaRef$current;
|
|
146
|
+
(_textareaRef$current = textareaRef.current) === null || _textareaRef$current === void 0 || _textareaRef$current.focus();
|
|
147
|
+
if (prev) {
|
|
148
|
+
var _textareaRef$current2;
|
|
149
|
+
(_textareaRef$current2 = textareaRef.current) === null || _textareaRef$current2 === void 0 || (_textareaRef$current2 = _textareaRef$current2.resizableTextArea) === null || _textareaRef$current2 === void 0 || (_textareaRef$current2 = _textareaRef$current2.textArea) === null || _textareaRef$current2 === void 0 || _textareaRef$current2.setSelectionRange(0, 0);
|
|
150
|
+
}
|
|
151
|
+
}, [prev]);
|
|
152
|
+
|
|
153
|
+
// 设置 textarea ref
|
|
154
|
+
var setTextareaRef = useCallback(function (ref) {
|
|
155
|
+
textareaRef.current = ref;
|
|
156
|
+
}, []);
|
|
157
|
+
useLexicalEditor(function (editor) {
|
|
158
|
+
return mergeRegister(editor.registerUpdateListener(function (_ref2) {
|
|
159
|
+
var prevEditorState = _ref2.prevEditorState;
|
|
160
|
+
// 如果正在更新中,跳过此次监听器调用
|
|
161
|
+
if (isUpdatingRef.current) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Handle editor state updates
|
|
166
|
+
var canEdit = editor.read(function () {
|
|
167
|
+
var selection = $getSelection();
|
|
168
|
+
if (!$isNodeSelection(selection)) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
var node = selection.getNodes()[0];
|
|
172
|
+
if (!$isMathNode(node)) {
|
|
173
|
+
// Handle math node
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 检查是否是同一个节点,避免不必要的状态更新
|
|
178
|
+
var isSameNode = mathNode && mathNode.getKey() === node.getKey();
|
|
179
|
+
setMathNode(node);
|
|
180
|
+
// 只有在不是同一个节点或值真正改变时才更新 value
|
|
181
|
+
if (!isSameNode && node.code !== value) {
|
|
182
|
+
setValue(node.code);
|
|
183
|
+
}
|
|
184
|
+
setMathDOM(editor.getElementByKey(node.getKey()));
|
|
185
|
+
setIsBlockMode(node instanceof MathBlockNode);
|
|
186
|
+
return node;
|
|
187
|
+
});
|
|
188
|
+
if (canEdit) {
|
|
189
|
+
var node = prevEditorState.read(function () {
|
|
190
|
+
var sel = prevEditorState._selection;
|
|
191
|
+
if (!$isRangeSelection(sel) || !sel.isCollapsed()) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
var node = sel.anchor.getNode();
|
|
195
|
+
if ($isTextNode(node)) {
|
|
196
|
+
return node.getNextSibling();
|
|
197
|
+
}
|
|
198
|
+
if (!$isElementNode(node)) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return node.getChildAtIndex(sel.anchor.offset);
|
|
202
|
+
});
|
|
203
|
+
if (canEdit === node) {
|
|
204
|
+
setPrev(true);
|
|
205
|
+
} else {
|
|
206
|
+
setPrev(false);
|
|
207
|
+
}
|
|
208
|
+
setIsOpen(true);
|
|
209
|
+
}
|
|
210
|
+
if (!canEdit) {
|
|
211
|
+
setMathNode(null);
|
|
212
|
+
setMathDOM(null);
|
|
213
|
+
setValue('');
|
|
214
|
+
setIsBlockMode(false);
|
|
215
|
+
setIsOpen(false);
|
|
216
|
+
}
|
|
217
|
+
}));
|
|
218
|
+
}, [mathNode, value]);
|
|
219
|
+
|
|
220
|
+
// 构建 MathEditorContent 组件
|
|
221
|
+
var mathEditorContent = /*#__PURE__*/_jsx(MathEditorContent, {
|
|
222
|
+
focusRef: setTextareaRef,
|
|
223
|
+
mathNode: mathNode,
|
|
224
|
+
onArrowLeft: handleArrowLeft,
|
|
225
|
+
onArrowRight: handleArrowRight,
|
|
226
|
+
onCancel: handleCancel,
|
|
227
|
+
onDelete: handleDelete,
|
|
228
|
+
onSubmit: handleSubmit,
|
|
229
|
+
onValueChange: setValue,
|
|
230
|
+
prev: prev,
|
|
231
|
+
value: value
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// 如果有自定义渲染组件,使用它来包装 MathEditorContent
|
|
235
|
+
if (renderComp) {
|
|
236
|
+
return /*#__PURE__*/_jsx(_Fragment, {
|
|
237
|
+
children: renderComp({
|
|
238
|
+
children: mathEditorContent,
|
|
239
|
+
open: isOpen
|
|
240
|
+
})
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 否则使用默认的 MathEditorContainer
|
|
245
|
+
return /*#__PURE__*/_jsx(MathEditorContainer, {
|
|
246
|
+
isBlockMode: isBlockMode,
|
|
247
|
+
mathDOM: mathDOM,
|
|
248
|
+
onFocus: handleFocus,
|
|
249
|
+
prev: prev,
|
|
250
|
+
children: mathEditorContent
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
export default MathEdit;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface MathEditorContainerProps {
|
|
3
|
+
/** 子组件内容 */
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
/** 是否为块模式 */
|
|
6
|
+
isBlockMode: boolean;
|
|
7
|
+
/** 数学节点对应的 DOM 元素 */
|
|
8
|
+
mathDOM: HTMLElement | null;
|
|
9
|
+
/** 焦点回调 */
|
|
10
|
+
onFocus?: () => void;
|
|
11
|
+
/** 是否从前一个位置进入 */
|
|
12
|
+
prev: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const MathEditorContainer: import("react").NamedExoticComponent<MathEditorContainerProps>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { computePosition, flip, offset, shift } from '@floating-ui/dom';
|
|
2
|
+
import { Block } from '@lobehub/ui';
|
|
3
|
+
import { memo, useEffect, useRef } from 'react';
|
|
4
|
+
import { useStyles } from "../style";
|
|
5
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
|
+
export var MathEditorContainer = /*#__PURE__*/memo(function (_ref) {
|
|
7
|
+
var children = _ref.children,
|
|
8
|
+
isBlockMode = _ref.isBlockMode,
|
|
9
|
+
mathDOM = _ref.mathDOM,
|
|
10
|
+
onFocus = _ref.onFocus,
|
|
11
|
+
prev = _ref.prev;
|
|
12
|
+
var divRef = useRef(null);
|
|
13
|
+
var _useStyles = useStyles(),
|
|
14
|
+
styles = _useStyles.styles;
|
|
15
|
+
useEffect(function () {
|
|
16
|
+
if (!mathDOM || !divRef.current) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Inline 模式下使用默认的 floating-ui 定位
|
|
21
|
+
computePosition(mathDOM, divRef.current, {
|
|
22
|
+
middleware: [offset(8), flip(), shift()],
|
|
23
|
+
placement: 'bottom-start'
|
|
24
|
+
}).then(function (_ref2) {
|
|
25
|
+
var x = _ref2.x,
|
|
26
|
+
y = _ref2.y;
|
|
27
|
+
if (divRef.current) {
|
|
28
|
+
divRef.current.style.left = "".concat(x, "px");
|
|
29
|
+
divRef.current.style.top = "".concat(y, "px");
|
|
30
|
+
divRef.current.style.width = ''; // 重置宽度
|
|
31
|
+
onFocus === null || onFocus === void 0 || onFocus();
|
|
32
|
+
}
|
|
33
|
+
if (isBlockMode && divRef.current) {
|
|
34
|
+
// Block 模式下,获取主编辑器容器的位置和宽度
|
|
35
|
+
var editorContainer = mathDOM.closest('[contenteditable="true"]');
|
|
36
|
+
if (editorContainer) {
|
|
37
|
+
var containerRect = editorContainer.getBoundingClientRect();
|
|
38
|
+
divRef.current.style.width = "".concat(containerRect.width, "px");
|
|
39
|
+
onFocus === null || onFocus === void 0 || onFocus();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}, [mathDOM, prev, isBlockMode, onFocus]);
|
|
44
|
+
|
|
45
|
+
// 当没有 mathDOM 时,隐藏容器
|
|
46
|
+
useEffect(function () {
|
|
47
|
+
if (!mathDOM && divRef.current) {
|
|
48
|
+
divRef.current.style.left = "-9999px";
|
|
49
|
+
divRef.current.style.top = "-9999px";
|
|
50
|
+
}
|
|
51
|
+
}, [mathDOM]);
|
|
52
|
+
return /*#__PURE__*/_jsx(Block, {
|
|
53
|
+
className: styles.mathEditor,
|
|
54
|
+
ref: divRef,
|
|
55
|
+
shadow: true,
|
|
56
|
+
variant: 'outlined',
|
|
57
|
+
children: children
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
MathEditorContainer.displayName = 'MathEditorContainer';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { type TextAreaRef } from 'antd/es/input/TextArea';
|
|
3
|
+
import { MathBlockNode, MathInlineNode } from '../../node';
|
|
4
|
+
export interface MathEditorContentProps {
|
|
5
|
+
/** 焦点引用 */
|
|
6
|
+
focusRef?: (ref: TextAreaRef | null) => void;
|
|
7
|
+
/** 数学节点 */
|
|
8
|
+
mathNode: MathInlineNode | MathBlockNode | null;
|
|
9
|
+
/** 左箭头回调 */
|
|
10
|
+
onArrowLeft: () => void;
|
|
11
|
+
/** 右箭头回调 */
|
|
12
|
+
onArrowRight: () => void;
|
|
13
|
+
/** 取消回调 */
|
|
14
|
+
onCancel: () => void;
|
|
15
|
+
/** 删除节点回调 */
|
|
16
|
+
onDelete: () => void;
|
|
17
|
+
/** 提交回调 */
|
|
18
|
+
onSubmit: () => void;
|
|
19
|
+
/** 值变化回调 */
|
|
20
|
+
onValueChange: (value: string) => void;
|
|
21
|
+
/** 是否从前一个位置进入 */
|
|
22
|
+
prev: boolean;
|
|
23
|
+
/** 当前输入值 */
|
|
24
|
+
value: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const MathEditorContent: import("react").NamedExoticComponent<MathEditorContentProps>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
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."); }
|
|
3
|
+
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); }
|
|
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
|
+
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
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { Button, Hotkey, Text, TextArea } from '@lobehub/ui';
|
|
8
|
+
import { renderToString } from 'katex';
|
|
9
|
+
import { isModifierMatch } from 'lexical';
|
|
10
|
+
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
11
|
+
import { Flexbox } from 'react-layout-kit';
|
|
12
|
+
import { CONTROL_OR_META } from "../../../../common/sys";
|
|
13
|
+
import { useTranslation } from "../../../../editor-kernel/react/useTranslation";
|
|
14
|
+
import { useStyles } from "../style";
|
|
15
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
+
export var MathEditorContent = /*#__PURE__*/memo(function (_ref) {
|
|
19
|
+
var focusRef = _ref.focusRef,
|
|
20
|
+
mathNode = _ref.mathNode,
|
|
21
|
+
onArrowLeft = _ref.onArrowLeft,
|
|
22
|
+
onArrowRight = _ref.onArrowRight,
|
|
23
|
+
onCancel = _ref.onCancel,
|
|
24
|
+
onDelete = _ref.onDelete,
|
|
25
|
+
onSubmit = _ref.onSubmit,
|
|
26
|
+
onValueChange = _ref.onValueChange,
|
|
27
|
+
prev = _ref.prev,
|
|
28
|
+
value = _ref.value;
|
|
29
|
+
var t = useTranslation();
|
|
30
|
+
var textareaRef = useRef(null);
|
|
31
|
+
var _useState = useState(''),
|
|
32
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
33
|
+
latexError = _useState2[0],
|
|
34
|
+
setLatexError = _useState2[1];
|
|
35
|
+
var _useStyles = useStyles(),
|
|
36
|
+
styles = _useStyles.styles;
|
|
37
|
+
|
|
38
|
+
// 将 ref 暴露给父组件
|
|
39
|
+
useEffect(function () {
|
|
40
|
+
focusRef === null || focusRef === void 0 || focusRef(textareaRef.current);
|
|
41
|
+
}, [focusRef]);
|
|
42
|
+
|
|
43
|
+
// 聚焦和光标位置处理
|
|
44
|
+
useEffect(function () {
|
|
45
|
+
if (textareaRef.current) {
|
|
46
|
+
textareaRef.current.focus();
|
|
47
|
+
if (prev) {
|
|
48
|
+
var _textareaRef$current$;
|
|
49
|
+
(_textareaRef$current$ = textareaRef.current.resizableTextArea) === null || _textareaRef$current$ === void 0 || (_textareaRef$current$ = _textareaRef$current$.textArea) === null || _textareaRef$current$ === void 0 || _textareaRef$current$.setSelectionRange(0, 0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, [prev]);
|
|
53
|
+
|
|
54
|
+
// 实时验证 LaTeX 语法
|
|
55
|
+
useEffect(function () {
|
|
56
|
+
if (!mathNode) return;
|
|
57
|
+
var isEmpty = !value.trim();
|
|
58
|
+
if (isEmpty) {
|
|
59
|
+
setLatexError('');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 使用防抖来避免过于频繁的验证
|
|
64
|
+
var timeoutId = setTimeout(function () {
|
|
65
|
+
try {
|
|
66
|
+
renderToString(value, {
|
|
67
|
+
displayMode: true,
|
|
68
|
+
throwOnError: true
|
|
69
|
+
});
|
|
70
|
+
// 验证成功:清除错误
|
|
71
|
+
setLatexError('');
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// 验证失败:设置错误信息
|
|
74
|
+
var errorMessage = error instanceof Error ? error.message : 'LaTeX Parse Error';
|
|
75
|
+
setLatexError(errorMessage);
|
|
76
|
+
}
|
|
77
|
+
}, 50);
|
|
78
|
+
return function () {
|
|
79
|
+
return clearTimeout(timeoutId);
|
|
80
|
+
};
|
|
81
|
+
}, [value, mathNode]);
|
|
82
|
+
var handleKeyDown = useCallback(function (e) {
|
|
83
|
+
if (!mathNode) return;
|
|
84
|
+
if (isModifierMatch(e, CONTROL_OR_META) && e.key === 'Enter') {
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
onSubmit();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (e.key === 'Escape') {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
onCancel();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 当内容为空且按退格键时,删除数学节点
|
|
96
|
+
if (e.key === 'Backspace' && !value.trim()) {
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
onDelete();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (e.key === 'ArrowLeft' && e.currentTarget.selectionStart === 0) {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
onArrowLeft();
|
|
104
|
+
}
|
|
105
|
+
if (e.key === 'ArrowRight' && e.currentTarget.selectionStart === e.currentTarget.value.length) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
onArrowRight();
|
|
108
|
+
}
|
|
109
|
+
}, [mathNode, onSubmit, onCancel, onDelete, onArrowLeft, onArrowRight, value]);
|
|
110
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
111
|
+
children: [/*#__PURE__*/_jsx(TextArea, {
|
|
112
|
+
autoFocus: true,
|
|
113
|
+
autoSize: {
|
|
114
|
+
maxRows: 6,
|
|
115
|
+
minRows: 1
|
|
116
|
+
},
|
|
117
|
+
onChange: function onChange(e) {
|
|
118
|
+
onValueChange(e.target.value);
|
|
119
|
+
},
|
|
120
|
+
onKeyDown: handleKeyDown,
|
|
121
|
+
placeholder: "".concat(t('math.placeholder'), "..."),
|
|
122
|
+
ref: textareaRef,
|
|
123
|
+
resize: false,
|
|
124
|
+
style: {
|
|
125
|
+
marginBlock: 4
|
|
126
|
+
},
|
|
127
|
+
value: value,
|
|
128
|
+
variant: 'borderless'
|
|
129
|
+
}), latexError && /*#__PURE__*/_jsx(Flexbox, {
|
|
130
|
+
className: styles.mathEditorFooter,
|
|
131
|
+
horizontal: true,
|
|
132
|
+
paddingBlock: 4,
|
|
133
|
+
paddingInline: 12,
|
|
134
|
+
width: '100%',
|
|
135
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
136
|
+
fontSize: 13,
|
|
137
|
+
type: 'danger',
|
|
138
|
+
children: latexError
|
|
139
|
+
})
|
|
140
|
+
}), /*#__PURE__*/_jsx(Flexbox, {
|
|
141
|
+
className: styles.mathEditorFooter,
|
|
142
|
+
horizontal: true,
|
|
143
|
+
justify: 'flex-end',
|
|
144
|
+
padding: 4,
|
|
145
|
+
width: '100%',
|
|
146
|
+
children: /*#__PURE__*/_jsxs(Button, {
|
|
147
|
+
onClick: function onClick(e) {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
onSubmit();
|
|
150
|
+
},
|
|
151
|
+
size: 'small',
|
|
152
|
+
type: 'text',
|
|
153
|
+
variant: 'filled',
|
|
154
|
+
children: [t('confirm'), /*#__PURE__*/_jsx(Hotkey, {
|
|
155
|
+
compact: true,
|
|
156
|
+
keys: "mod+enter",
|
|
157
|
+
variant: 'borderless'
|
|
158
|
+
})]
|
|
159
|
+
})
|
|
160
|
+
})]
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
MathEditorContent.displayName = 'MathEditorContent';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { LexicalEditor } from 'lexical';
|
|
3
|
+
import { MathBlockNode, MathInlineNode } from '../../node';
|
|
4
|
+
export interface MathInlineProps {
|
|
5
|
+
className?: string;
|
|
6
|
+
editor: LexicalEditor;
|
|
7
|
+
node: MathInlineNode | MathBlockNode;
|
|
8
|
+
}
|
|
9
|
+
declare const MathInline: import("react").NamedExoticComponent<MathInlineProps>;
|
|
10
|
+
export default MathInline;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
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."); }
|
|
3
|
+
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); }
|
|
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
|
+
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
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { addClassNamesToElement, removeClassNamesFromElement } from '@lexical/utils';
|
|
8
|
+
import Katex from 'katex';
|
|
9
|
+
import { $getSelection, $isNodeSelection, CLICK_COMMAND, COMMAND_PRIORITY_NORMAL } from 'lexical';
|
|
10
|
+
import { memo, useEffect, useRef, useState } from 'react';
|
|
11
|
+
import { useLexicalEditor } from "../../../../editor-kernel/react";
|
|
12
|
+
import { useLexicalNodeSelection } from "../../../../editor-kernel/react/useLexicalNodeSelection";
|
|
13
|
+
import { $isMathNode, MathBlockNode } from "../../node";
|
|
14
|
+
import Placeholder from "./Placeholder";
|
|
15
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
|
+
var MathInline = /*#__PURE__*/memo(function (_ref) {
|
|
17
|
+
var editor = _ref.editor,
|
|
18
|
+
node = _ref.node,
|
|
19
|
+
className = _ref.className;
|
|
20
|
+
var ref = useRef(null);
|
|
21
|
+
var _useLexicalNodeSelect = useLexicalNodeSelection(node.getKey()),
|
|
22
|
+
_useLexicalNodeSelect2 = _slicedToArray(_useLexicalNodeSelect, 2),
|
|
23
|
+
isSelected = _useLexicalNodeSelect2[0],
|
|
24
|
+
setSelected = _useLexicalNodeSelect2[1];
|
|
25
|
+
var _useState = useState(false),
|
|
26
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
27
|
+
isEditing = _useState2[0],
|
|
28
|
+
setIsEditing = _useState2[1];
|
|
29
|
+
useEffect(function () {
|
|
30
|
+
if (ref.current && node.code) {
|
|
31
|
+
Katex.render(node.code, ref.current, {
|
|
32
|
+
throwOnError: false
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}, [node.code]);
|
|
36
|
+
useEffect(function () {
|
|
37
|
+
var parent = editor.getElementByKey(node.getKey());
|
|
38
|
+
if (parent) {
|
|
39
|
+
// 防抖处理,避免过于频繁的 DOM 操作
|
|
40
|
+
var timeoutId = setTimeout(function () {
|
|
41
|
+
if (isEditing) {
|
|
42
|
+
addClassNamesToElement(parent, 'editing');
|
|
43
|
+
removeClassNamesFromElement(parent, 'selected');
|
|
44
|
+
} else if (isSelected) {
|
|
45
|
+
addClassNamesToElement(parent, 'selected');
|
|
46
|
+
removeClassNamesFromElement(parent, 'editing');
|
|
47
|
+
} else {
|
|
48
|
+
removeClassNamesFromElement(parent, 'selected');
|
|
49
|
+
removeClassNamesFromElement(parent, 'editing');
|
|
50
|
+
}
|
|
51
|
+
}, 10);
|
|
52
|
+
return function () {
|
|
53
|
+
return clearTimeout(timeoutId);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}, [isSelected, isEditing, editor, node]);
|
|
57
|
+
useLexicalEditor(function (editor) {
|
|
58
|
+
return editor.registerCommand(CLICK_COMMAND, function (payload) {
|
|
59
|
+
console.info(payload, payload.target, ref.current);
|
|
60
|
+
if (payload.target && payload.target instanceof Node) {
|
|
61
|
+
var _ref$current;
|
|
62
|
+
// 获取节点对应的 DOM 元素
|
|
63
|
+
var nodeElement = editor.getElementByKey(node.getKey());
|
|
64
|
+
|
|
65
|
+
// 对于 block 模式,检查是否点击在整个节点容器内
|
|
66
|
+
// 对于 inline 模式,仍然检查是否点击在渲染内容内
|
|
67
|
+
var isClickInNode = node instanceof MathBlockNode ? nodeElement === null || nodeElement === void 0 ? void 0 : nodeElement.contains(payload.target) : (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.contains(payload.target);
|
|
68
|
+
if (isClickInNode) {
|
|
69
|
+
setSelected(true);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
74
|
+
}, [node]);
|
|
75
|
+
|
|
76
|
+
// 监听编辑器状态变化来检测编辑状态
|
|
77
|
+
useLexicalEditor(function (editor) {
|
|
78
|
+
return editor.registerUpdateListener(function () {
|
|
79
|
+
editor.read(function () {
|
|
80
|
+
var selection = $getSelection();
|
|
81
|
+
if (!$isNodeSelection(selection)) {
|
|
82
|
+
setIsEditing(false);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
var selectedNode = selection.getNodes()[0];
|
|
86
|
+
if (!$isMathNode(selectedNode)) {
|
|
87
|
+
setIsEditing(false);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// 检查是否选中的是当前节点,且有数学编辑器显示
|
|
91
|
+
if (selectedNode.getKey() === node.getKey()) {
|
|
92
|
+
setIsEditing(true);
|
|
93
|
+
} else {
|
|
94
|
+
setIsEditing(false);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}, [node]);
|
|
99
|
+
return /*#__PURE__*/_jsx("span", {
|
|
100
|
+
className: className,
|
|
101
|
+
ref: ref,
|
|
102
|
+
children: node.code ? node.code : /*#__PURE__*/_jsx(Placeholder, {})
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
export default MathInline;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Text } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { useTranslation } from "../../../../editor-kernel/react/useTranslation";
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
var Placeholder = /*#__PURE__*/memo(function () {
|
|
6
|
+
var t = useTranslation();
|
|
7
|
+
return /*#__PURE__*/_jsx(Text, {
|
|
8
|
+
as: 'span',
|
|
9
|
+
className: 'katex',
|
|
10
|
+
fontSize: '1em',
|
|
11
|
+
style: {
|
|
12
|
+
fontStyle: 'italic',
|
|
13
|
+
paddingInline: '0.2em'
|
|
14
|
+
},
|
|
15
|
+
type: 'secondary',
|
|
16
|
+
children: t('math.placeholder')
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
export default Placeholder;
|