diff --git a/.changeset/tiny-adults-marry.md b/.changeset/tiny-adults-marry.md new file mode 100644 index 0000000000..4f861aec2d --- /dev/null +++ b/.changeset/tiny-adults-marry.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-dnd': patch +--- + +Improve performance of drag and drop diff --git a/apps/www/content/docs/cn/dnd.mdx b/apps/www/content/docs/cn/dnd.mdx index 5cab0609d0..b926be7b12 100644 --- a/apps/www/content/docs/cn/dnd.mdx +++ b/apps/www/content/docs/cn/dnd.mdx @@ -190,8 +190,8 @@ const plugins = [ - - 要拖动的节点的 ID。 + + 要拖动的节点。 拖动项的类型。默认为 `'block'`。 @@ -247,10 +247,10 @@ const plugins = [ 编辑器实例。 - 拖动行为的选项,包括要拖动的节点的唯一 ID。 + 拖动行为的选项,包括要拖动的节点,该节点必须有一个唯一的 ID。 - - 要拖动的节点的唯一 ID。 + + 要拖动的节点。 被拖动节点的引用。 - - 节点的 ID。 + + 节点。 放置线的当前值。 diff --git a/apps/www/content/docs/en/dnd.mdx b/apps/www/content/docs/en/dnd.mdx index 96a6cec881..5d6ff599c3 100644 --- a/apps/www/content/docs/en/dnd.mdx +++ b/apps/www/content/docs/en/dnd.mdx @@ -194,8 +194,8 @@ A custom hook that combines the `useDragNode` and `useDropNode` hooks to enable - - The ID of the node to be dragged. + + The node to be dragged. The type of drag item. Defaults to `'block'`. @@ -251,11 +251,11 @@ A custom hook that enables dragging of a node from the editor using the `useDrag The editor instance. - The options for the drag behavior, including the unique ID of the node to be - dragged. + The options for the drag behavior, including the node to be dragged, which + must have a unique ID. - - The unique ID of the node to be dragged. + + The node to be dragged. The reference to the node being dragged. - - The ID of the node. + + The node to which the drop line is attached. The current value of the drop line. diff --git a/packages/dnd/src/components/useDraggable.ts b/packages/dnd/src/components/useDraggable.ts index a222e866e7..c201ab2abf 100644 --- a/packages/dnd/src/components/useDraggable.ts +++ b/packages/dnd/src/components/useDraggable.ts @@ -1,7 +1,5 @@ import React from 'react'; -import type { TElement } from '@udecode/plate'; - import { useEditorRef } from '@udecode/plate/react'; import { type UseDndNodeOptions, DRAG_ITEM_BLOCK, useDndNode } from '..'; @@ -16,11 +14,8 @@ export type DraggableState = { ) => void; }; -export const useDraggable = ( - props: UseDndNodeOptions & { element: TElement } -): DraggableState => { +export const useDraggable = (props: UseDndNodeOptions): DraggableState => { const { - element, orientation = 'vertical', type = DRAG_ITEM_BLOCK, onDropHandler, @@ -34,7 +29,6 @@ export const useDraggable = ( // eslint-disable-next-line react-hooks/rules-of-hooks const { dragRef, isDragging } = useDndNode({ - id: element.id as string, nodeRef, orientation, type, diff --git a/packages/dnd/src/components/useDropLine.ts b/packages/dnd/src/components/useDropLine.ts index 4f22b0a9a2..463e423de3 100644 --- a/packages/dnd/src/components/useDropLine.ts +++ b/packages/dnd/src/components/useDropLine.ts @@ -1,4 +1,4 @@ -import { useEditorPlugin, useElement } from '@udecode/plate/react'; +import { useEditorRef, useElement } from '@udecode/plate/react'; import type { DropLineDirection } from '../types'; @@ -14,17 +14,18 @@ export const useDropLine = ({ } = {}): { dropLine?: DropLineDirection; } => { + const editor = useEditorRef(); const element = useElement(); const id = idProp || (element.id as string); - const dropTarget = useEditorPlugin(DndPlugin).useOption('dropTarget'); - const dropLine = dropTarget?.line; - - // Only show dropline for currently hovered element - if (id && dropTarget?.id !== id) { - return { - dropLine: '', - }; - } + + const dropLine = + editor.useOptions(DndPlugin, ({ dropTarget }) => { + if (!dropTarget) return null; + if (dropTarget.id !== id) return null; + + return dropTarget.line; + }) ?? ''; + if (orientation) { const isHorizontalDropLine = dropLine === 'left' || dropLine === 'right'; const isVerticalDropLine = dropLine === 'top' || dropLine === 'bottom'; diff --git a/packages/dnd/src/hooks/useDndNode.ts b/packages/dnd/src/hooks/useDndNode.ts index 76c9a697dc..1ba158d734 100644 --- a/packages/dnd/src/hooks/useDndNode.ts +++ b/packages/dnd/src/hooks/useDndNode.ts @@ -11,10 +11,14 @@ import { DRAG_ITEM_BLOCK, DndPlugin } from '../DndPlugin'; import { type UseDragNodeOptions, useDragNode } from './useDragNode'; import { type UseDropNodeOptions, useDropNode } from './useDropNode'; -export type UseDndNodeOptions = Partial< - Pick -> & +export type UseDndNodeOptions = Pick & + Partial> & Partial> & { + /** Options passed to the drop hook, excluding element, nodeRef. */ + drop?: Partial< + Omit + >; + preview?: { /** Whether to disable the preview. */ disable?: boolean; @@ -26,9 +30,6 @@ export type UseDndNodeOptions = Partial< /** Options passed to the drag hook. */ drag?: Partial>; - /** Options passed to the drop hook, excluding id, nodeRef. */ - drop?: Partial>; - /** Orientation of the drag and drop interaction. */ orientation?: 'horizontal' | 'vertical'; @@ -49,10 +50,10 @@ export type UseDndNodeOptions = Partial< * can be customized or removed. Returns the drag ref and drop line direction. */ export const useDndNode = ({ - id = '', canDropNode, drag: dragOptions, drop: dropOptions, + element, nodeRef, orientation = 'vertical', preview: previewOptions = {}, @@ -66,15 +67,15 @@ export const useDndNode = ({ const editor = useEditorRef(); const [{ isDragging }, dragRef, preview] = useDragNode(editor, { - id, + element, type, ...dragOptions, }); const [{ isOver }, drop] = useDropNode(editor, { - id, accept: [type, NativeTypes.FILE], canDropNode, + element, nodeRef, orientation, onDropHandler, diff --git a/packages/dnd/src/hooks/useDragNode.ts b/packages/dnd/src/hooks/useDragNode.ts index 5f804635df..0dacb2e38e 100644 --- a/packages/dnd/src/hooks/useDragNode.ts +++ b/packages/dnd/src/hooks/useDragNode.ts @@ -1,5 +1,6 @@ import { type DragSourceHookSpec, useDrag } from 'react-dnd'; +import type { TElement } from '@udecode/plate'; import type { PlateEditor } from '@udecode/plate/react'; import type { DragItemNode } from '../types'; @@ -8,7 +9,7 @@ import { DndPlugin } from '../DndPlugin'; export interface UseDragNodeOptions extends DragSourceHookSpec { - id: string; + element: TElement; } /** @@ -30,7 +31,7 @@ export interface UseDragNodeOptions */ export const useDragNode = ( editor: PlateEditor, - { id, item, ...options }: UseDragNodeOptions + { element, item, ...options }: UseDragNodeOptions ) => { return useDrag( () => ({ @@ -48,8 +49,9 @@ export const useDragNode = ( const _item = typeof item === 'function' ? item(monitor) : item; return { - id, + id: element.id as string, editorId: editor.id, + element, ..._item, }; }, diff --git a/packages/dnd/src/hooks/useDropNode.ts b/packages/dnd/src/hooks/useDropNode.ts index d131a38fc6..3402493843 100644 --- a/packages/dnd/src/hooks/useDropNode.ts +++ b/packages/dnd/src/hooks/useDropNode.ts @@ -26,8 +26,8 @@ export type CanDropCallback = (args: { export interface UseDropNodeOptions extends DropTargetHookSpec { - /** Id of the node. */ - id: string; + /** The node to which the drop line is attached. */ + element: TElement; /** The reference to the node being dragged. */ nodeRef: any; @@ -76,14 +76,16 @@ export interface UseDropNodeOptions export const useDropNode = ( editor: PlateEditor, { - id, canDropNode, + element, nodeRef, orientation, onDropHandler, ...options }: UseDropNodeOptions ) => { + const id = element.id as string; + return useDrop({ collect: (monitor) => ({ isOver: monitor.isOver({ @@ -95,9 +97,9 @@ export const useDropNode = ( if (!(dragItem as ElementDragItemNode).id) { const result = getDropPath(editor, { - id, canDropNode, dragItem: dragItem as any, + element, monitor, nodeRef, orientation, @@ -129,8 +131,8 @@ export const useDropNode = ( if (handled) return; onDropNode(editor, { - id, dragItem: dragItem as ElementDragItemNode, + element, monitor, nodeRef, orientation, @@ -138,9 +140,9 @@ export const useDropNode = ( }, hover(item: DragItemNode, monitor: DropTargetMonitor) { onHoverNode(editor, { - id, canDropNode, dragItem: item, + element, monitor, nodeRef, orientation, diff --git a/packages/dnd/src/transforms/onDropNode.spec.ts b/packages/dnd/src/transforms/onDropNode.spec.ts index 805bbfc416..2d38c4c167 100644 --- a/packages/dnd/src/transforms/onDropNode.spec.ts +++ b/packages/dnd/src/transforms/onDropNode.spec.ts @@ -1,9 +1,9 @@ +import type { TElement } from '@udecode/plate'; import type { DropTargetMonitor } from 'react-dnd'; - /* eslint-disable @typescript-eslint/no-require-imports */ import { createPlateEditor } from '@udecode/plate/react'; -import type { ElementDragItemNode } from '../types'; +import type { DragItemNode } from '../types'; import { onDropNode } from './onDropNode'; @@ -15,10 +15,13 @@ describe('onDropNode', () => { const editor = createPlateEditor(); editor.tf.moveNodes = jest.fn(); editor.tf.focus = jest.fn(); - editor.api.node = jest.fn(); + editor.api.findPath = jest.fn(); const monitor = {} as DropTargetMonitor; const nodeRef = {}; - const dragItem: ElementDragItemNode = { id: 'drag' }; + const dragElement = { id: 'drag' } as unknown as TElement; + const dragItem: DragItemNode = { id: 'drag', element: dragElement }; + + const hoverElement = { id: 'hover' } as unknown as TElement; beforeEach(() => { jest.clearAllMocks(); @@ -29,7 +32,7 @@ describe('onDropNode', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValueOnce(); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).not.toHaveBeenCalled(); }); @@ -39,9 +42,9 @@ describe('onDropNode', () => { it('should do nothing if drag node is not found', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValueOnce('bottom'); - (editor.api.node as jest.Mock).mockReturnValueOnce(undefined); + (editor.api.findPath as jest.Mock).mockReturnValueOnce(undefined); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).not.toHaveBeenCalled(); }); @@ -49,11 +52,11 @@ describe('onDropNode', () => { it('should do nothing if hover node is not found', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValueOnce('bottom'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [0]]) + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([0]) .mockReturnValueOnce(undefined); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).not.toHaveBeenCalled(); }); @@ -63,11 +66,11 @@ describe('onDropNode', () => { it('should move node below when direction is bottom', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('bottom'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [0]]) - .mockReturnValueOnce([{}, [1]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([0]) + .mockReturnValueOnce([1]); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).toHaveBeenCalledWith({ at: [0], @@ -78,11 +81,11 @@ describe('onDropNode', () => { it('should move node above when direction is top', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('top'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [2]]) - .mockReturnValueOnce([{}, [1]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([2]) + .mockReturnValueOnce([1]); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).toHaveBeenCalledWith({ at: [2], @@ -93,11 +96,11 @@ describe('onDropNode', () => { it('should not move if already in position for bottom', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('bottom'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [1]]) - .mockReturnValueOnce([{}, [0]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([1]) + .mockReturnValueOnce([0]); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).not.toHaveBeenCalled(); }); @@ -105,11 +108,11 @@ describe('onDropNode', () => { it('should not move if already in position for top', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('top'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [0]]) - .mockReturnValueOnce([{}, [1]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([0]) + .mockReturnValueOnce([1]); - onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef }); + onDropNode(editor, { dragItem, element: hoverElement, monitor, nodeRef }); expect(editor.tf.moveNodes).not.toHaveBeenCalled(); }); @@ -119,13 +122,13 @@ describe('onDropNode', () => { it('should move node right when direction is right', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('right'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [2, 0]]) - .mockReturnValueOnce([{}, [2, 1]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([2, 0]) + .mockReturnValueOnce([2, 1]); onDropNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, orientation: 'horizontal', @@ -140,13 +143,13 @@ describe('onDropNode', () => { it('should move node left when direction is left', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('left'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [2, 2]]) - .mockReturnValueOnce([{}, [2, 1]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([2, 2]) + .mockReturnValueOnce([2, 1]); onDropNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, orientation: 'horizontal', @@ -161,13 +164,13 @@ describe('onDropNode', () => { it('should not move if already in position for right', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('right'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [2, 1]]) - .mockReturnValueOnce([{}, [2, 0]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([2, 1]) + .mockReturnValueOnce([2, 0]); onDropNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, orientation: 'horizontal', @@ -179,13 +182,13 @@ describe('onDropNode', () => { it('should not move if already in position for left', () => { const { getHoverDirection } = require('../utils'); getHoverDirection.mockReturnValue('left'); - (editor.api.node as jest.Mock) - .mockReturnValueOnce([{}, [2, 0]]) - .mockReturnValueOnce([{}, [2, 1]]); + (editor.api.findPath as jest.Mock) + .mockReturnValueOnce([2, 0]) + .mockReturnValueOnce([2, 1]); onDropNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, orientation: 'horizontal', diff --git a/packages/dnd/src/transforms/onDropNode.ts b/packages/dnd/src/transforms/onDropNode.ts index 8ab0dcb735..e32af0e27b 100644 --- a/packages/dnd/src/transforms/onDropNode.ts +++ b/packages/dnd/src/transforms/onDropNode.ts @@ -1,7 +1,12 @@ import type { PlateEditor } from '@udecode/plate/react'; import type { DropTargetMonitor } from 'react-dnd'; -import { type Path, type TElement, PathApi } from '@udecode/plate'; +import { + type NodeEntry, + type Path, + type TElement, + PathApi, +} from '@udecode/plate'; import type { UseDropNodeOptions } from '../hooks'; import type { ElementDragItemNode } from '../types'; @@ -12,20 +17,23 @@ import { getHoverDirection } from '../utils'; export const getDropPath = ( editor: PlateEditor, { - id, canDropNode, dragItem, + element, monitor, nodeRef, orientation = 'vertical', }: { dragItem: ElementDragItemNode; monitor: DropTargetMonitor; - } & Pick + } & Pick< + UseDropNodeOptions, + 'canDropNode' | 'element' | 'nodeRef' | 'orientation' + > ) => { const direction = getHoverDirection({ - id, dragItem, + element, monitor, nodeRef, orientation, @@ -33,25 +41,23 @@ export const getDropPath = ( if (!direction) return; - const dragEntry = editor.api.node({ - id: dragItem.id, - at: [], - }); + const dragPath = editor.api.findPath(dragItem.element); + + if (!dragPath) return; + + const dragEntry: NodeEntry = [dragItem.element, dragPath]; - if (!dragEntry) return; + const hoveredPath = editor.api.findPath(element); - const dropEntry = editor.api.node({ id, at: [] }); + if (!hoveredPath) return; + + const dropEntry: NodeEntry = [element, hoveredPath]; if (!dropEntry) return; if (canDropNode && !canDropNode({ dragEntry, dragItem, dropEntry, editor })) { return; } - const [, dragPath] = dragEntry; - const [, hoveredPath] = dropEntry; - - editor.tf.focus(); - let dropPath: Path | undefined; // Treat 'right' like 'bottom' (after hovered) @@ -83,21 +89,24 @@ export const getDropPath = ( export const onDropNode = ( editor: PlateEditor, { - id, canDropNode, dragItem, + element, monitor, nodeRef, orientation = 'vertical', }: { dragItem: ElementDragItemNode; monitor: DropTargetMonitor; - } & Pick + } & Pick< + UseDropNodeOptions, + 'canDropNode' | 'element' | 'nodeRef' | 'orientation' + > ) => { const result = getDropPath(editor, { - id, canDropNode, dragItem, + element, monitor, nodeRef, orientation, diff --git a/packages/dnd/src/transforms/onHoverNode.spec.ts b/packages/dnd/src/transforms/onHoverNode.spec.ts index 6876881997..bb03555646 100644 --- a/packages/dnd/src/transforms/onHoverNode.spec.ts +++ b/packages/dnd/src/transforms/onHoverNode.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-require-imports */ import type { DropTargetMonitor } from 'react-dnd'; -import { RangeApi } from '@udecode/plate'; +import { type TElement, RangeApi } from '@udecode/plate'; import { createPlateEditor } from '@udecode/plate/react'; import type { DragItemNode } from '../types'; @@ -40,7 +40,10 @@ describe('onHoverNode', () => { const monitor = {} as DropTargetMonitor; const nodeRef = {}; - const dragItem: DragItemNode = { id: 'drag' }; + const dragElement = { id: 'drag' } as unknown as TElement; + const dragItem: DragItemNode = { id: 'drag', element: dragElement }; + + const hoverElement = { id: 'hover' } as unknown as TElement; beforeEach(() => { jest.clearAllMocks(); @@ -60,8 +63,8 @@ describe('onHoverNode', () => { (RangeApi.isExpanded as jest.Mock).mockReturnValue(false); onHoverNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, }); @@ -88,8 +91,8 @@ describe('onHoverNode', () => { }; onHoverNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, }); @@ -109,8 +112,8 @@ describe('onHoverNode', () => { (RangeApi.isExpanded as jest.Mock).mockReturnValue(false); onHoverNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, orientation: 'horizontal', @@ -131,8 +134,8 @@ describe('onHoverNode', () => { }); onHoverNode(editor, { - id: 'hover', dragItem, + element: hoverElement, monitor, nodeRef, }); diff --git a/packages/dnd/src/transforms/onHoverNode.ts b/packages/dnd/src/transforms/onHoverNode.ts index 7dc611ed4d..d110a2d669 100644 --- a/packages/dnd/src/transforms/onHoverNode.ts +++ b/packages/dnd/src/transforms/onHoverNode.ts @@ -11,16 +11,19 @@ import { getDropPath } from './onDropNode'; export const onHoverNode = ( editor: PlateEditor, { - id, canDropNode, dragItem, + element, monitor, nodeRef, orientation = 'vertical', }: { dragItem: DragItemNode; monitor: DropTargetMonitor; - } & Pick + } & Pick< + UseDropNodeOptions, + 'canDropNode' | 'element' | 'nodeRef' | 'orientation' + > ) => { const { dropTarget } = editor.getOptions(DndPlugin); const currentId = dropTarget?.id ?? null; @@ -28,9 +31,9 @@ export const onHoverNode = ( // Check if the drop would actually move the node. const result = getDropPath(editor, { - id, canDropNode, dragItem: dragItem as any, + element, monitor, nodeRef, orientation, @@ -47,7 +50,7 @@ export const onHoverNode = ( } const { direction } = result; - const newDropTarget = { id, line: direction }; + const newDropTarget = { id: element.id as string, line: direction }; if (newDropTarget.id !== currentId || newDropTarget.line !== currentLine) { // Only set if there's a real change diff --git a/packages/dnd/src/types.ts b/packages/dnd/src/types.ts index c54dd257a5..0d644aed9a 100644 --- a/packages/dnd/src/types.ts +++ b/packages/dnd/src/types.ts @@ -1,9 +1,12 @@ +import type { TElement } from '@udecode/plate'; + export type DragItemNode = ElementDragItemNode | FileDragItemNode; export interface ElementDragItemNode { /** Required to identify the node. */ id: string; [key: string]: unknown; + element: TElement; } export interface FileDragItemNode { diff --git a/packages/dnd/src/utils/getHoverDirection.spec.ts b/packages/dnd/src/utils/getHoverDirection.spec.ts index 012a4650b4..2fcd2de4dc 100644 --- a/packages/dnd/src/utils/getHoverDirection.spec.ts +++ b/packages/dnd/src/utils/getHoverDirection.spec.ts @@ -1,3 +1,4 @@ +import type { TElement } from '@udecode/plate'; import type { DropTargetMonitor } from 'react-dnd'; import type { DragItemNode } from '../types'; @@ -15,7 +16,10 @@ describe('getHoverDirection', () => { getClientOffset: jest.fn(), } as unknown as DropTargetMonitor; - const dragItem: DragItemNode = { id: 'drag' }; + const dragElement = { id: 'drag' } as unknown as TElement; + const dragItem: DragItemNode = { id: 'drag', element: dragElement }; + + const hoverElement = { id: 'hover' } as unknown as TElement; beforeEach(() => { jest.clearAllMocks(); @@ -29,8 +33,8 @@ describe('getHoverDirection', () => { (mockMonitor.getClientOffset as any).mockReturnValue({ x: 150, y: 120 }); const direction = getHoverDirection({ - id: 'hover', dragItem, + element: hoverElement, monitor: mockMonitor, nodeRef, orientation: 'vertical', @@ -47,8 +51,8 @@ describe('getHoverDirection', () => { (mockMonitor.getClientOffset as any).mockReturnValue({ x: 150, y: 180 }); const direction = getHoverDirection({ - id: 'hover', dragItem, + element: hoverElement, monitor: mockMonitor, nodeRef, orientation: 'vertical', @@ -65,8 +69,8 @@ describe('getHoverDirection', () => { (mockMonitor.getClientOffset as any).mockReturnValue({ x: 120, y: 150 }); const direction = getHoverDirection({ - id: 'hover', dragItem, + element: hoverElement, monitor: mockMonitor, nodeRef, orientation: 'horizontal', @@ -83,8 +87,8 @@ describe('getHoverDirection', () => { (mockMonitor.getClientOffset as any).mockReturnValue({ x: 180, y: 150 }); const direction = getHoverDirection({ - id: 'hover', dragItem, + element: hoverElement, monitor: mockMonitor, nodeRef, orientation: 'horizontal', @@ -95,8 +99,8 @@ describe('getHoverDirection', () => { it('should return undefined if dragId === id', () => { const direction = getHoverDirection({ - id: 'drag', - dragItem: { id: 'drag' }, + dragItem, + element: dragElement, monitor: mockMonitor, nodeRef, }); diff --git a/packages/dnd/src/utils/getHoverDirection.ts b/packages/dnd/src/utils/getHoverDirection.ts index 65be13af99..c88d040fef 100644 --- a/packages/dnd/src/utils/getHoverDirection.ts +++ b/packages/dnd/src/utils/getHoverDirection.ts @@ -1,3 +1,4 @@ +import type { TElement } from '@udecode/plate'; import type { DropTargetMonitor, XYCoord } from 'react-dnd'; import type { @@ -7,11 +8,11 @@ import type { } from '../types'; export interface GetHoverDirectionOptions { - /** Hovering node id. */ - id: string; - dragItem: DragItemNode; + /** Hovering node. */ + element: TElement; + monitor: DropTargetMonitor; /** The node ref of the node being dragged. */ @@ -26,18 +27,15 @@ export interface GetHoverDirectionOptions { * relative to node B. */ export const getHoverDirection = ({ - id, dragItem, + element, monitor, nodeRef, orientation = 'vertical', }: GetHoverDirectionOptions): DropDirection => { if (!nodeRef.current) return; - - const dragId = (dragItem as ElementDragItemNode).id; - // Don't replace items with themselves - if (dragId === id) return; + if (element === (dragItem as ElementDragItemNode).element) return; // Determine rectangle on screen const hoverBoundingRect = nodeRef.current?.getBoundingClientRect();