@haklex/rich-plugin-block-handle 0.0.101 → 0.0.103
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/dist/BlockHandlePlugin.d.ts.map +1 -1
- package/dist/blockSelectionUtils.d.ts +4 -0
- package/dist/blockSelectionUtils.d.ts.map +1 -0
- package/dist/index.mjs +685 -86
- package/dist/rich-plugin-block-handle.css +1 -1
- package/dist/styles.css.d.ts +2 -0
- package/dist/styles.css.d.ts.map +1 -1
- package/dist/useBlockSelection.d.ts +8 -0
- package/dist/useBlockSelection.d.ts.map +1 -0
- package/package.json +5 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BlockHandlePlugin.d.ts","sourceRoot":"","sources":["../src/BlockHandlePlugin.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BlockHandlePlugin.d.ts","sourceRoot":"","sources":["../src/BlockHandlePlugin.tsx"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAgD,YAAY,EAAE,MAAM,OAAO,CAAC;AA+xBxF,wBAAgB,iBAAiB,IAAI,YAAY,CAGhD"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { LexicalEditor, LexicalNode } from 'lexical';
|
|
2
|
+
export declare function buildBlockClipboardData(editor: LexicalEditor, nodes: readonly LexicalNode[]): Record<string, string>;
|
|
3
|
+
export declare function removeTopLevelNodesAndRestoreSelection(nodes: readonly LexicalNode[]): void;
|
|
4
|
+
//# sourceMappingURL=blockSelectionUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockSelectionUtils.d.ts","sourceRoot":"","sources":["../src/blockSelectionUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,SAAS,CAAC;AA+BjB,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2CxB;AAED,wBAAgB,sCAAsC,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAkC1F"}
|
package/dist/index.mjs
CHANGED
|
@@ -7,17 +7,489 @@ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext
|
|
|
7
7
|
import { INSERT_HORIZONTAL_RULE_COMMAND } from "@lexical/react/LexicalHorizontalRuleNode";
|
|
8
8
|
import { $createQuoteNode, $createHeadingNode } from "@lexical/rich-text";
|
|
9
9
|
import { $setBlocksType } from "@lexical/selection";
|
|
10
|
-
import { $
|
|
10
|
+
import { $getRoot, $createParagraphNode, createEditor, $parseSerializedNode, $isElementNode, $createNodeSelection, $setSelection, $getSelection, $isNodeSelection, KEY_ARROW_DOWN_COMMAND, COMMAND_PRIORITY_CRITICAL, KEY_ARROW_UP_COMMAND, SELECT_ALL_COMMAND, $isRangeSelection, COMMAND_PRIORITY_HIGH, KEY_ESCAPE_COMMAND, COPY_COMMAND, CUT_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, REMOVE_TEXT_COMMAND, DELETE_CHARACTER_COMMAND, $getNearestNodeFromDOMNode, $getNodeByKey, DRAGOVER_COMMAND, DROP_COMMAND, DRAGSTART_COMMAND } from "lexical";
|
|
11
11
|
import { Plus, GripVertical, Type, Heading1, Heading2, Heading3, List, ListOrdered, ListChecks, TextQuote, Minus, Code2, Copy, ArrowUp, ArrowDown, Trash2 } from "lucide-react";
|
|
12
|
-
import {
|
|
12
|
+
import { useRef, useCallback, useEffect, useState } from "react";
|
|
13
13
|
import { createPortal } from "react-dom";
|
|
14
|
+
import { $generateHtmlFromNodes } from "@lexical/html";
|
|
14
15
|
var handleContainer = "iihqkc0";
|
|
15
16
|
var handleContainerVisible = "iihqkc1";
|
|
16
17
|
var handleBtn = "iihqkc2";
|
|
17
18
|
var draggingBlock = "iihqkc3";
|
|
18
19
|
var dragPreview = "iihqkc4";
|
|
19
20
|
var dropIndicator = "iihqkc5";
|
|
20
|
-
var
|
|
21
|
+
var blockSelected = "iihqkc6";
|
|
22
|
+
var dragCountBadge = "iihqkc7";
|
|
23
|
+
var menuItemDestructive = "iihqkc8";
|
|
24
|
+
function serializeNodeWithChildren(node) {
|
|
25
|
+
const serialized = node.exportJSON();
|
|
26
|
+
if ($isElementNode(node)) {
|
|
27
|
+
serialized.children = node.getChildren().map(serializeNodeWithChildren);
|
|
28
|
+
}
|
|
29
|
+
return serialized;
|
|
30
|
+
}
|
|
31
|
+
function selectTopLevelNode(node, placement) {
|
|
32
|
+
if ($isElementNode(node)) {
|
|
33
|
+
if (placement === "end") {
|
|
34
|
+
node.selectEnd();
|
|
35
|
+
} else {
|
|
36
|
+
node.selectStart();
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const selection = $createNodeSelection();
|
|
41
|
+
selection.add(node.getKey());
|
|
42
|
+
$setSelection(selection);
|
|
43
|
+
}
|
|
44
|
+
function buildBlockClipboardData(editor, nodes) {
|
|
45
|
+
const lexicalEditor = editor;
|
|
46
|
+
const namespace = lexicalEditor._config.namespace;
|
|
47
|
+
const serializedNodes = nodes.map(serializeNodeWithChildren);
|
|
48
|
+
let html = "";
|
|
49
|
+
try {
|
|
50
|
+
const registeredNodeKlasses = [
|
|
51
|
+
...new Set([...lexicalEditor._nodes.values()].map((entry) => entry.klass))
|
|
52
|
+
];
|
|
53
|
+
const tempEditor = createEditor({
|
|
54
|
+
namespace: `${namespace}-block-selection-html`,
|
|
55
|
+
nodes: registeredNodeKlasses,
|
|
56
|
+
onError: (error) => {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
tempEditor.update(
|
|
61
|
+
() => {
|
|
62
|
+
const root = $getRoot();
|
|
63
|
+
for (const serializedNode of serializedNodes) {
|
|
64
|
+
root.append($parseSerializedNode(serializedNode));
|
|
65
|
+
}
|
|
66
|
+
html = $generateHtmlFromNodes(tempEditor, null);
|
|
67
|
+
},
|
|
68
|
+
{ discrete: true }
|
|
69
|
+
);
|
|
70
|
+
} catch {
|
|
71
|
+
html = "";
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
"application/x-lexical-editor": JSON.stringify({
|
|
75
|
+
namespace,
|
|
76
|
+
nodes: serializedNodes
|
|
77
|
+
}),
|
|
78
|
+
"text/html": html,
|
|
79
|
+
"text/plain": nodes.map((node) => node.getTextContent()).join("\n\n")
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function removeTopLevelNodesAndRestoreSelection(nodes) {
|
|
83
|
+
if (nodes.length === 0) return;
|
|
84
|
+
const firstNode = nodes[0];
|
|
85
|
+
const lastNode = nodes.at(-1);
|
|
86
|
+
const previousSibling = firstNode.getPreviousSibling();
|
|
87
|
+
const nextSibling = lastNode.getNextSibling();
|
|
88
|
+
for (const node of nodes) {
|
|
89
|
+
node.remove();
|
|
90
|
+
}
|
|
91
|
+
const root = $getRoot();
|
|
92
|
+
if (root.getChildrenSize() === 0) {
|
|
93
|
+
const paragraph = $createParagraphNode();
|
|
94
|
+
root.append(paragraph);
|
|
95
|
+
paragraph.selectStart();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (nextSibling) {
|
|
99
|
+
selectTopLevelNode(nextSibling, "start");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (previousSibling) {
|
|
103
|
+
selectTopLevelNode(previousSibling, "end");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const fallbackNode = root.getFirstChild();
|
|
107
|
+
if (fallbackNode) {
|
|
108
|
+
selectTopLevelNode(fallbackNode, "start");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function $getTopLevelKeys() {
|
|
112
|
+
return $getRoot().getChildren().map((c) => c.getKey());
|
|
113
|
+
}
|
|
114
|
+
function $selectBlockRange(anchorKey, focusKey) {
|
|
115
|
+
const children = $getRoot().getChildren();
|
|
116
|
+
const anchorIdx = children.findIndex((c) => c.getKey() === anchorKey);
|
|
117
|
+
const focusIdx = children.findIndex((c) => c.getKey() === focusKey);
|
|
118
|
+
if (anchorIdx === -1 || focusIdx === -1) return;
|
|
119
|
+
const start = Math.min(anchorIdx, focusIdx);
|
|
120
|
+
const end = Math.max(anchorIdx, focusIdx);
|
|
121
|
+
const sel = $createNodeSelection();
|
|
122
|
+
for (let i = start; i <= end; i++) {
|
|
123
|
+
sel.add(children[i].getKey());
|
|
124
|
+
}
|
|
125
|
+
$setSelection(sel);
|
|
126
|
+
}
|
|
127
|
+
function useBlockSelection(editor) {
|
|
128
|
+
const anchorKeyRef = useRef(null);
|
|
129
|
+
const focusKeyRef = useRef(null);
|
|
130
|
+
const blockSelectionKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
131
|
+
const clearBlockSelectionState = useCallback(() => {
|
|
132
|
+
blockSelectionKeysRef.current = /* @__PURE__ */ new Set();
|
|
133
|
+
anchorKeyRef.current = null;
|
|
134
|
+
focusKeyRef.current = null;
|
|
135
|
+
}, []);
|
|
136
|
+
const getTopLevelNodesByKeys = useCallback((keys) => {
|
|
137
|
+
if (keys.size === 0) return [];
|
|
138
|
+
return $getRoot().getChildren().filter((node) => keys.has(node.getKey()));
|
|
139
|
+
}, []);
|
|
140
|
+
const getOwnedSelectionNodes = useCallback(() => {
|
|
141
|
+
const ownedKeys = blockSelectionKeysRef.current;
|
|
142
|
+
if (ownedKeys.size === 0) return [];
|
|
143
|
+
const selection = $getSelection();
|
|
144
|
+
if (!$isNodeSelection(selection)) return [];
|
|
145
|
+
const selectionKeys = new Set(selection.getNodes().map((node) => node.getKey()));
|
|
146
|
+
const isOwnedSelection = selectionKeys.size === ownedKeys.size && [...selectionKeys].every((key) => ownedKeys.has(key));
|
|
147
|
+
if (!isOwnedSelection) return [];
|
|
148
|
+
return getTopLevelNodesByKeys(ownedKeys);
|
|
149
|
+
}, [getTopLevelNodesByKeys]);
|
|
150
|
+
const deleteBlocksByKeys = useCallback(
|
|
151
|
+
(keys) => {
|
|
152
|
+
editor.update(
|
|
153
|
+
() => {
|
|
154
|
+
const nodes = getTopLevelNodesByKeys(new Set(keys));
|
|
155
|
+
if (nodes.length === 0) return;
|
|
156
|
+
removeTopLevelNodesAndRestoreSelection(nodes);
|
|
157
|
+
clearBlockSelectionState();
|
|
158
|
+
},
|
|
159
|
+
{ discrete: true }
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
[clearBlockSelectionState, editor, getTopLevelNodesByKeys]
|
|
163
|
+
);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
let prevKeys = /* @__PURE__ */ new Set();
|
|
166
|
+
const unregister = editor.registerUpdateListener(({ editorState }) => {
|
|
167
|
+
const rootEl = editor.getRootElement();
|
|
168
|
+
if (!rootEl) return;
|
|
169
|
+
const nextKeys = /* @__PURE__ */ new Set();
|
|
170
|
+
let isNodeSel = false;
|
|
171
|
+
let topLevelKeys = [];
|
|
172
|
+
editorState.read(() => {
|
|
173
|
+
const sel = $getSelection();
|
|
174
|
+
if ($isNodeSelection(sel)) {
|
|
175
|
+
isNodeSel = true;
|
|
176
|
+
for (const node of sel.getNodes()) {
|
|
177
|
+
nextKeys.add(node.getKey());
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
topLevelKeys = $getTopLevelKeys();
|
|
181
|
+
});
|
|
182
|
+
if (blockSelectionKeysRef.current.size > 0) {
|
|
183
|
+
if (!isNodeSel) {
|
|
184
|
+
clearBlockSelectionState();
|
|
185
|
+
} else {
|
|
186
|
+
const owned = blockSelectionKeysRef.current;
|
|
187
|
+
const stillOwned = nextKeys.size === owned.size && [...nextKeys].every((k) => owned.has(k));
|
|
188
|
+
if (!stillOwned) {
|
|
189
|
+
const topLevelSet = new Set(topLevelKeys);
|
|
190
|
+
const restoredTopLevel = [...nextKeys].filter((k) => topLevelSet.has(k));
|
|
191
|
+
if (restoredTopLevel.length > 1) {
|
|
192
|
+
const indices = restoredTopLevel.map((k) => topLevelKeys.indexOf(k)).sort((a, b) => a - b);
|
|
193
|
+
anchorKeyRef.current = topLevelKeys[indices[0]];
|
|
194
|
+
focusKeyRef.current = topLevelKeys[indices.at(-1)];
|
|
195
|
+
blockSelectionKeysRef.current = new Set(restoredTopLevel);
|
|
196
|
+
} else {
|
|
197
|
+
clearBlockSelectionState();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const highlightKeys = blockSelectionKeysRef.current.size > 0 ? nextKeys : /* @__PURE__ */ new Set();
|
|
203
|
+
for (const key of prevKeys) {
|
|
204
|
+
if (!highlightKeys.has(key)) {
|
|
205
|
+
editor.getElementByKey(key)?.classList.remove(blockSelected);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
for (const key of highlightKeys) {
|
|
209
|
+
if (!prevKeys.has(key)) {
|
|
210
|
+
editor.getElementByKey(key)?.classList.add(blockSelected);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
prevKeys = highlightKeys;
|
|
214
|
+
});
|
|
215
|
+
return () => {
|
|
216
|
+
unregister();
|
|
217
|
+
for (const key of prevKeys) {
|
|
218
|
+
editor.getElementByKey(key)?.classList.remove(blockSelected);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}, [clearBlockSelectionState, editor]);
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
const rootEl = editor.getRootElement();
|
|
224
|
+
if (!rootEl) return;
|
|
225
|
+
const onFocusIn = (e) => {
|
|
226
|
+
if (blockSelectionKeysRef.current.size === 0) return;
|
|
227
|
+
const target = e.target;
|
|
228
|
+
if (!target) return;
|
|
229
|
+
const nestedEditable = target.closest('[contenteditable="true"]');
|
|
230
|
+
if (nestedEditable && nestedEditable !== rootEl) {
|
|
231
|
+
clearBlockSelectionState();
|
|
232
|
+
editor.update(() => {
|
|
233
|
+
const sel = $getSelection();
|
|
234
|
+
if ($isNodeSelection(sel)) {
|
|
235
|
+
$setSelection(null);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
rootEl.addEventListener("focusin", onFocusIn);
|
|
241
|
+
return () => rootEl.removeEventListener("focusin", onFocusIn);
|
|
242
|
+
}, [clearBlockSelectionState, editor]);
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
const unregShiftDown = editor.registerCommand(
|
|
245
|
+
KEY_ARROW_DOWN_COMMAND,
|
|
246
|
+
(event) => {
|
|
247
|
+
if (!event?.shiftKey) return false;
|
|
248
|
+
if (blockSelectionKeysRef.current.size === 0 || !focusKeyRef.current) return false;
|
|
249
|
+
const sel = $getSelection();
|
|
250
|
+
if (!$isNodeSelection(sel)) return false;
|
|
251
|
+
const children = $getRoot().getChildren();
|
|
252
|
+
const focusIdx = children.findIndex((c) => c.getKey() === focusKeyRef.current);
|
|
253
|
+
if (focusIdx === -1 || focusIdx >= children.length - 1) return false;
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
focusKeyRef.current = children[focusIdx + 1].getKey();
|
|
256
|
+
$selectBlockRange(anchorKeyRef.current, focusKeyRef.current);
|
|
257
|
+
const start = Math.min(
|
|
258
|
+
children.findIndex((c) => c.getKey() === anchorKeyRef.current),
|
|
259
|
+
focusIdx + 1
|
|
260
|
+
);
|
|
261
|
+
const end = Math.max(
|
|
262
|
+
children.findIndex((c) => c.getKey() === anchorKeyRef.current),
|
|
263
|
+
focusIdx + 1
|
|
264
|
+
);
|
|
265
|
+
blockSelectionKeysRef.current = new Set(
|
|
266
|
+
children.slice(start, end + 1).map((c) => c.getKey())
|
|
267
|
+
);
|
|
268
|
+
return true;
|
|
269
|
+
},
|
|
270
|
+
COMMAND_PRIORITY_CRITICAL
|
|
271
|
+
);
|
|
272
|
+
const unregShiftUp = editor.registerCommand(
|
|
273
|
+
KEY_ARROW_UP_COMMAND,
|
|
274
|
+
(event) => {
|
|
275
|
+
if (!event?.shiftKey) return false;
|
|
276
|
+
if (blockSelectionKeysRef.current.size === 0 || !focusKeyRef.current) return false;
|
|
277
|
+
const sel = $getSelection();
|
|
278
|
+
if (!$isNodeSelection(sel)) return false;
|
|
279
|
+
const children = $getRoot().getChildren();
|
|
280
|
+
const focusIdx = children.findIndex((c) => c.getKey() === focusKeyRef.current);
|
|
281
|
+
if (focusIdx === -1 || focusIdx <= 0) return false;
|
|
282
|
+
event.preventDefault();
|
|
283
|
+
focusKeyRef.current = children[focusIdx - 1].getKey();
|
|
284
|
+
$selectBlockRange(anchorKeyRef.current, focusKeyRef.current);
|
|
285
|
+
const anchorIdx = children.findIndex((c) => c.getKey() === anchorKeyRef.current);
|
|
286
|
+
const start = Math.min(anchorIdx, focusIdx - 1);
|
|
287
|
+
const end = Math.max(anchorIdx, focusIdx - 1);
|
|
288
|
+
blockSelectionKeysRef.current = new Set(
|
|
289
|
+
children.slice(start, end + 1).map((c) => c.getKey())
|
|
290
|
+
);
|
|
291
|
+
return true;
|
|
292
|
+
},
|
|
293
|
+
COMMAND_PRIORITY_CRITICAL
|
|
294
|
+
);
|
|
295
|
+
const unregSelectAll = editor.registerCommand(
|
|
296
|
+
SELECT_ALL_COMMAND,
|
|
297
|
+
() => {
|
|
298
|
+
if (blockSelectionKeysRef.current.size > 0) {
|
|
299
|
+
const children = $getRoot().getChildren();
|
|
300
|
+
const allKeys = children.map((c) => c.getKey());
|
|
301
|
+
if (blockSelectionKeysRef.current.size >= allKeys.length) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
anchorKeyRef.current = allKeys[0];
|
|
305
|
+
focusKeyRef.current = allKeys.at(-1);
|
|
306
|
+
blockSelectionKeysRef.current = new Set(allKeys);
|
|
307
|
+
const nodeSel = $createNodeSelection();
|
|
308
|
+
for (const key of allKeys) nodeSel.add(key);
|
|
309
|
+
$setSelection(nodeSel);
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
const sel = $getSelection();
|
|
313
|
+
let topLevelKey = null;
|
|
314
|
+
if ($isRangeSelection(sel)) {
|
|
315
|
+
let node = sel.anchor.getNode();
|
|
316
|
+
while (node.getParent() && node.getParent() !== $getRoot()) {
|
|
317
|
+
node = node.getParent();
|
|
318
|
+
}
|
|
319
|
+
if (node.getParent() === $getRoot()) {
|
|
320
|
+
topLevelKey = node.getKey();
|
|
321
|
+
}
|
|
322
|
+
} else if ($isNodeSelection(sel)) {
|
|
323
|
+
const nodes = sel.getNodes();
|
|
324
|
+
if (nodes.length > 0) {
|
|
325
|
+
let node = nodes[0];
|
|
326
|
+
while (node.getParent() && node.getParent() !== $getRoot()) {
|
|
327
|
+
node = node.getParent();
|
|
328
|
+
}
|
|
329
|
+
if (node.getParent() === $getRoot()) {
|
|
330
|
+
topLevelKey = node.getKey();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (topLevelKey) {
|
|
335
|
+
anchorKeyRef.current = topLevelKey;
|
|
336
|
+
focusKeyRef.current = topLevelKey;
|
|
337
|
+
blockSelectionKeysRef.current = /* @__PURE__ */ new Set([topLevelKey]);
|
|
338
|
+
const nodeSel = $createNodeSelection();
|
|
339
|
+
nodeSel.add(topLevelKey);
|
|
340
|
+
$setSelection(nodeSel);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
return false;
|
|
344
|
+
},
|
|
345
|
+
COMMAND_PRIORITY_HIGH
|
|
346
|
+
);
|
|
347
|
+
const unregEscape = editor.registerCommand(
|
|
348
|
+
KEY_ESCAPE_COMMAND,
|
|
349
|
+
() => {
|
|
350
|
+
if (blockSelectionKeysRef.current.size === 0) return false;
|
|
351
|
+
clearBlockSelectionState();
|
|
352
|
+
$setSelection(null);
|
|
353
|
+
return true;
|
|
354
|
+
},
|
|
355
|
+
COMMAND_PRIORITY_HIGH
|
|
356
|
+
);
|
|
357
|
+
const unregCopy = editor.registerCommand(
|
|
358
|
+
COPY_COMMAND,
|
|
359
|
+
(event) => {
|
|
360
|
+
if (!event?.clipboardData) return false;
|
|
361
|
+
let handled = false;
|
|
362
|
+
editor.getEditorState().read(() => {
|
|
363
|
+
const nodes = getOwnedSelectionNodes();
|
|
364
|
+
if (nodes.length === 0) return;
|
|
365
|
+
event.preventDefault();
|
|
366
|
+
const clipboardData = buildBlockClipboardData(editor, nodes);
|
|
367
|
+
for (const [mimeType, value] of Object.entries(clipboardData)) {
|
|
368
|
+
event.clipboardData?.setData(mimeType, value);
|
|
369
|
+
}
|
|
370
|
+
handled = true;
|
|
371
|
+
});
|
|
372
|
+
return handled;
|
|
373
|
+
},
|
|
374
|
+
COMMAND_PRIORITY_CRITICAL
|
|
375
|
+
);
|
|
376
|
+
const unregCut = editor.registerCommand(
|
|
377
|
+
CUT_COMMAND,
|
|
378
|
+
(event) => {
|
|
379
|
+
if (!event?.clipboardData) return false;
|
|
380
|
+
let keysToDelete = [];
|
|
381
|
+
editor.getEditorState().read(() => {
|
|
382
|
+
const nodes = getOwnedSelectionNodes();
|
|
383
|
+
if (nodes.length === 0) return;
|
|
384
|
+
event.preventDefault();
|
|
385
|
+
const clipboardData = buildBlockClipboardData(editor, nodes);
|
|
386
|
+
for (const [mimeType, value] of Object.entries(clipboardData)) {
|
|
387
|
+
event.clipboardData?.setData(mimeType, value);
|
|
388
|
+
}
|
|
389
|
+
keysToDelete = nodes.map((node) => node.getKey());
|
|
390
|
+
});
|
|
391
|
+
if (keysToDelete.length === 0) return false;
|
|
392
|
+
deleteBlocksByKeys(keysToDelete);
|
|
393
|
+
return true;
|
|
394
|
+
},
|
|
395
|
+
COMMAND_PRIORITY_CRITICAL
|
|
396
|
+
);
|
|
397
|
+
const handleDeleteBlocks = (payload) => {
|
|
398
|
+
let keysToDelete = [];
|
|
399
|
+
editor.getEditorState().read(() => {
|
|
400
|
+
keysToDelete = getOwnedSelectionNodes().map((node) => node.getKey());
|
|
401
|
+
});
|
|
402
|
+
if (keysToDelete.length === 0) return false;
|
|
403
|
+
if (payload && typeof payload !== "boolean" && "preventDefault" in payload && typeof payload.preventDefault === "function") {
|
|
404
|
+
payload.preventDefault();
|
|
405
|
+
}
|
|
406
|
+
deleteBlocksByKeys(keysToDelete);
|
|
407
|
+
return true;
|
|
408
|
+
};
|
|
409
|
+
const unregBackspace = editor.registerCommand(
|
|
410
|
+
KEY_BACKSPACE_COMMAND,
|
|
411
|
+
handleDeleteBlocks,
|
|
412
|
+
COMMAND_PRIORITY_CRITICAL
|
|
413
|
+
);
|
|
414
|
+
const unregDelete = editor.registerCommand(
|
|
415
|
+
KEY_DELETE_COMMAND,
|
|
416
|
+
handleDeleteBlocks,
|
|
417
|
+
COMMAND_PRIORITY_CRITICAL
|
|
418
|
+
);
|
|
419
|
+
const unregRemoveText = editor.registerCommand(
|
|
420
|
+
REMOVE_TEXT_COMMAND,
|
|
421
|
+
handleDeleteBlocks,
|
|
422
|
+
COMMAND_PRIORITY_CRITICAL
|
|
423
|
+
);
|
|
424
|
+
const unregDeleteCharacter = editor.registerCommand(
|
|
425
|
+
DELETE_CHARACTER_COMMAND,
|
|
426
|
+
handleDeleteBlocks,
|
|
427
|
+
COMMAND_PRIORITY_CRITICAL
|
|
428
|
+
);
|
|
429
|
+
return () => {
|
|
430
|
+
unregShiftDown();
|
|
431
|
+
unregShiftUp();
|
|
432
|
+
unregSelectAll();
|
|
433
|
+
unregEscape();
|
|
434
|
+
unregCopy();
|
|
435
|
+
unregCut();
|
|
436
|
+
unregBackspace();
|
|
437
|
+
unregDelete();
|
|
438
|
+
unregRemoveText();
|
|
439
|
+
unregDeleteCharacter();
|
|
440
|
+
};
|
|
441
|
+
}, [clearBlockSelectionState, deleteBlocksByKeys, editor, getOwnedSelectionNodes]);
|
|
442
|
+
const selectBlock = useCallback(
|
|
443
|
+
(nodeKey, shiftKey) => {
|
|
444
|
+
editor.update(() => {
|
|
445
|
+
if (shiftKey && anchorKeyRef.current) {
|
|
446
|
+
focusKeyRef.current = nodeKey;
|
|
447
|
+
$selectBlockRange(anchorKeyRef.current, nodeKey);
|
|
448
|
+
const children = $getRoot().getChildren();
|
|
449
|
+
const anchorIdx = children.findIndex((c) => c.getKey() === anchorKeyRef.current);
|
|
450
|
+
const focusIdx = children.findIndex((c) => c.getKey() === nodeKey);
|
|
451
|
+
if (anchorIdx !== -1 && focusIdx !== -1) {
|
|
452
|
+
const start = Math.min(anchorIdx, focusIdx);
|
|
453
|
+
const end = Math.max(anchorIdx, focusIdx);
|
|
454
|
+
blockSelectionKeysRef.current = new Set(
|
|
455
|
+
children.slice(start, end + 1).map((c) => c.getKey())
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
anchorKeyRef.current = nodeKey;
|
|
460
|
+
focusKeyRef.current = nodeKey;
|
|
461
|
+
blockSelectionKeysRef.current = /* @__PURE__ */ new Set([nodeKey]);
|
|
462
|
+
const sel = $createNodeSelection();
|
|
463
|
+
sel.add(nodeKey);
|
|
464
|
+
$setSelection(sel);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
[editor]
|
|
469
|
+
);
|
|
470
|
+
const getSelectedKeys = useCallback(() => {
|
|
471
|
+
if (blockSelectionKeysRef.current.size === 0) return [];
|
|
472
|
+
let keys = [];
|
|
473
|
+
editor.getEditorState().read(() => {
|
|
474
|
+
keys = getTopLevelNodesByKeys(blockSelectionKeysRef.current).map((node) => node.getKey());
|
|
475
|
+
});
|
|
476
|
+
return keys;
|
|
477
|
+
}, [editor, getTopLevelNodesByKeys]);
|
|
478
|
+
const deleteSelectedBlocks = useCallback(
|
|
479
|
+
(fallbackNodeKey) => {
|
|
480
|
+
const selectedKeys = getSelectedKeys();
|
|
481
|
+
const keys = selectedKeys.length > 0 ? selectedKeys : fallbackNodeKey ? [fallbackNodeKey] : [];
|
|
482
|
+
if (keys.length === 0) return;
|
|
483
|
+
deleteBlocksByKeys(keys);
|
|
484
|
+
},
|
|
485
|
+
[deleteBlocksByKeys, getSelectedKeys]
|
|
486
|
+
);
|
|
487
|
+
const isBlockSelectionActive = useCallback(
|
|
488
|
+
() => blockSelectionKeysRef.current.size > 0,
|
|
489
|
+
[]
|
|
490
|
+
);
|
|
491
|
+
return { selectBlock, getSelectedKeys, isBlockSelectionActive, deleteSelectedBlocks };
|
|
492
|
+
}
|
|
21
493
|
const DRAG_DATA_KEY = "application/x-rich-editor-drag";
|
|
22
494
|
const HIDE_DELAY = 300;
|
|
23
495
|
const HANDLE_OFFSET = 52;
|
|
@@ -123,6 +595,8 @@ function BlockHandleInner({ editor }) {
|
|
|
123
595
|
const menuOpenCountRef = useRef(0);
|
|
124
596
|
const dragPreviewRef = useRef(null);
|
|
125
597
|
const draggingBlockRef = useRef(null);
|
|
598
|
+
const draggingBlockKeysRef = useRef(null);
|
|
599
|
+
const { selectBlock, getSelectedKeys, deleteSelectedBlocks } = useBlockSelection(editor);
|
|
126
600
|
const clearHideTimer = useCallback(() => {
|
|
127
601
|
if (hideTimerRef.current) {
|
|
128
602
|
clearTimeout(hideTimerRef.current);
|
|
@@ -134,7 +608,7 @@ function BlockHandleInner({ editor }) {
|
|
|
134
608
|
hideTimerRef.current = setTimeout(() => {
|
|
135
609
|
if (!hoveringHandleRef.current && menuOpenCountRef.current === 0) {
|
|
136
610
|
activeBlockRef.current = null;
|
|
137
|
-
setHandle((
|
|
611
|
+
setHandle((state) => ({ ...state, visible: false, nodeKey: null }));
|
|
138
612
|
}
|
|
139
613
|
}, HIDE_DELAY);
|
|
140
614
|
}, [clearHideTimer]);
|
|
@@ -152,25 +626,25 @@ function BlockHandleInner({ editor }) {
|
|
|
152
626
|
if (!open) scheduleHide();
|
|
153
627
|
else clearHideTimer();
|
|
154
628
|
},
|
|
155
|
-
[
|
|
629
|
+
[clearHideTimer, scheduleHide]
|
|
156
630
|
);
|
|
157
631
|
const updatePositionFromBlock = useCallback(
|
|
158
632
|
(block) => {
|
|
159
633
|
if (block !== void 0) activeBlockRef.current = block;
|
|
160
|
-
const
|
|
161
|
-
if (!
|
|
634
|
+
const element = activeBlockRef.current;
|
|
635
|
+
if (!element || !element.isConnected) {
|
|
162
636
|
activeBlockRef.current = null;
|
|
163
|
-
setHandle((
|
|
637
|
+
setHandle((state) => state.visible ? { ...state, visible: false, nodeKey: null } : state);
|
|
164
638
|
return;
|
|
165
639
|
}
|
|
166
640
|
const rootElement = editor.getRootElement();
|
|
167
641
|
if (!rootElement) return;
|
|
168
|
-
const blockRect =
|
|
642
|
+
const blockRect = element.getBoundingClientRect();
|
|
169
643
|
const rootRect = rootElement.getBoundingClientRect();
|
|
170
644
|
const page = toPagePosition(blockRect);
|
|
171
645
|
let nodeKey = null;
|
|
172
646
|
editor.read(() => {
|
|
173
|
-
const node = $getNearestNodeFromDOMNode(
|
|
647
|
+
const node = $getNearestNodeFromDOMNode(element);
|
|
174
648
|
if (node) nodeKey = node.getKey();
|
|
175
649
|
});
|
|
176
650
|
setHandle({
|
|
@@ -186,11 +660,11 @@ function BlockHandleInner({ editor }) {
|
|
|
186
660
|
const rootElement = editor.getRootElement();
|
|
187
661
|
if (!rootElement) return;
|
|
188
662
|
let rafId = null;
|
|
189
|
-
const onMouseMove = (
|
|
663
|
+
const onMouseMove = (event) => {
|
|
190
664
|
if (rafId !== null) return;
|
|
191
665
|
rafId = requestAnimationFrame(() => {
|
|
192
666
|
rafId = null;
|
|
193
|
-
const target =
|
|
667
|
+
const target = event.target;
|
|
194
668
|
const block = getBlockElement(editor, target);
|
|
195
669
|
if (block) {
|
|
196
670
|
clearHideTimer();
|
|
@@ -211,7 +685,7 @@ function BlockHandleInner({ editor }) {
|
|
|
211
685
|
rootElement.removeEventListener("mouseleave", onMouseLeave);
|
|
212
686
|
clearHideTimer();
|
|
213
687
|
};
|
|
214
|
-
}, [
|
|
688
|
+
}, [clearHideTimer, editor, scheduleHide, updatePositionFromBlock]);
|
|
215
689
|
useEffect(() => {
|
|
216
690
|
const update = () => updatePositionFromBlock();
|
|
217
691
|
window.addEventListener("scroll", update, true);
|
|
@@ -230,9 +704,9 @@ function BlockHandleInner({ editor }) {
|
|
|
230
704
|
editor.update(() => {
|
|
231
705
|
const node = $getNodeByKey(handle.nodeKey);
|
|
232
706
|
if (!node) return;
|
|
233
|
-
const
|
|
234
|
-
node.insertAfter(
|
|
235
|
-
|
|
707
|
+
const paragraph = $createParagraphNode();
|
|
708
|
+
node.insertAfter(paragraph);
|
|
709
|
+
paragraph.selectStart();
|
|
236
710
|
});
|
|
237
711
|
}, [editor, handle.nodeKey]);
|
|
238
712
|
const handleTurnInto = useCallback(
|
|
@@ -258,8 +732,8 @@ function BlockHandleInner({ editor }) {
|
|
|
258
732
|
const node = $getNodeByKey(nodeKey);
|
|
259
733
|
if (!node || !$isElementNode(node)) return;
|
|
260
734
|
node.selectStart();
|
|
261
|
-
const
|
|
262
|
-
if (!$isRangeSelection(
|
|
735
|
+
const selection = $getSelection();
|
|
736
|
+
if (!$isRangeSelection(selection)) return;
|
|
263
737
|
const creators = {
|
|
264
738
|
paragraph: () => $createParagraphNode(),
|
|
265
739
|
h1: () => $createHeadingNode("h1"),
|
|
@@ -269,52 +743,69 @@ function BlockHandleInner({ editor }) {
|
|
|
269
743
|
code: () => $createCodeNode()
|
|
270
744
|
};
|
|
271
745
|
const create = creators[type];
|
|
272
|
-
if (create) $setBlocksType(
|
|
746
|
+
if (create) $setBlocksType(selection, create);
|
|
273
747
|
});
|
|
274
748
|
},
|
|
275
|
-
[editor, handle
|
|
749
|
+
[editor, handle]
|
|
276
750
|
);
|
|
277
751
|
const handleDelete = useCallback(() => {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
node?.remove();
|
|
282
|
-
});
|
|
283
|
-
setHandle((s) => ({ ...s, visible: false, nodeKey: null }));
|
|
284
|
-
}, [editor, handle.nodeKey]);
|
|
752
|
+
deleteSelectedBlocks(handle.nodeKey);
|
|
753
|
+
setHandle((state) => ({ ...state, visible: false, nodeKey: null }));
|
|
754
|
+
}, [deleteSelectedBlocks, handle.nodeKey]);
|
|
285
755
|
const handleDuplicate = useCallback(() => {
|
|
286
|
-
|
|
756
|
+
const selectedKeys = getSelectedKeys();
|
|
757
|
+
const keys = selectedKeys.length > 0 ? selectedKeys : handle.nodeKey ? [handle.nodeKey] : [];
|
|
758
|
+
if (!keys.length) return;
|
|
287
759
|
editor.update(() => {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
760
|
+
const root = $getRoot();
|
|
761
|
+
const children = root.getChildren();
|
|
762
|
+
const keySet = new Set(keys);
|
|
763
|
+
const nodesToDuplicate = children.filter((child) => keySet.has(child.getKey()));
|
|
764
|
+
if (!nodesToDuplicate.length) return;
|
|
765
|
+
let insertAfter = nodesToDuplicate.at(-1);
|
|
766
|
+
for (const node of nodesToDuplicate) {
|
|
767
|
+
const clone = $cloneNode(node);
|
|
768
|
+
insertAfter.insertAfter(clone);
|
|
769
|
+
insertAfter = clone;
|
|
770
|
+
}
|
|
292
771
|
});
|
|
293
|
-
}, [editor, handle.nodeKey]);
|
|
772
|
+
}, [editor, getSelectedKeys, handle.nodeKey]);
|
|
294
773
|
const handleMoveUp = useCallback(() => {
|
|
295
|
-
|
|
774
|
+
const selectedKeys = getSelectedKeys();
|
|
775
|
+
const keys = selectedKeys.length > 0 ? selectedKeys : handle.nodeKey ? [handle.nodeKey] : [];
|
|
776
|
+
if (!keys.length) return;
|
|
296
777
|
editor.update(() => {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
778
|
+
const root = $getRoot();
|
|
779
|
+
const children = root.getChildren();
|
|
780
|
+
const keySet = new Set(keys);
|
|
781
|
+
const selectedNodes = children.filter((child) => keySet.has(child.getKey()));
|
|
782
|
+
if (!selectedNodes.length) return;
|
|
783
|
+
const firstSelected = selectedNodes[0];
|
|
784
|
+
const previousSibling = firstSelected.getPreviousSibling();
|
|
785
|
+
if (!previousSibling || keySet.has(previousSibling.getKey())) return;
|
|
786
|
+
const lastSelected = selectedNodes.at(-1);
|
|
787
|
+
previousSibling.remove();
|
|
788
|
+
lastSelected.insertAfter(previousSibling);
|
|
304
789
|
});
|
|
305
|
-
}, [editor, handle.nodeKey]);
|
|
790
|
+
}, [editor, getSelectedKeys, handle.nodeKey]);
|
|
306
791
|
const handleMoveDown = useCallback(() => {
|
|
307
|
-
|
|
792
|
+
const selectedKeys = getSelectedKeys();
|
|
793
|
+
const keys = selectedKeys.length > 0 ? selectedKeys : handle.nodeKey ? [handle.nodeKey] : [];
|
|
794
|
+
if (!keys.length) return;
|
|
308
795
|
editor.update(() => {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
796
|
+
const root = $getRoot();
|
|
797
|
+
const children = root.getChildren();
|
|
798
|
+
const keySet = new Set(keys);
|
|
799
|
+
const selectedNodes = children.filter((child) => keySet.has(child.getKey()));
|
|
800
|
+
if (!selectedNodes.length) return;
|
|
801
|
+
const lastSelected = selectedNodes.at(-1);
|
|
802
|
+
const nextSibling = lastSelected.getNextSibling();
|
|
803
|
+
if (!nextSibling || keySet.has(nextSibling.getKey())) return;
|
|
804
|
+
const firstSelected = selectedNodes[0];
|
|
805
|
+
nextSibling.remove();
|
|
806
|
+
firstSelected.insertBefore(nextSibling);
|
|
316
807
|
});
|
|
317
|
-
}, [editor, handle.nodeKey]);
|
|
808
|
+
}, [editor, getSelectedKeys, handle.nodeKey]);
|
|
318
809
|
const [gripMenuOpen, setGripMenuOpen] = useState(false);
|
|
319
810
|
const dragStartedRef = useRef(false);
|
|
320
811
|
const clearDragVisualState = useCallback(() => {
|
|
@@ -328,13 +819,22 @@ function BlockHandleInner({ editor }) {
|
|
|
328
819
|
draggingBlock$1.classList.remove(draggingBlock);
|
|
329
820
|
draggingBlockRef.current = null;
|
|
330
821
|
}
|
|
331
|
-
|
|
822
|
+
const draggingKeys = draggingBlockKeysRef.current;
|
|
823
|
+
if (draggingKeys) {
|
|
824
|
+
for (const key of draggingKeys) {
|
|
825
|
+
editor.getElementByKey(key)?.classList.remove(draggingBlock);
|
|
826
|
+
}
|
|
827
|
+
draggingBlockKeysRef.current = null;
|
|
828
|
+
}
|
|
829
|
+
}, [editor]);
|
|
332
830
|
const onGripDragStart = useCallback(
|
|
333
|
-
(
|
|
831
|
+
(event) => {
|
|
334
832
|
dragStartedRef.current = true;
|
|
335
|
-
if (!
|
|
336
|
-
|
|
337
|
-
|
|
833
|
+
if (!event.dataTransfer || !handle.nodeKey) return;
|
|
834
|
+
const selectedKeys = getSelectedKeys();
|
|
835
|
+
const dragKeys = selectedKeys.length > 0 && selectedKeys.includes(handle.nodeKey) ? selectedKeys : [handle.nodeKey];
|
|
836
|
+
event.dataTransfer.setData(DRAG_DATA_KEY, JSON.stringify(dragKeys));
|
|
837
|
+
event.dataTransfer.effectAllowed = "move";
|
|
338
838
|
const block = activeBlockRef.current;
|
|
339
839
|
if (!block) return;
|
|
340
840
|
clearDragVisualState();
|
|
@@ -342,6 +842,13 @@ function BlockHandleInner({ editor }) {
|
|
|
342
842
|
const preview = block.cloneNode(true);
|
|
343
843
|
preview.classList.add(dragPreview);
|
|
344
844
|
preview.style.width = `${rect.width}px`;
|
|
845
|
+
preview.style.position = "relative";
|
|
846
|
+
if (dragKeys.length > 1) {
|
|
847
|
+
const badge = document.createElement("div");
|
|
848
|
+
badge.className = dragCountBadge;
|
|
849
|
+
badge.textContent = String(dragKeys.length);
|
|
850
|
+
preview.appendChild(badge);
|
|
851
|
+
}
|
|
345
852
|
if (portalClassName) {
|
|
346
853
|
const wrapper = document.createElement("div");
|
|
347
854
|
wrapper.className = portalClassName;
|
|
@@ -354,40 +861,61 @@ function BlockHandleInner({ editor }) {
|
|
|
354
861
|
document.body.append(preview);
|
|
355
862
|
dragPreviewRef.current = preview;
|
|
356
863
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
864
|
+
if (dragKeys.length > 1) {
|
|
865
|
+
for (const key of dragKeys) {
|
|
866
|
+
editor.getElementByKey(key)?.classList.add(draggingBlock);
|
|
867
|
+
}
|
|
868
|
+
draggingBlockKeysRef.current = dragKeys;
|
|
869
|
+
} else {
|
|
870
|
+
draggingBlockRef.current = block;
|
|
871
|
+
block.classList.add(draggingBlock);
|
|
872
|
+
}
|
|
873
|
+
const offsetX = Math.max(12, Math.min(rect.width - 12, event.clientX - rect.left));
|
|
874
|
+
const offsetY = Math.max(8, Math.min(rect.height - 8, event.clientY - rect.top));
|
|
875
|
+
event.dataTransfer.setDragImage(preview, offsetX, offsetY);
|
|
362
876
|
},
|
|
363
|
-
[clearDragVisualState, handle.nodeKey, portalClassName, theme]
|
|
877
|
+
[clearDragVisualState, editor, getSelectedKeys, handle.nodeKey, portalClassName, theme]
|
|
364
878
|
);
|
|
365
879
|
const onGripOpenChange = useCallback(
|
|
366
880
|
(open) => {
|
|
367
|
-
setGripMenuOpen((
|
|
368
|
-
if (
|
|
881
|
+
setGripMenuOpen((previous) => {
|
|
882
|
+
if (previous === open) return previous;
|
|
369
883
|
onMenuOpenChange(open);
|
|
370
884
|
return open;
|
|
371
885
|
});
|
|
372
886
|
},
|
|
373
887
|
[onMenuOpenChange]
|
|
374
888
|
);
|
|
375
|
-
const onGripMouseDownCapture = useCallback((
|
|
889
|
+
const onGripMouseDownCapture = useCallback((event) => {
|
|
376
890
|
dragStartedRef.current = false;
|
|
377
|
-
if (
|
|
891
|
+
if (event.button === 0) event.stopPropagation();
|
|
378
892
|
}, []);
|
|
379
893
|
const onGripClick = useCallback(
|
|
380
|
-
(
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
894
|
+
(event) => {
|
|
895
|
+
if (event.detail === 0) return;
|
|
896
|
+
event.preventDefault();
|
|
897
|
+
event.stopPropagation();
|
|
384
898
|
if (dragStartedRef.current) {
|
|
385
899
|
dragStartedRef.current = false;
|
|
386
900
|
return;
|
|
387
901
|
}
|
|
388
|
-
|
|
902
|
+
if (!handle.nodeKey) return;
|
|
903
|
+
selectBlock(handle.nodeKey, event.shiftKey);
|
|
389
904
|
},
|
|
390
|
-
[
|
|
905
|
+
[handle.nodeKey, selectBlock]
|
|
906
|
+
);
|
|
907
|
+
const onGripContextMenu = useCallback(
|
|
908
|
+
(event) => {
|
|
909
|
+
event.preventDefault();
|
|
910
|
+
if (handle.nodeKey) {
|
|
911
|
+
const currentKeys = getSelectedKeys();
|
|
912
|
+
if (!currentKeys.includes(handle.nodeKey)) {
|
|
913
|
+
selectBlock(handle.nodeKey, false);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
onGripOpenChange(true);
|
|
917
|
+
},
|
|
918
|
+
[getSelectedKeys, handle.nodeKey, onGripOpenChange, selectBlock]
|
|
391
919
|
);
|
|
392
920
|
useEffect(() => {
|
|
393
921
|
const rootElement = editor.getRootElement();
|
|
@@ -400,7 +928,7 @@ function BlockHandleInner({ editor }) {
|
|
|
400
928
|
event.dataTransfer.dropEffect = "move";
|
|
401
929
|
const block = getDropTargetBlock(editor, rootElement, event);
|
|
402
930
|
if (!block) {
|
|
403
|
-
setDropLine((
|
|
931
|
+
setDropLine((state) => state.visible ? { ...state, visible: false } : state);
|
|
404
932
|
return true;
|
|
405
933
|
}
|
|
406
934
|
const rect = block.getBoundingClientRect();
|
|
@@ -420,24 +948,46 @@ function BlockHandleInner({ editor }) {
|
|
|
420
948
|
const unregDrop = editor.registerCommand(
|
|
421
949
|
DROP_COMMAND,
|
|
422
950
|
(event) => {
|
|
423
|
-
const
|
|
424
|
-
if (!
|
|
951
|
+
const raw = event.dataTransfer?.getData(DRAG_DATA_KEY);
|
|
952
|
+
if (!raw) return false;
|
|
425
953
|
event.preventDefault();
|
|
426
|
-
setDropLine((
|
|
954
|
+
setDropLine((state) => ({ ...state, visible: false }));
|
|
427
955
|
clearDragVisualState();
|
|
956
|
+
let draggedKeys;
|
|
957
|
+
try {
|
|
958
|
+
const parsed = JSON.parse(raw);
|
|
959
|
+
draggedKeys = Array.isArray(parsed) && parsed.every((key) => typeof key === "string") ? parsed : [raw];
|
|
960
|
+
} catch {
|
|
961
|
+
draggedKeys = [raw];
|
|
962
|
+
}
|
|
963
|
+
if (!draggedKeys.length) return false;
|
|
428
964
|
const block = getDropTargetBlock(editor, rootElement, event);
|
|
429
965
|
if (!block) return false;
|
|
430
966
|
editor.update(() => {
|
|
431
|
-
const draggedNode = $getNodeByKey(draggedKey);
|
|
432
967
|
const targetNode = $getNearestNodeFromDOMNode(block);
|
|
433
|
-
if (!
|
|
968
|
+
if (!targetNode) return;
|
|
969
|
+
if (draggedKeys.includes(targetNode.getKey())) return;
|
|
434
970
|
const rect = block.getBoundingClientRect();
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
971
|
+
const insertBefore = event.clientY < rect.top + rect.height / 2;
|
|
972
|
+
const root = $getRoot();
|
|
973
|
+
const children = root.getChildren();
|
|
974
|
+
const keySet = new Set(draggedKeys);
|
|
975
|
+
const draggedNodes = children.filter((child) => keySet.has(child.getKey()));
|
|
976
|
+
for (const node of draggedNodes) {
|
|
977
|
+
node.remove();
|
|
978
|
+
}
|
|
979
|
+
const freshTarget = $getNodeByKey(targetNode.getKey());
|
|
980
|
+
if (!freshTarget) return;
|
|
981
|
+
if (insertBefore) {
|
|
982
|
+
for (let i = draggedNodes.length - 1; i >= 0; i--) {
|
|
983
|
+
freshTarget.insertBefore(draggedNodes[i]);
|
|
984
|
+
}
|
|
439
985
|
} else {
|
|
440
|
-
|
|
986
|
+
let cursor = freshTarget;
|
|
987
|
+
for (const node of draggedNodes) {
|
|
988
|
+
cursor.insertAfter(node);
|
|
989
|
+
cursor = node;
|
|
990
|
+
}
|
|
441
991
|
}
|
|
442
992
|
});
|
|
443
993
|
return true;
|
|
@@ -453,14 +1003,14 @@ function BlockHandleInner({ editor }) {
|
|
|
453
1003
|
COMMAND_PRIORITY_HIGH
|
|
454
1004
|
);
|
|
455
1005
|
const clearDropLine = () => {
|
|
456
|
-
setDropLine((
|
|
1006
|
+
setDropLine((state) => state.visible ? { ...state, visible: false } : state);
|
|
457
1007
|
};
|
|
458
1008
|
const clearDragState = () => {
|
|
459
1009
|
clearDropLine();
|
|
460
1010
|
clearDragVisualState();
|
|
461
1011
|
};
|
|
462
|
-
const onDragLeave = (
|
|
463
|
-
if (
|
|
1012
|
+
const onDragLeave = (event) => {
|
|
1013
|
+
if (event.relatedTarget === null || !rootElement.contains(event.relatedTarget)) {
|
|
464
1014
|
clearDropLine();
|
|
465
1015
|
}
|
|
466
1016
|
};
|
|
@@ -477,6 +1027,54 @@ function BlockHandleInner({ editor }) {
|
|
|
477
1027
|
clearDragState();
|
|
478
1028
|
};
|
|
479
1029
|
}, [clearDragVisualState, editor]);
|
|
1030
|
+
useEffect(() => {
|
|
1031
|
+
const rootElement = editor.getRootElement();
|
|
1032
|
+
if (!rootElement) return;
|
|
1033
|
+
const outerContainer = rootElement.closest(".rich-editor");
|
|
1034
|
+
if (!outerContainer) return;
|
|
1035
|
+
let isDragging = false;
|
|
1036
|
+
let lastKey = null;
|
|
1037
|
+
const getBlockKeyAtY = (clientY) => {
|
|
1038
|
+
const block = getNearestBlockByY(rootElement, clientY);
|
|
1039
|
+
if (!block) return null;
|
|
1040
|
+
let nodeKey = null;
|
|
1041
|
+
editor.read(() => {
|
|
1042
|
+
nodeKey = $getNearestNodeFromDOMNode(block)?.getKey() ?? null;
|
|
1043
|
+
});
|
|
1044
|
+
return nodeKey;
|
|
1045
|
+
};
|
|
1046
|
+
const onMouseDown = (event) => {
|
|
1047
|
+
if (event.button !== 0) return;
|
|
1048
|
+
const target = event.target;
|
|
1049
|
+
if (rootElement.contains(target)) return;
|
|
1050
|
+
const nodeKey = getBlockKeyAtY(event.clientY);
|
|
1051
|
+
if (!nodeKey) return;
|
|
1052
|
+
event.preventDefault();
|
|
1053
|
+
selectBlock(nodeKey, event.shiftKey);
|
|
1054
|
+
editor.focus();
|
|
1055
|
+
isDragging = true;
|
|
1056
|
+
lastKey = nodeKey;
|
|
1057
|
+
};
|
|
1058
|
+
const onMouseMove = (event) => {
|
|
1059
|
+
if (!isDragging) return;
|
|
1060
|
+
const nodeKey = getBlockKeyAtY(event.clientY);
|
|
1061
|
+
if (!nodeKey || nodeKey === lastKey) return;
|
|
1062
|
+
selectBlock(nodeKey, true);
|
|
1063
|
+
lastKey = nodeKey;
|
|
1064
|
+
};
|
|
1065
|
+
const onMouseUp = () => {
|
|
1066
|
+
isDragging = false;
|
|
1067
|
+
lastKey = null;
|
|
1068
|
+
};
|
|
1069
|
+
outerContainer.addEventListener("mousedown", onMouseDown);
|
|
1070
|
+
window.addEventListener("mousemove", onMouseMove);
|
|
1071
|
+
window.addEventListener("mouseup", onMouseUp);
|
|
1072
|
+
return () => {
|
|
1073
|
+
outerContainer.removeEventListener("mousedown", onMouseDown);
|
|
1074
|
+
window.removeEventListener("mousemove", onMouseMove);
|
|
1075
|
+
window.removeEventListener("mouseup", onMouseUp);
|
|
1076
|
+
};
|
|
1077
|
+
}, [editor, selectBlock]);
|
|
480
1078
|
const themeWrapperProps = portalClassName ? {
|
|
481
1079
|
"className": portalClassName,
|
|
482
1080
|
"data-theme": theme,
|
|
@@ -500,6 +1098,7 @@ function BlockHandleInner({ editor }) {
|
|
|
500
1098
|
"aria-label": "Block actions",
|
|
501
1099
|
className: handleBtn,
|
|
502
1100
|
onClick: onGripClick,
|
|
1101
|
+
onContextMenu: onGripContextMenu,
|
|
503
1102
|
onDragStart: onGripDragStart,
|
|
504
1103
|
onMouseDownCapture: onGripMouseDownCapture,
|
|
505
1104
|
children: /* @__PURE__ */ jsx(GripVertical, { size: 14 })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}:root.dark{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}[data-theme=dark]{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc0{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc1{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.8;--rc-line-height-tight: 1.4;--rc-font-family: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc2{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #a3a3a3;--rc-quote-bg: #fafafa;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: none;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 2px;--rc-space-sm: 4px;--rc-space-md: 10px;--rc-space-lg: 16px;--rc-space-xl: 20px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 14px;--rc-font-size-small: 12px;--rc-line-height: 1.5;--rc-line-height-tight: 1.3;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 3px;--rc-radius-md: 6px;--rc-radius-lg: 12px}.dark ._1bfi5uc0,[data-theme=dark] ._1bfi5uc0,.dark._1bfi5uc0,[data-theme=dark]._1bfi5uc0,.dark ._1bfi5uc1,[data-theme=dark] ._1bfi5uc1,.dark._1bfi5uc1,[data-theme=dark]._1bfi5uc1,.dark ._1bfi5uc2,[data-theme=dark] ._1bfi5uc2,.dark._1bfi5uc2,[data-theme=dark]._1bfi5uc2{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4)}.iihqkc0{display:flex;align-items:center;gap:1px;position:absolute;z-index:30;opacity:0;pointer-events:none;transition:opacity .15s}.iihqkc1{opacity:1;pointer-events:auto}.iihqkc2{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;border:none;background:transparent;color:color-mix(in srgb,var(--rc-text) 35%,transparent);cursor:pointer;padding:0;transition:background-color .15s,color .15s}.iihqkc2:hover{background:color-mix(in srgb,var(--rc-text) 8%,transparent);color:color-mix(in srgb,var(--rc-text) 70%,transparent)}.iihqkc3{opacity:.35}.iihqkc4{position:fixed;top:-10000px;left:-10000px;z-index:40;pointer-events:none;margin:0;box-sizing:border-box;opacity:.9;background-color:var(--rc-bg);border:1px solid var(--rc-border);border-radius:var(--rc-radius-sm);box-shadow:var(--rc-shadow-top-bar)}.iihqkc5{position:absolute;height:2px;background:var(--rc-accent);border-radius:1px;z-index:30;pointer-events:none}.iihqkc6{color:var(--rc-alert-caution)}.iihqkc6[data-highlighted]{color:var(--rc-alert-caution);background-color:color-mix(in srgb,var(--rc-alert-caution) 8%,transparent)}.iihqkc6 svg{color:var(--rc-alert-caution)}.iihqkc6[data-highlighted] svg{color:var(--rc-alert-caution)}
|
|
1
|
+
:root{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}:root.dark{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}[data-theme=dark]{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc0{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.7;--rc-line-height-tight: 1.4;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc1{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #2563eb;--rc-quote-bg: #f5f5f5;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: 700px;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 4px;--rc-space-sm: 8px;--rc-space-md: 16px;--rc-space-lg: 24px;--rc-space-xl: 32px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 16px;--rc-font-size-small: 14px;--rc-line-height: 1.8;--rc-line-height-tight: 1.4;--rc-font-family: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm: 4px;--rc-radius-md: 8px;--rc-radius-lg: 12px}._1bfi5uc2{--rc-text: #000;--rc-text-secondary: #262626;--rc-text-tertiary: #737373;--rc-text-quaternary: #a3a3a3;--rc-bg: #ffffff;--rc-bg-secondary: #fafafa;--rc-bg-tertiary: #f5f5f5;--rc-fill: #e8e8e8;--rc-fill-secondary: #eeeeee;--rc-fill-tertiary: #f5f5f5;--rc-fill-quaternary: #fafafa;--rc-border: #f5f5f5;--rc-accent: #2563eb;--rc-accent-light: #2563eb20;--rc-link: #2563eb;--rc-code-text: #404040;--rc-code-bg: #f5f5f5;--rc-hr-border: #e5e5e5;--rc-quote-border: #a3a3a3;--rc-quote-bg: #fafafa;--rc-alert-info: #006bb7;--rc-alert-warning: #cc5500;--rc-alert-tip: #11cc00;--rc-alert-caution: #cc0011;--rc-alert-important: #5500cc;--rc-max-width: none;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.04), 0 4px 16px rgba(0,0,0,.08);--rc-space-xs: 2px;--rc-space-sm: 4px;--rc-space-md: 10px;--rc-space-lg: 16px;--rc-space-xl: 20px;--rc-font-family-sans: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif: "Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai: "楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono: "SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs: .625em;--rc-font-size-xs: .75em;--rc-font-size-sm: .8125em;--rc-font-size-md: .875em;--rc-font-size-lg: 1.25em;--rc-font-size-base: 14px;--rc-font-size-small: 12px;--rc-line-height: 1.5;--rc-line-height-tight: 1.3;--rc-font-family: "PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm: 3px;--rc-radius-md: 6px;--rc-radius-lg: 12px}.dark ._1bfi5uc0,[data-theme=dark] ._1bfi5uc0,.dark._1bfi5uc0,[data-theme=dark]._1bfi5uc0,.dark ._1bfi5uc1,[data-theme=dark] ._1bfi5uc1,.dark._1bfi5uc1,[data-theme=dark]._1bfi5uc1,.dark ._1bfi5uc2,[data-theme=dark] ._1bfi5uc2,.dark._1bfi5uc2,[data-theme=dark]._1bfi5uc2{--rc-text: #fafafa;--rc-text-secondary: #a3a3a3;--rc-text-tertiary: #737373;--rc-text-quaternary: #525252;--rc-bg: #0a0a0a;--rc-bg-secondary: #171717;--rc-bg-tertiary: #262626;--rc-fill: #2a2a2a;--rc-fill-secondary: #222222;--rc-fill-tertiary: #1a1a1a;--rc-fill-quaternary: #141414;--rc-border: #262626;--rc-accent: #60a5fa;--rc-accent-light: #60a5fa20;--rc-link: #60a5fa;--rc-code-text: #d4d4d4;--rc-code-bg: #262626;--rc-hr-border: #262626;--rc-quote-border: #60a5fa;--rc-quote-bg: #262626;--rc-alert-info: #7db9e5;--rc-alert-warning: #da864a;--rc-alert-tip: #54da48;--rc-alert-caution: #e16973;--rc-alert-important: #9966e0;--rc-shadow-top-bar: 0 8px 30px rgba(0, 0, 0, .45), 0 2px 8px rgba(0, 0, 0, .3);--rc-shadow-modal: 0 10px 15px -3px rgba(0,0,0,.4), 0 4px 6px -4px rgba(0,0,0,.35);--rc-shadow-menu: 0 1px 4px rgba(0,0,0,.25), 0 4px 16px rgba(0,0,0,.4)}.iihqkc0{display:flex;align-items:center;gap:1px;position:absolute;z-index:30;opacity:0;pointer-events:none;transition:opacity .15s}.iihqkc1{opacity:1;pointer-events:auto}.iihqkc2{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;border:none;background:transparent;color:color-mix(in srgb,var(--rc-text) 35%,transparent);cursor:pointer;padding:0;transition:background-color .15s,color .15s}.iihqkc2:hover{background:color-mix(in srgb,var(--rc-text) 8%,transparent);color:color-mix(in srgb,var(--rc-text) 70%,transparent)}.iihqkc3{opacity:.35}.iihqkc4{position:fixed;top:-10000px;left:-10000px;z-index:40;pointer-events:none;margin:0;box-sizing:border-box;opacity:.9;background-color:var(--rc-bg);border:1px solid var(--rc-border);border-radius:var(--rc-radius-sm);box-shadow:var(--rc-shadow-top-bar)}.iihqkc5{position:absolute;height:2px;background:var(--rc-accent);border-radius:1px;z-index:30;pointer-events:none}.iihqkc6{box-shadow:0 0 0 2px color-mix(in srgb,var(--rc-accent) 30%,transparent);background-color:color-mix(in srgb,var(--rc-accent) 6%,transparent);border-radius:var(--rc-radius-sm);transition:box-shadow .15s ease,background-color .15s ease}.iihqkc7{position:absolute;top:-8px;right:-8px;min-width:20px;height:20px;border-radius:10px;background-color:var(--rc-accent);color:#fff;font-size:12px;font-weight:600;display:flex;align-items:center;justify-content:center;padding:0 6px;box-shadow:0 2px 4px #00000026}.iihqkc8{color:var(--rc-alert-caution)}.iihqkc8[data-highlighted]{color:var(--rc-alert-caution);background-color:color-mix(in srgb,var(--rc-alert-caution) 8%,transparent)}.iihqkc8 svg{color:var(--rc-alert-caution)}.iihqkc8[data-highlighted] svg{color:var(--rc-alert-caution)}
|
package/dist/styles.css.d.ts
CHANGED
|
@@ -4,5 +4,7 @@ export declare const handleBtn: string;
|
|
|
4
4
|
export declare const draggingBlock: string;
|
|
5
5
|
export declare const dragPreview: string;
|
|
6
6
|
export declare const dropIndicator: string;
|
|
7
|
+
export declare const blockSelected: string;
|
|
8
|
+
export declare const dragCountBadge: string;
|
|
7
9
|
export declare const menuItemDestructive: string;
|
|
8
10
|
//# sourceMappingURL=styles.css.d.ts.map
|
package/dist/styles.css.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,QAS1B,
|
|
1
|
+
{"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,QAS1B,CAAC;AAEH,eAAO,MAAM,sBAAsB,QAGjC,CAAC;AAGH,eAAO,MAAM,SAAS,QAiBpB,CAAC;AAGH,eAAO,MAAM,aAAa,QAExB,CAAC;AAEH,eAAO,MAAM,WAAW,QAatB,CAAC;AAGH,eAAO,MAAM,aAAa,QAOxB,CAAC;AAGH,eAAO,MAAM,aAAa,QAKxB,CAAC;AAEH,eAAO,MAAM,cAAc,QAgBzB,CAAC;AAGH,eAAO,MAAM,mBAAmB,QAQ9B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LexicalEditor } from 'lexical';
|
|
2
|
+
export declare function useBlockSelection(editor: LexicalEditor): {
|
|
3
|
+
selectBlock: (nodeKey: string, shiftKey: boolean) => void;
|
|
4
|
+
getSelectedKeys: () => string[];
|
|
5
|
+
isBlockSelectionActive: () => boolean;
|
|
6
|
+
deleteSelectedBlocks: (fallbackNodeKey?: string | null) => void;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=useBlockSelection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockSelection.d.ts","sourceRoot":"","sources":["../src/useBlockSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAiBL,KAAK,aAAa,EAInB,MAAM,SAAS,CAAC;AA8BjB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa;2BA4ZzC,MAAM,YAAY,OAAO;2BA6BG,MAAM,EAAE;kCAwB1C,OAAO;6CAZQ,MAAM,GAAG,IAAI;EAiBnC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haklex/rich-plugin-block-handle",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
4
4
|
"description": "Block handle plugin with add button and context menu",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@lexical/code-core": "^0.42.0",
|
|
25
|
-
"@haklex/rich-
|
|
26
|
-
"@haklex/rich-
|
|
25
|
+
"@haklex/rich-style-token": "0.0.103",
|
|
26
|
+
"@haklex/rich-editor-ui": "0.0.103"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"@lexical/html": "^0.42.0",
|
|
29
30
|
"@lexical/list": "^0.42.0",
|
|
30
31
|
"@lexical/react": "^0.42.0",
|
|
31
32
|
"@lexical/rich-text": "^0.42.0",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"vite-plugin-dts": "^4.5.4"
|
|
44
45
|
},
|
|
45
46
|
"peerDependencies": {
|
|
47
|
+
"@lexical/html": "^0.42.0",
|
|
46
48
|
"@lexical/list": "^0.42.0",
|
|
47
49
|
"@lexical/react": "^0.42.0",
|
|
48
50
|
"@lexical/rich-text": "^0.42.0",
|