返回 presentation-ai
getHoverDirection.ts
根目录 / src / components / notebook / presentation / editor / dnd / utils / getHoverDirection.ts
1 /** biome-ignore-all lint/suspicious/noExplicitAny: This use requires any */
2 import {
3 type DragItemNode,
4 type DropDirection,
5 type ElementDragItemNode,
6 } from "@platejs/dnd";
7 import { type TElement } from "platejs";
8 import { type DropTargetMonitor, type XYCoord } from "react-dnd";
9
10 export interface GetHoverDirectionOptions {
11 dragItem: DragItemNode;
12
13 /** Hovering node. */
14 element: TElement;
15
16 monitor: DropTargetMonitor;
17
18 /** The node ref of the node being dragged. */
19 nodeRef: any;
20 }
21
22 /**
23 * If dragging a node A over another node B: get the direction of node A
24 * relative to node B based on mouse position.
25 *
26 * Always detects all 4 directions (top/bottom/left/right).
27 * Uses edge zones for left/right detection with fallback to vertical.
28 */
29 export const getHoverDirection = ({
30 dragItem,
31 element,
32 monitor,
33 nodeRef,
34 }: GetHoverDirectionOptions): DropDirection => {
35 if (!nodeRef.current) return;
36
37 // Don't replace items with themselves
38 if (element === (dragItem as ElementDragItemNode).element) return;
39
40 // For multiple node drag, don't show drop line if hovering over any selected element
41 const elementDragItem = dragItem as ElementDragItemNode;
42 const draggedIds = Array.isArray(elementDragItem.id)
43 ? elementDragItem.id
44 : [elementDragItem.id];
45 if (draggedIds.includes(element.id as string)) return;
46
47 const HORIZONTAL_THRESHOLD = 40;
48
49 const hoverBoundingRect = nodeRef.current?.getBoundingClientRect();
50 if (!hoverBoundingRect) return;
51
52 const clientOffset = monitor.getClientOffset();
53 if (!clientOffset) return;
54
55 const hoverClientX = (clientOffset as XYCoord).x - hoverBoundingRect.left;
56 const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
57
58 // Check for left edge zone
59 if (hoverClientX < HORIZONTAL_THRESHOLD) {
60 return "left";
61 }
62
63 // Check for right edge zone
64 const hoverMiddleX = hoverBoundingRect.width / 2;
65 if (hoverClientX > hoverMiddleX + HORIZONTAL_THRESHOLD) {
66 return "right";
67 }
68
69 // Default: vertical direction based on mouse Y position
70 const hoverMiddleY = hoverBoundingRect.height / 2;
71 return hoverClientY < hoverMiddleY ? "top" : "bottom";
72 };
73
73 lines TYPESCRIPT