onchain-lexical-components 0.0.1 → 0.0.3

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.
@@ -0,0 +1,805 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
12
+ var lexical = require('lexical');
13
+ var onchainLexicalInstance = require('onchain-lexical-instance');
14
+ var react = require('react');
15
+ var richText = require('@lexical/rich-text');
16
+ var utils = require('@lexical/utils');
17
+ var traversal = require('onchain-utility/traversal');
18
+ var reactDom = require('react-dom');
19
+ var jsxRuntime = require('react/jsx-runtime');
20
+
21
+ /**
22
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
23
+ *
24
+ * This source code is licensed under the MIT license found in the
25
+ * LICENSE file in the root directory of this source tree.
26
+ *
27
+ */
28
+ const useLatest = value => {
29
+ const latest = react.useRef(value);
30
+ react.useEffect(() => {
31
+ latest.current = value;
32
+ }, [value]);
33
+ return latest;
34
+ };
35
+
36
+ /**
37
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
38
+ *
39
+ * This source code is licensed under the MIT license found in the
40
+ * LICENSE file in the root directory of this source tree.
41
+ *
42
+ */
43
+ class Point {
44
+ constructor(x, y) {
45
+ this._x = x;
46
+ this._y = y;
47
+ }
48
+ get x() {
49
+ return this._x;
50
+ }
51
+ get y() {
52
+ return this._y;
53
+ }
54
+ equals({
55
+ x,
56
+ y
57
+ }) {
58
+ return this.x === x && this.y === y;
59
+ }
60
+ calcDeltaXTo({
61
+ x
62
+ }) {
63
+ return this.x - x;
64
+ }
65
+ calcDeltaYTo({
66
+ y
67
+ }) {
68
+ return this.y - y;
69
+ }
70
+ calcHorizontalDistanceTo(point) {
71
+ return Math.abs(this.calcDeltaXTo(point));
72
+ }
73
+ calcVerticalDistance(point) {
74
+ return Math.abs(this.calcDeltaYTo(point));
75
+ }
76
+ calcDistanceTo(point) {
77
+ return Math.sqrt(Math.pow(this.calcDeltaXTo(point), 2) + Math.pow(this.calcDeltaYTo(point), 2));
78
+ }
79
+ }
80
+ function isPoint(x) {
81
+ return x instanceof Point;
82
+ }
83
+
84
+ /**
85
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
86
+ *
87
+ * This source code is licensed under the MIT license found in the
88
+ * LICENSE file in the root directory of this source tree.
89
+ *
90
+ */
91
+ class Rectangle {
92
+ constructor(left, top, right, bottom) {
93
+ const [physicTop, physicBottom] = top <= bottom ? [top, bottom] : [bottom, top];
94
+ const [physicLeft, physicRight] = left <= right ? [left, right] : [right, left];
95
+ this._top = physicTop;
96
+ this._right = physicRight;
97
+ this._left = physicLeft;
98
+ this._bottom = physicBottom;
99
+ }
100
+ get top() {
101
+ return this._top;
102
+ }
103
+ get right() {
104
+ return this._right;
105
+ }
106
+ get bottom() {
107
+ return this._bottom;
108
+ }
109
+ get left() {
110
+ return this._left;
111
+ }
112
+ get width() {
113
+ return Math.abs(this._left - this._right);
114
+ }
115
+ get height() {
116
+ return Math.abs(this._bottom - this._top);
117
+ }
118
+ equals({
119
+ top,
120
+ left,
121
+ bottom,
122
+ right
123
+ }) {
124
+ return top === this._top && bottom === this._bottom && left === this._left && right === this._right;
125
+ }
126
+ contains(target) {
127
+ if (isPoint(target)) {
128
+ const {
129
+ x,
130
+ y
131
+ } = target;
132
+ const isOnTopSide = y < this._top;
133
+ const isOnBottomSide = y > this._bottom;
134
+ const isOnLeftSide = x < this._left;
135
+ const isOnRightSide = x > this._right;
136
+ const result = !isOnTopSide && !isOnBottomSide && !isOnLeftSide && !isOnRightSide;
137
+ return {
138
+ reason: {
139
+ isOnBottomSide,
140
+ isOnLeftSide,
141
+ isOnRightSide,
142
+ isOnTopSide
143
+ },
144
+ result
145
+ };
146
+ } else {
147
+ const {
148
+ top,
149
+ left,
150
+ bottom,
151
+ right
152
+ } = target;
153
+ return top >= this._top && top <= this._bottom && bottom >= this._top && bottom <= this._bottom && left >= this._left && left <= this._right && right >= this._left && right <= this._right;
154
+ }
155
+ }
156
+ intersectsWith(rect) {
157
+ const {
158
+ left: x1,
159
+ top: y1,
160
+ width: w1,
161
+ height: h1
162
+ } = rect;
163
+ const {
164
+ left: x2,
165
+ top: y2,
166
+ width: w2,
167
+ height: h2
168
+ } = this;
169
+ const maxX = x1 + w1 >= x2 + w2 ? x1 + w1 : x2 + w2;
170
+ const maxY = y1 + h1 >= y2 + h2 ? y1 + h1 : y2 + h2;
171
+ const minX = x1 <= x2 ? x1 : x2;
172
+ const minY = y1 <= y2 ? y1 : y2;
173
+ return maxX - minX <= w1 + w2 && maxY - minY <= h1 + h2;
174
+ }
175
+ generateNewRect({
176
+ left = this.left,
177
+ top = this.top,
178
+ right = this.right,
179
+ bottom = this.bottom
180
+ }) {
181
+ return new Rectangle(left, top, right, bottom);
182
+ }
183
+ static fromLTRB(left, top, right, bottom) {
184
+ return new Rectangle(left, top, right, bottom);
185
+ }
186
+ static fromLWTH(left, width, top, height) {
187
+ return new Rectangle(left, top, left + width, top + height);
188
+ }
189
+ static fromPoints(startPoint, endPoint) {
190
+ const {
191
+ y: top,
192
+ x: left
193
+ } = startPoint;
194
+ const {
195
+ y: bottom,
196
+ x: right
197
+ } = endPoint;
198
+ return Rectangle.fromLTRB(left, top, right, bottom);
199
+ }
200
+ static fromDOM(dom) {
201
+ const {
202
+ top,
203
+ width,
204
+ left,
205
+ height
206
+ } = dom.getBoundingClientRect();
207
+ return Rectangle.fromLWTH(left, width, top, height);
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
213
+ *
214
+ * This source code is licensed under the MIT license found in the
215
+ * LICENSE file in the root directory of this source tree.
216
+ *
217
+ */
218
+
219
+ const SPACE = 4;
220
+ const TARGET_LINE_HALF_HEIGHT = 2;
221
+ const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
222
+ const TEXT_BOX_HORIZONTAL_PADDING = 28;
223
+ const BOX_INDENT_INSERT = 60;
224
+ const Downward = 1;
225
+ const Upward = -1;
226
+ const Indeterminate = 0;
227
+ let prevIndex = Infinity;
228
+ function getCurrentIndex(keysLength) {
229
+ if (keysLength === 0) {
230
+ return Infinity;
231
+ }
232
+ if (prevIndex >= 0 && prevIndex < keysLength) {
233
+ return prevIndex;
234
+ }
235
+ return Math.floor(keysLength / 2);
236
+ }
237
+ function getTopLevelNodeKeys(editor) {
238
+ return editor.getEditorState().read(() => traversal.dfs(lexical.$getRoot().getChildren(), node => {
239
+ if (node.getChildren) {
240
+ return node.getChildren().filter(node => onchainLexicalInstance.$isInstanceNode(node));
241
+ }
242
+ return [];
243
+ })).map(node => {
244
+ return node.getKey();
245
+ });
246
+ }
247
+ function getCollapsedMargins(elem) {
248
+ const getMargin = (element, margin) => element ? parseFloat(window.getComputedStyle(element)[margin]) : 0;
249
+ const {
250
+ marginTop,
251
+ marginBottom
252
+ } = window.getComputedStyle(elem);
253
+ const prevElemSiblingMarginBottom = getMargin(elem.previousElementSibling, 'marginBottom');
254
+ const nextElemSiblingMarginTop = getMargin(elem.nextElementSibling, 'marginTop');
255
+ const collapsedTopMargin = Math.max(parseFloat(marginTop), prevElemSiblingMarginBottom);
256
+ const collapsedBottomMargin = Math.max(parseFloat(marginBottom), nextElemSiblingMarginTop);
257
+ return {
258
+ marginBottom: collapsedBottomMargin,
259
+ marginTop: collapsedTopMargin
260
+ };
261
+ }
262
+ function getBlockElement(anchorElem, editor, event, useEdgeAsDefault = false) {
263
+ const anchorElementRect = anchorElem.getBoundingClientRect();
264
+ const topLevelNodeKeys = getTopLevelNodeKeys(editor);
265
+ let blockElem = null;
266
+ editor.getEditorState().read(() => {
267
+ if (useEdgeAsDefault) {
268
+ const [firstNode, lastNode] = [editor.getElementByKey(topLevelNodeKeys[0]), editor.getElementByKey(topLevelNodeKeys[topLevelNodeKeys.length - 1])];
269
+ const [firstNodeRect, lastNodeRect] = [firstNode != null ? firstNode.getBoundingClientRect() : undefined, lastNode != null ? lastNode.getBoundingClientRect() : undefined];
270
+ if (firstNodeRect && lastNodeRect) {
271
+ const firstNodeZoom = utils.calculateZoomLevel(firstNode);
272
+ const lastNodeZoom = utils.calculateZoomLevel(lastNode);
273
+ if (event.y / firstNodeZoom < firstNodeRect.top) {
274
+ blockElem = firstNode;
275
+ } else if (event.y / lastNodeZoom > lastNodeRect.bottom) {
276
+ blockElem = lastNode;
277
+ }
278
+ if (blockElem) {
279
+ return;
280
+ }
281
+ }
282
+ }
283
+ // 直接获取第一个有instance属性的祖先元素 并返回
284
+ if (event.target instanceof Element) {
285
+ blockElem = event.target.closest(`[instance=true]`);
286
+ return blockElem;
287
+ }
288
+ let index = getCurrentIndex(topLevelNodeKeys.length);
289
+ let direction = Indeterminate;
290
+ while (index >= 0 && index < topLevelNodeKeys.length) {
291
+ const key = topLevelNodeKeys[index];
292
+ const elem = editor.getElementByKey(key);
293
+ if (elem === null) {
294
+ break;
295
+ }
296
+ const zoom = utils.calculateZoomLevel(elem);
297
+ const point = new Point(event.x / zoom, event.y / zoom);
298
+ const domRect = Rectangle.fromDOM(elem);
299
+ // console.log({direction, elem, index, zoom, point, domRect}, "elem")
300
+ const {
301
+ marginTop,
302
+ marginBottom
303
+ } = getCollapsedMargins(elem);
304
+ const rect = domRect.generateNewRect({
305
+ bottom: domRect.bottom + marginBottom,
306
+ left: anchorElementRect.left,
307
+ right: anchorElementRect.right,
308
+ top: domRect.top - marginTop
309
+ });
310
+ const {
311
+ result,
312
+ reason: {
313
+ isOnTopSide,
314
+ isOnBottomSide
315
+ }
316
+ } = rect.contains(point);
317
+ if (result) {
318
+ blockElem = elem;
319
+ prevIndex = index;
320
+ break;
321
+ }
322
+ if (direction === Indeterminate) {
323
+ if (isOnTopSide) {
324
+ direction = Upward;
325
+ } else if (isOnBottomSide) {
326
+ direction = Downward;
327
+ } else {
328
+ // stop search block element
329
+ direction = Infinity;
330
+ }
331
+ }
332
+ index += direction;
333
+ }
334
+ });
335
+ return blockElem;
336
+ }
337
+ function setMenuPosition(targetElem, floatingElem, anchorElem) {
338
+ if (!targetElem) {
339
+ floatingElem.style.opacity = '0';
340
+ floatingElem.style.transform = 'translate(-10000px, -10000px)';
341
+ return;
342
+ }
343
+ const targetRect = targetElem.getBoundingClientRect();
344
+ window.getComputedStyle(targetElem);
345
+ // const floatingElemRect = floatingElem.getBoundingClientRect();
346
+ const anchorElementRect = anchorElem.getBoundingClientRect();
347
+
348
+ // [源码修改]
349
+ // const top =
350
+ // targetRect.top +
351
+ // (targetCalculateHeight - floatingElemRect.height) / 2 -
352
+ // anchorElementRect.top;
353
+ const top = targetRect.top - anchorElementRect.top;
354
+ const left = SPACE;
355
+ floatingElem.style.opacity = '1';
356
+ floatingElem.style.transform = `translate(${left}px, ${top}px)`;
357
+ }
358
+ function setDragImage(dataTransfer, draggableBlockElem) {
359
+ const {
360
+ transform
361
+ } = draggableBlockElem.style;
362
+
363
+ // Remove dragImage borders
364
+ draggableBlockElem.style.transform = 'translateZ(0)';
365
+ dataTransfer.setDragImage(draggableBlockElem, 0, 0);
366
+ setTimeout(() => {
367
+ draggableBlockElem.style.transform = transform;
368
+ });
369
+ }
370
+
371
+ /** 放置区域是否是自身 */
372
+ // function isPlacementAreaElemSelf(
373
+ // targetBlockElem: HTMLElement,
374
+ // latestDraggableBlockElem: React.MutableRefObject<HTMLElement | null>,
375
+ // ) {
376
+ // const isTargetSelf = latestDraggableBlockElem.current === targetBlockElem;
377
+ // return isTargetSelf;
378
+ // }
379
+
380
+ /** 放置区域是否是自身的子级 */
381
+ function $isPlacementAreaElemSelfRoChild(targetBlockElem, latestDraggableBlockElem, isPlacementDown = true) {
382
+ const key = targetBlockElem.getAttribute('key');
383
+ let node = key ? lexical.$getNodeByKey(key) : null;
384
+ if (!isPlacementDown) {
385
+ node = node ? node.getPreviousSibling() : null;
386
+ }
387
+ if (node) {
388
+ return Boolean(utils.$findMatchingParent(node, current => {
389
+ const draggable = latestDraggableBlockElem.current;
390
+ if (current && draggable) {
391
+ return current.getKey() === draggable.getAttribute('key');
392
+ }
393
+ return false;
394
+ }));
395
+ }
396
+ return false;
397
+ }
398
+
399
+ /** 是否是插入到子节点 */
400
+ function isInsertChild(targetBlockMouseX) {
401
+ const isIndent = targetBlockMouseX > BOX_INDENT_INSERT;
402
+ return isIndent;
403
+ }
404
+
405
+ /** 是否是放到节点下面 */
406
+ function isPlacementAreaDown(mouseY, targetBlockElemTop) {
407
+ return mouseY >= targetBlockElemTop + 40;
408
+ }
409
+
410
+ /** 获取鼠标位置以及一些基础数据 */
411
+ function getMouseInfo({
412
+ target,
413
+ pageY,
414
+ pageX,
415
+ targetBlockElem
416
+ }) {
417
+ const mouseY = pageY / utils.calculateZoomLevel(target);
418
+ const mouseX = pageX / utils.calculateZoomLevel(target);
419
+ const targetBlockElemDOMRect = targetBlockElem.getBoundingClientRect();
420
+ const {
421
+ left: targetBlockElemLeft
422
+ } = targetBlockElemDOMRect;
423
+ const targetBlockMouseX = mouseX - targetBlockElemLeft;
424
+ const isAddChild = isInsertChild(targetBlockMouseX);
425
+ return {
426
+ isAddChild,
427
+ mouseX,
428
+ mouseY,
429
+ targetBlockElemDOMRect
430
+ };
431
+ }
432
+ function setTargetLine(params) {
433
+ const {
434
+ targetBlockElem,
435
+ targetLineElem,
436
+ mouseY,
437
+ mouseX,
438
+ anchorElem,
439
+ targetLineIndent,
440
+ targetBlockElemDOMRect
441
+ } = params;
442
+ const {
443
+ top: targetBlockElemTop,
444
+ height: targetBlockElemHeight,
445
+ left: targetBlockElemLeft,
446
+ width: targetBlockElemWidth
447
+ } = targetBlockElemDOMRect || targetBlockElem.getBoundingClientRect();
448
+ const {
449
+ top: anchorTop
450
+ } = anchorElem.getBoundingClientRect();
451
+ const {
452
+ marginTop,
453
+ marginBottom
454
+ } = getCollapsedMargins(targetBlockElem);
455
+ let lineTop = targetBlockElemTop;
456
+ if (isPlacementAreaDown(mouseY, targetBlockElemTop)) {
457
+ lineTop += targetBlockElemHeight + marginBottom / 2;
458
+ } else {
459
+ lineTop -= marginTop / 2;
460
+ }
461
+ const targetBlockMouseX = mouseX - targetBlockElemLeft;
462
+ const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT;
463
+ const lineIndent = targetLineIndent || TEXT_BOX_HORIZONTAL_PADDING;
464
+ const isAddChild = isInsertChild(targetBlockMouseX);
465
+ const left = isAddChild ? BOX_INDENT_INSERT + lineIndent : lineIndent;
466
+ targetLineElem.style.transform = `translate(${left}px, ${top}px)`;
467
+ targetLineElem.style.backgroundColor = isAddChild ? 'purple' : 'deepskyblue';
468
+ targetLineElem.style.width = `${targetBlockElemWidth - (isAddChild ? BOX_INDENT_INSERT : 0)}px`;
469
+ targetLineElem.style.opacity = '.4';
470
+ }
471
+ function hideTargetLine(targetLineElem) {
472
+ if (targetLineElem) {
473
+ targetLineElem.style.opacity = '0';
474
+ targetLineElem.style.transform = 'translate(-10000px, -10000px)';
475
+ }
476
+ }
477
+ function useDraggableBlockMenu(editor, anchorElem, menuRef, targetLineRef, isEditable, menuComponent, targetLineComponent, isOnMenu, targetLineIndent, onElementChanged) {
478
+ const scrollerElem = anchorElem.parentElement;
479
+ const isDraggingBlockRef = react.useRef(false);
480
+ const [draggableBlockElem, setDraggableBlockElemState] = react.useState(null);
481
+ const latestDraggableBlockElem = useLatest(draggableBlockElem);
482
+ const setDraggableBlockElem = react.useCallback(elem => {
483
+ setDraggableBlockElemState(elem);
484
+ if (onElementChanged) {
485
+ onElementChanged(elem);
486
+ }
487
+ }, [onElementChanged]);
488
+ react.useEffect(() => {
489
+ function onMouseMove(event) {
490
+ const target = event.target;
491
+ if (!utils.isHTMLElement(target)) {
492
+ setDraggableBlockElem(null);
493
+ return;
494
+ }
495
+ if (isOnMenu(target)) {
496
+ return;
497
+ }
498
+ const _draggableBlockElem = getBlockElement(anchorElem, editor, event);
499
+ setDraggableBlockElem(_draggableBlockElem);
500
+ }
501
+ function onMouseLeave() {
502
+ setDraggableBlockElem(null);
503
+ }
504
+ if (scrollerElem != null) {
505
+ scrollerElem.addEventListener('mousemove', onMouseMove);
506
+ scrollerElem.addEventListener('mouseleave', onMouseLeave);
507
+ }
508
+ return () => {
509
+ if (scrollerElem != null) {
510
+ scrollerElem.removeEventListener('mousemove', onMouseMove);
511
+ scrollerElem.removeEventListener('mouseleave', onMouseLeave);
512
+ }
513
+ };
514
+ }, [scrollerElem, anchorElem, editor, isOnMenu, setDraggableBlockElem]);
515
+ react.useEffect(() => {
516
+ if (menuRef.current) {
517
+ // 更新操作栏区域
518
+ setMenuPosition(draggableBlockElem, menuRef.current, anchorElem);
519
+ }
520
+ }, [anchorElem, draggableBlockElem, menuRef]);
521
+ react.useEffect(() => {
522
+ // 拖动
523
+ function $onDragover(event) {
524
+ if (!isDraggingBlockRef.current) {
525
+ return false;
526
+ }
527
+ const [isFileTransfer] = richText.eventFiles(event);
528
+ if (isFileTransfer) {
529
+ return false;
530
+ }
531
+ const {
532
+ pageY,
533
+ pageX,
534
+ target
535
+ } = event;
536
+ if (!utils.isHTMLElement(target)) {
537
+ return false;
538
+ }
539
+ const targetBlockElem = getBlockElement(anchorElem, editor, event, true);
540
+ const targetLineElem = targetLineRef.current;
541
+ if (targetBlockElem === null || targetLineElem === null) {
542
+ return false;
543
+ }
544
+ const {
545
+ mouseY,
546
+ mouseX,
547
+ isAddChild,
548
+ targetBlockElemDOMRect
549
+ } = getMouseInfo({
550
+ pageX,
551
+ pageY,
552
+ target,
553
+ targetBlockElem
554
+ });
555
+ const isPlacementDown = isPlacementAreaDown(mouseY, targetBlockElemDOMRect.top);
556
+ const isPlacementSelfRoChild = $isPlacementAreaElemSelfRoChild(targetBlockElem, latestDraggableBlockElem, isPlacementDown);
557
+ if (isPlacementSelfRoChild) {
558
+ if (isAddChild) {
559
+ hideTargetLine(targetLineElem);
560
+ return false;
561
+ }
562
+ const key = targetBlockElem.getAttribute('key');
563
+ const targetNode = key ? lexical.$getNodeByKey(key) : null;
564
+ if (!targetNode || !lexical.$isRootNode(targetNode.getParent())) {
565
+ hideTargetLine(targetLineElem);
566
+ return false;
567
+ }
568
+ }
569
+ setTargetLine({
570
+ anchorElem,
571
+ mouseX,
572
+ mouseY,
573
+ targetBlockElem,
574
+ targetBlockElemDOMRect,
575
+ targetLineElem,
576
+ targetLineIndent
577
+ });
578
+ // Prevent default event to be able to trigger onDrop events
579
+ event.preventDefault();
580
+ return true;
581
+ }
582
+
583
+ // 放下
584
+ function $onDrop(event) {
585
+ if (!isDraggingBlockRef.current) {
586
+ return false;
587
+ }
588
+ const [isFileTransfer] = richText.eventFiles(event);
589
+ if (isFileTransfer) {
590
+ return false;
591
+ }
592
+ const {
593
+ target,
594
+ dataTransfer,
595
+ pageY,
596
+ pageX
597
+ } = event;
598
+ const dragData = dataTransfer != null ? dataTransfer.getData(DRAG_DATA_FORMAT) : '';
599
+ const draggedNode = lexical.$getNodeByKey(dragData);
600
+ if (!draggedNode) {
601
+ return false;
602
+ }
603
+ if (!utils.isHTMLElement(target)) {
604
+ return false;
605
+ }
606
+ const targetBlockElem = getBlockElement(anchorElem, editor, event, true);
607
+ if (!targetBlockElem) {
608
+ return false;
609
+ }
610
+ const targetNode = lexical.$getNearestNodeFromDOMNode(targetBlockElem);
611
+ if (!targetNode) {
612
+ return false;
613
+ }
614
+ const {
615
+ mouseY,
616
+ isAddChild,
617
+ targetBlockElemDOMRect: {
618
+ top: targetBlockElemTop
619
+ }
620
+ } = getMouseInfo({
621
+ pageX,
622
+ pageY,
623
+ target,
624
+ targetBlockElem
625
+ });
626
+ if (targetNode === draggedNode && !isAddChild) {
627
+ return true;
628
+ }
629
+ if (isPlacementAreaDown(mouseY, targetBlockElemTop)) {
630
+ if (isAddChild) {
631
+ if (onchainLexicalInstance.$isInstanceNode(targetNode)) {
632
+ targetNode.append(draggedNode);
633
+ }
634
+ } else {
635
+ targetNode.insertAfter(draggedNode);
636
+ }
637
+ } else {
638
+ if (isAddChild && lexical.$isElementNode(targetNode)) {
639
+ const previous = targetNode.getPreviousSibling();
640
+ if (onchainLexicalInstance.$isInstanceNode(previous)) {
641
+ previous.append(draggedNode);
642
+ }
643
+ } else {
644
+ targetNode.insertBefore(draggedNode);
645
+ }
646
+ }
647
+ draggedNode.selectEnd();
648
+ setDraggableBlockElem(null);
649
+ return true;
650
+ }
651
+
652
+ // 添加拖拽事件
653
+ return utils.mergeRegister(editor.registerCommand(lexical.DRAGOVER_COMMAND, event => {
654
+ const result = $onDragover(event);
655
+ if (!result) {
656
+ if (event.dataTransfer) {
657
+ event.dataTransfer.dropEffect = 'none';
658
+ }
659
+ }
660
+ return result;
661
+ }, lexical.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical.DROP_COMMAND, event => {
662
+ return $onDrop(event);
663
+ }, lexical.COMMAND_PRIORITY_HIGH));
664
+ }, [anchorElem, editor, targetLineRef, setDraggableBlockElem, targetLineIndent, latestDraggableBlockElem]);
665
+ function onDragStart(event) {
666
+ const dataTransfer = event.dataTransfer;
667
+ if (!dataTransfer || !draggableBlockElem) {
668
+ return;
669
+ }
670
+ setDragImage(dataTransfer, draggableBlockElem);
671
+ let nodeKey = '';
672
+ editor.update(() => {
673
+ const node = lexical.$getNearestNodeFromDOMNode(draggableBlockElem);
674
+ if (node) {
675
+ nodeKey = node.getKey();
676
+ }
677
+ });
678
+ isDraggingBlockRef.current = true;
679
+ dataTransfer.setData(DRAG_DATA_FORMAT, nodeKey);
680
+ }
681
+ function onDragEnd() {
682
+ isDraggingBlockRef.current = false;
683
+ hideTargetLine(targetLineRef.current);
684
+ }
685
+ return /*#__PURE__*/reactDom.createPortal(/*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
686
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
687
+ draggable: true,
688
+ onDragStart: onDragStart,
689
+ onDragEnd: onDragEnd,
690
+ children: isEditable && menuComponent
691
+ }), targetLineComponent]
692
+ }), anchorElem);
693
+ }
694
+ function DraggableBlockPlugin_EXPERIMENTAL({
695
+ anchorElem = document.body,
696
+ menuRef,
697
+ targetLineRef,
698
+ menuComponent,
699
+ targetLineComponent,
700
+ targetLineIndent,
701
+ isOnMenu,
702
+ onElementChanged
703
+ }) {
704
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
705
+ return useDraggableBlockMenu(editor, anchorElem, menuRef, targetLineRef, editor._editable, menuComponent, targetLineComponent, isOnMenu, targetLineIndent, onElementChanged);
706
+ }
707
+
708
+ function styleInject(css, ref) {
709
+ if (ref === void 0) ref = {};
710
+ var insertAt = ref.insertAt;
711
+ if (typeof document === 'undefined') {
712
+ return;
713
+ }
714
+ var head = document.head || document.getElementsByTagName('head')[0];
715
+ var style = document.createElement('style');
716
+ style.type = 'text/css';
717
+ if (insertAt === 'top') {
718
+ if (head.firstChild) {
719
+ head.insertBefore(style, head.firstChild);
720
+ } else {
721
+ head.appendChild(style);
722
+ }
723
+ } else {
724
+ head.appendChild(style);
725
+ }
726
+ if (style.styleSheet) {
727
+ style.styleSheet.cssText = css;
728
+ } else {
729
+ style.appendChild(document.createTextNode(css));
730
+ }
731
+ }
732
+
733
+ var css_248z = ".index-module_draggable-block-menu__LSGkG{align-items:center;border-radius:4px;cursor:grab;display:flex;gap:2px;height:40px;left:0;opacity:0;position:absolute;top:0;will-change:transform}.index-module_draggable-block-menu__LSGkG .index-module_icon__J-jv8{align-items:center;background-image:url(/richText/draggable-block-menu.svg);display:flex;height:16px;justify-content:center;opacity:.3;width:16px}.index-module_draggable-block-menu__LSGkG .index-module_icon__J-jv8:hover{background-color:#efefef}.index-module_draggable-block-menu__LSGkG .index-module_icon-plus__jGImy{background-color:transparent;background-image:url(/richText/plus.svg);border:none;cursor:pointer;display:inline-block}.index-module_draggable-block-menu__LSGkG:active{cursor:grabbing}.index-module_draggable-block-target-line__iSspi{background:#00bfff;height:4px;left:0;opacity:0;pointer-events:none;position:absolute;top:0;will-change:transform}";
734
+ var Styles = {"draggable-block-menu":"index-module_draggable-block-menu__LSGkG","icon":"index-module_icon__J-jv8","icon-plus":"index-module_icon-plus__jGImy","draggable-block-target-line":"index-module_draggable-block-target-line__iSspi"};
735
+ styleInject(css_248z);
736
+
737
+ /**
738
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
739
+ *
740
+ * This source code is licensed under the MIT license found in the
741
+ * LICENSE file in the root directory of this source tree.
742
+ *
743
+ */
744
+
745
+ const DRAGGABLE_BLOCK_MENU_CLASSNAME = Styles['draggable-block-menu'];
746
+ function isOnMenu(element) {
747
+ return !!element.closest(`.${DRAGGABLE_BLOCK_MENU_CLASSNAME}`);
748
+ }
749
+ function DraggableBlockPlugin({
750
+ anchorElem = document.body,
751
+ dragIcon,
752
+ targetLineIndent
753
+ }) {
754
+ const [editor] = LexicalComposerContext.useLexicalComposerContext();
755
+ const menuRef = react.useRef(null);
756
+ const targetLineRef = react.useRef(null);
757
+ const [draggableElement, setDraggableElement] = react.useState(null);
758
+ function insertBlock(e) {
759
+ if (!draggableElement || !editor) {
760
+ return;
761
+ }
762
+ editor.update(() => {
763
+ const node = lexical.$getNearestNodeFromDOMNode(draggableElement);
764
+ if (!node) {
765
+ return;
766
+ }
767
+ const pNode = onchainLexicalInstance.$createInstanceNode();
768
+ if (e.altKey || e.ctrlKey) {
769
+ node.insertBefore(pNode);
770
+ } else {
771
+ node.insertAfter(pNode);
772
+ }
773
+ pNode.select();
774
+ });
775
+ }
776
+ return /*#__PURE__*/jsxRuntime.jsx(DraggableBlockPlugin_EXPERIMENTAL, {
777
+ anchorElem: anchorElem,
778
+ menuRef: menuRef,
779
+ targetLineRef: targetLineRef,
780
+ menuComponent: /*#__PURE__*/jsxRuntime.jsxs("div", {
781
+ ref: menuRef,
782
+ className: `${Styles.icon} ${Styles['draggable-block-menu']}`,
783
+ children: [/*#__PURE__*/jsxRuntime.jsx("button", {
784
+ title: "Click to add below",
785
+ className: `${Styles.icon} ${Styles['icon-plus']}`,
786
+ onClick: insertBlock,
787
+ style: {
788
+ display: 'none'
789
+ }
790
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
791
+ className: Styles.icon,
792
+ children: dragIcon
793
+ })]
794
+ }),
795
+ targetLineComponent: /*#__PURE__*/jsxRuntime.jsx("div", {
796
+ ref: targetLineRef,
797
+ className: `${Styles['draggable-block-target-line']}`
798
+ }),
799
+ targetLineIndent: targetLineIndent,
800
+ isOnMenu: isOnMenu,
801
+ onElementChanged: setDraggableElement
802
+ });
803
+ }
804
+
805
+ exports.default = DraggableBlockPlugin;