-
-
-
-
+export const Draggable = withRef<'div', DraggableProps>(
+ ({ className, onDropHandler, ...props }, ref) => {
+ const { children, element } = props;
+
+ const state = useDraggableState({ element, onDropHandler });
+ const { isDragging } = state;
+ const { previewRef, handleRef } = useDraggable(state);
+
+ return (
+
+
+
+
-
-
{children}
+
+ {children}
-
-
+
- );
- }
- )
+
+ );
+ }
);
const Gutter = React.forwardRef<
@@ -154,7 +149,7 @@ const DragHandle = React.memo(() => {
const DropLine = React.memo(
React.forwardRef
>(
- ({ children, className, ...props }, ref) => {
+ ({ className, ...props }, ref) => {
const state = useDropLine();
if (!state.dropLine) return null;
@@ -172,9 +167,7 @@ const DropLine = React.memo(
state.dropLine === 'bottom' && '-bottom-px',
className
)}
- >
- {children}
-
+ />
);
}
)
diff --git a/config/eslint/bases/unicorn.cjs b/config/eslint/bases/unicorn.cjs
index 08a3172d8a..a3c94d9b1a 100644
--- a/config/eslint/bases/unicorn.cjs
+++ b/config/eslint/bases/unicorn.cjs
@@ -32,6 +32,7 @@ module.exports = {
'unicorn/no-for-loop': 'off',
'unicorn/no-null': 'off',
'unicorn/no-thenable': 'off',
+ 'unicorn/no-useless-undefined': 'off',
'unicorn/prefer-export-from': 'off',
'unicorn/prefer-module': 'off',
'unicorn/prefer-optional-catch-binding': 'off',
diff --git a/packages/dnd/src/DndPlugin.tsx b/packages/dnd/src/DndPlugin.tsx
index 563fe0d8d2..a06d890f4c 100644
--- a/packages/dnd/src/DndPlugin.tsx
+++ b/packages/dnd/src/DndPlugin.tsx
@@ -9,13 +9,23 @@ import {
createTPlatePlugin,
} from '@udecode/plate-common/react';
-import type { DragItemNode, FileDragItemNode } from './types';
+import type {
+ DragItemNode,
+ DropLineDirection,
+ FileDragItemNode,
+} from './types';
import { type ScrollerProps, DndScroller } from './components/Scroller';
+export const DRAG_ITEM_BLOCK = 'block';
+
export type DndConfig = PluginConfig<
'dnd',
{
+ dropTarget?: {
+ id: string | null;
+ line: DropLineDirection;
+ };
draggingId?: string | null;
enableScroller?: boolean;
isDragging?: boolean;
@@ -35,6 +45,7 @@ export const DndPlugin = createTPlatePlugin
({
key: 'dnd',
options: {
draggingId: null,
+ dropTarget: { id: null, line: '' },
isDragging: false,
},
handlers: {
diff --git a/packages/dnd/src/components/index.ts b/packages/dnd/src/components/index.ts
index 27070539ea..895f403539 100644
--- a/packages/dnd/src/components/index.ts
+++ b/packages/dnd/src/components/index.ts
@@ -3,6 +3,7 @@
*/
export * from './useDraggable';
+export * from './useDropLine';
export * from './useWithDraggable';
export * from './withDraggable';
export * from './Scroller/index';
diff --git a/packages/dnd/src/components/useDraggable.ts b/packages/dnd/src/components/useDraggable.ts
index 42212e39fc..769bf003a5 100644
--- a/packages/dnd/src/components/useDraggable.ts
+++ b/packages/dnd/src/components/useDraggable.ts
@@ -1,18 +1,8 @@
import React from 'react';
-import type { TEditor, TElement } from '@udecode/plate-common';
-import type { DropTargetMonitor } from 'react-dnd';
+import type { TElement } from '@udecode/plate-common';
-import { createAtomStore } from '@udecode/plate-common/react';
-
-import { type DragItemNode, type DropLineDirection, useDndBlock } from '..';
-
-export const { DraggableProvider, useDraggableStore } = createAtomStore(
- {
- dropLine: '' as DropLineDirection,
- },
- { name: 'draggable' }
-);
+import { type UseDndNodeOptions, DRAG_ITEM_BLOCK, useDndNode } from '..';
export type DraggableState = {
dragRef: (
@@ -22,25 +12,25 @@ export type DraggableState = {
nodeRef: React.RefObject;
};
-export const useDraggableState = (props: {
- element: TElement;
- onDropHandler?: (
- editor: TEditor,
- props: {
- id: string;
- dragItem: DragItemNode;
- monitor: DropTargetMonitor;
- nodeRef: any;
- }
- ) => boolean;
-}): DraggableState => {
- const { element, onDropHandler } = props;
+export const useDraggableState = (
+ props: UseDndNodeOptions & { element: TElement }
+): DraggableState => {
+ const {
+ element,
+ orientation = 'vertical',
+ type = DRAG_ITEM_BLOCK,
+ onDropHandler,
+ } = props;
const nodeRef = React.useRef(null);
- const { dragRef, isDragging } = useDndBlock({
+
+ const { dragRef, isDragging } = useDndNode({
id: element.id as string,
nodeRef,
+ orientation,
+ type,
onDropHandler,
+ ...props,
});
return {
@@ -64,14 +54,3 @@ export const useDraggableGutter = () => {
},
};
};
-
-export const useDropLine = () => {
- const dropLine = useDraggableStore().get.dropLine();
-
- return {
- dropLine,
- props: {
- contentEditable: false,
- },
- };
-};
diff --git a/packages/dnd/src/components/useDropLine.ts b/packages/dnd/src/components/useDropLine.ts
new file mode 100644
index 0000000000..cfeb0ef5ff
--- /dev/null
+++ b/packages/dnd/src/components/useDropLine.ts
@@ -0,0 +1,51 @@
+import { useEditorPlugin, useElement } from '@udecode/plate-common/react';
+
+import { DndPlugin } from '../DndPlugin';
+
+export const useDropLine = ({
+ id: idProp,
+ orientation = 'vertical',
+}: {
+ /** The id of the element to show the dropline for. */
+ id?: string;
+ orientation?: 'horizontal' | 'vertical';
+} = {}) => {
+ 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: '',
+ props: {
+ contentEditable: false,
+ },
+ };
+ }
+ if (orientation) {
+ const isHorizontalDropLine = dropLine === 'left' || dropLine === 'right';
+ const isVerticalDropLine = dropLine === 'top' || dropLine === 'bottom';
+
+ // If the orientation is vertical but we got a horizontal dropline, clear it.
+ if (
+ (orientation === 'vertical' && isHorizontalDropLine) ||
+ (orientation === 'horizontal' && isVerticalDropLine)
+ ) {
+ return {
+ dropLine: '',
+ props: {
+ contentEditable: false,
+ },
+ };
+ }
+ }
+
+ return {
+ dropLine,
+ props: {
+ contentEditable: false,
+ },
+ };
+};
diff --git a/packages/dnd/src/components/useWithDraggable.ts b/packages/dnd/src/components/useWithDraggable.ts
index 5a455e5840..43c255661e 100644
--- a/packages/dnd/src/components/useWithDraggable.ts
+++ b/packages/dnd/src/components/useWithDraggable.ts
@@ -1,9 +1,9 @@
import React from 'react';
-import type { TEditor } from '@udecode/plate-common';
import type { Path } from 'slate';
import {
+ type PlateEditor,
type PlateRenderElementProps,
findNodePath,
} from '@udecode/plate-common/react';
@@ -16,7 +16,7 @@ export interface WithDraggableOptions {
draggableProps?: T;
/** Filter out elements that can't be dragged. */
- filter?: (editor: TEditor, path: Path) => boolean;
+ filter?: (editor: PlateEditor, path: Path) => boolean;
/**
* Document level where dnd is enabled. 0 = root blocks, 1 = first level of
* children, etc. Set to null to allow all levels.
diff --git a/packages/dnd/src/hooks/index.ts b/packages/dnd/src/hooks/index.ts
index bb208b66f5..3f1b38bd21 100644
--- a/packages/dnd/src/hooks/index.ts
+++ b/packages/dnd/src/hooks/index.ts
@@ -2,9 +2,6 @@
* @file Automatically generated by barrelsby.
*/
-export * from './useDndBlock';
export * from './useDndNode';
-export * from './useDragBlock';
export * from './useDragNode';
-export * from './useDropBlock';
export * from './useDropNode';
diff --git a/packages/dnd/src/hooks/useDndBlock.ts b/packages/dnd/src/hooks/useDndBlock.ts
deleted file mode 100644
index df84b889f1..0000000000
--- a/packages/dnd/src/hooks/useDndBlock.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { WithPartial } from '@udecode/plate-common';
-
-import { type UseDndNodeOptions, useDndNode } from './useDndNode';
-import { DRAG_ITEM_BLOCK } from './useDragBlock';
-
-/** {@link useDndNode} */
-export const useDndBlock = (options: WithPartial) =>
- useDndNode({
- type: DRAG_ITEM_BLOCK,
- ...options,
- });
diff --git a/packages/dnd/src/hooks/useDndNode.ts b/packages/dnd/src/hooks/useDndNode.ts
index edea21453b..0cd8b69997 100644
--- a/packages/dnd/src/hooks/useDndNode.ts
+++ b/packages/dnd/src/hooks/useDndNode.ts
@@ -7,32 +7,41 @@ import { type PlateEditor, useEditorRef } from '@udecode/plate-common/react';
import type { DragItemNode } from '../types';
-import { useDraggableStore } from '../components/useDraggable';
+import { DRAG_ITEM_BLOCK, DndPlugin } from '../DndPlugin';
import { type UseDragNodeOptions, useDragNode } from './useDragNode';
import { type UseDropNodeOptions, useDropNode } from './useDropNode';
-export interface UseDndNodeOptions
- extends Pick,
- Pick {
- onDropHandler?: (
- editor: PlateEditor,
- props: {
- id: string;
- dragItem: DragItemNode;
- monitor: DropTargetMonitor;
- nodeRef: any;
- }
- ) => boolean;
- preview?: {
- /** Whether to disable the preview. */
- disable?: boolean;
+export type UseDndNodeOptions = Partial<
+ Pick
+> &
+ Partial> & {
+ preview?: {
+ /** Whether to disable the preview. */
+ disable?: boolean;
+
+ /** The reference to the preview element. */
+ ref?: any;
+ };
+
+ /** Options passed to the drag hook. */
+ drag?: Partial>;
+
+ /** Options passed to the drop hook, excluding id, nodeRef. */
+ drop?: Partial>;
- /** The reference to the preview element. */
- ref?: any;
+ /** Orientation of the drag and drop interaction. */
+ orientation?: 'horizontal' | 'vertical';
+
+ onDropHandler?: (
+ editor: PlateEditor,
+ props: {
+ id: string;
+ dragItem: DragItemNode;
+ monitor: DropTargetMonitor;
+ nodeRef: any;
+ }
+ ) => boolean;
};
- drag?: UseDragNodeOptions;
- drop?: UseDropNodeOptions;
-}
/**
* {@link useDragNode} and {@link useDropNode} hooks to drag and drop a node from
@@ -40,28 +49,29 @@ export interface UseDndNodeOptions
* can be customized or removed. Returns the drag ref and drop line direction.
*/
export const useDndNode = ({
- id,
+ id = '',
+ canDropNode,
drag: dragOptions,
drop: dropOptions,
nodeRef,
+ orientation = 'vertical',
preview: previewOptions = {},
- type,
+ type = DRAG_ITEM_BLOCK,
onDropHandler,
}: UseDndNodeOptions) => {
const editor = useEditorRef();
- const [dropLine, setDropLine] = useDraggableStore().use.dropLine();
-
const [{ isDragging }, dragRef, preview] = useDragNode(editor, {
id,
type,
...dragOptions,
});
+
const [{ isOver }, drop] = useDropNode(editor, {
id,
accept: [type, NativeTypes.FILE],
- dropLine,
+ canDropNode,
nodeRef,
- onChangeDropLine: setDropLine,
+ orientation,
onDropHandler,
...dropOptions,
});
@@ -77,10 +87,10 @@ export const useDndNode = ({
}
useEffect(() => {
- if (!isOver && dropLine) {
- setDropLine('');
+ if (!isOver && editor.getOptions(DndPlugin).dropTarget?.id) {
+ editor.setOption(DndPlugin, 'dropTarget', { id: null, line: '' });
}
- }, [isOver, dropLine, setDropLine]);
+ }, [isOver, editor]);
return {
dragRef,
diff --git a/packages/dnd/src/hooks/useDragBlock.ts b/packages/dnd/src/hooks/useDragBlock.ts
deleted file mode 100644
index de8eb6f142..0000000000
--- a/packages/dnd/src/hooks/useDragBlock.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { PlateEditor } from '@udecode/plate-common/react';
-
-import { useDragNode } from './useDragNode';
-
-export const DRAG_ITEM_BLOCK = 'block';
-
-/** {@link useDragNode} */
-export const useDragBlock = (editor: PlateEditor, id: string) =>
- useDragNode(editor, {
- id,
- type: DRAG_ITEM_BLOCK,
- });
diff --git a/packages/dnd/src/hooks/useDropBlock.ts b/packages/dnd/src/hooks/useDropBlock.ts
deleted file mode 100644
index 846a5b7171..0000000000
--- a/packages/dnd/src/hooks/useDropBlock.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { PlateEditor } from '@udecode/plate-common/react';
-
-import { DRAG_ITEM_BLOCK } from './useDragBlock';
-import { type UseDropNodeOptions, useDropNode } from './useDropNode';
-
-/** {@link useDropNode} */
-export const useDropBlock = (
- editor: PlateEditor,
- options: Omit
-) => useDropNode(editor, { accept: DRAG_ITEM_BLOCK, ...options });
diff --git a/packages/dnd/src/hooks/useDropNode.ts b/packages/dnd/src/hooks/useDropNode.ts
index 5f88c1ab7f..90b1aeacd3 100644
--- a/packages/dnd/src/hooks/useDropNode.ts
+++ b/packages/dnd/src/hooks/useDropNode.ts
@@ -4,11 +4,11 @@ import {
useDrop,
} from 'react-dnd';
+import type { TElement, TNodeEntry } from '@udecode/plate-common';
import type { PlateEditor } from '@udecode/plate-common/react';
import type {
DragItemNode,
- DropLineDirection,
ElementDragItemNode,
FileDragItemNode,
} from '../types';
@@ -17,19 +17,21 @@ import { DndPlugin } from '../DndPlugin';
import { getDropPath, onDropNode } from '../transforms/onDropNode';
import { onHoverNode } from '../transforms/onHoverNode';
+export type CanDropCallback = (args: {
+ dragEntry: TNodeEntry;
+ dragItem: DragItemNode;
+ dropEntry: TNodeEntry;
+ editor: PlateEditor;
+}) => boolean;
+
export interface UseDropNodeOptions
extends DropTargetHookSpec {
/** Id of the node. */
id: string;
- /** Current value of dropLine. */
- dropLine: string;
-
/** The reference to the node being dragged. */
nodeRef: any;
- /** Callback called on dropLine change. */
- onChangeDropLine: (newValue: DropLineDirection) => void;
/**
* Intercepts the drop handling. If `false` is returned, the default drop
* behavior is called after. If `true` is returned, the default behavior is
@@ -44,6 +46,10 @@ export interface UseDropNodeOptions
nodeRef: any;
}
) => boolean;
+
+ canDropNode?: CanDropCallback;
+
+ orientation?: 'horizontal' | 'vertical';
}
/**
@@ -71,16 +77,18 @@ export const useDropNode = (
editor: PlateEditor,
{
id,
- dropLine,
+ canDropNode,
nodeRef,
- onChangeDropLine,
+ orientation,
onDropHandler,
...options
}: UseDropNodeOptions
) => {
return useDrop({
collect: (monitor) => ({
- isOver: monitor.isOver(),
+ isOver: monitor.isOver({
+ shallow: true,
+ }),
}),
drop: (dragItem, monitor) => {
// Don't call onDropNode if this is a file drop
@@ -88,9 +96,11 @@ export const useDropNode = (
if (!(dragItem as ElementDragItemNode).id) {
const result = getDropPath(editor, {
id,
+ canDropNode,
dragItem: dragItem as any,
monitor,
nodeRef,
+ orientation,
});
const onDropFiles = editor.getOptions(DndPlugin).onDropFiles;
@@ -123,16 +133,17 @@ export const useDropNode = (
dragItem: dragItem as ElementDragItemNode,
monitor,
nodeRef,
+ orientation,
});
},
hover(item: DragItemNode, monitor: DropTargetMonitor) {
onHoverNode(editor, {
id,
+ canDropNode,
dragItem: item,
- dropLine,
monitor,
nodeRef,
- onChangeDropLine,
+ orientation,
});
},
...options,
diff --git a/packages/dnd/src/transforms/onDropNode.spec.ts b/packages/dnd/src/transforms/onDropNode.spec.ts
new file mode 100644
index 0000000000..2367e79448
--- /dev/null
+++ b/packages/dnd/src/transforms/onDropNode.spec.ts
@@ -0,0 +1,207 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+import type { PlateEditor } from '@udecode/plate-common/react';
+import type { DropTargetMonitor } from 'react-dnd';
+
+import { moveNodes } from '@udecode/plate-common';
+import { findNode } from '@udecode/plate-common';
+
+import type { ElementDragItemNode } from '../types';
+
+import { onDropNode } from './onDropNode';
+
+jest.mock('@udecode/plate-common', () => ({
+ ...jest.requireActual('@udecode/plate-common'),
+ findNode: jest.fn(),
+ moveNodes: jest.fn(),
+}));
+
+jest.mock('@udecode/plate-common/react', () => ({
+ ...jest.requireActual('@udecode/plate-common/react'),
+ focusEditor: jest.fn(),
+}));
+
+jest.mock('../utils', () => ({
+ getHoverDirection: jest.fn(),
+}));
+
+describe('onDropNode', () => {
+ const editor = { selection: {} } as unknown as PlateEditor;
+ const monitor = {} as DropTargetMonitor;
+ const nodeRef = {};
+ const dragItem: ElementDragItemNode = { id: 'drag' };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('when direction is undefined', () => {
+ it('should do nothing', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValueOnce();
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when nodes are not found', () => {
+ it('should do nothing if drag node is not found', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValueOnce('bottom');
+ (findNode as jest.Mock).mockReturnValueOnce(undefined);
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+
+ it('should do nothing if hover node is not found', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValueOnce('bottom');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [0]])
+ .mockReturnValueOnce(undefined);
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('vertical orientation', () => {
+ it('should move node below when direction is bottom', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('bottom');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [0]])
+ .mockReturnValueOnce([{}, [1]]);
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).toHaveBeenCalledWith(editor, {
+ at: [0],
+ to: [1],
+ });
+ });
+
+ it('should move node above when direction is top', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('top');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [2]])
+ .mockReturnValueOnce([{}, [1]]);
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).toHaveBeenCalledWith(editor, {
+ at: [2],
+ to: [1],
+ });
+ });
+
+ it('should not move if already in position for bottom', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('bottom');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [1]])
+ .mockReturnValueOnce([{}, [0]]);
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+
+ it('should not move if already in position for top', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('top');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [0]])
+ .mockReturnValueOnce([{}, [1]]);
+
+ onDropNode(editor, { id: 'hover', dragItem, monitor, nodeRef });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('horizontal orientation', () => {
+ it('should move node right when direction is right', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('right');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [2, 0]])
+ .mockReturnValueOnce([{}, [2, 1]]);
+
+ onDropNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(moveNodes).toHaveBeenCalledWith(editor, {
+ at: [2, 0],
+ to: [2, 1],
+ });
+ });
+
+ it('should move node left when direction is left', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('left');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [2, 2]])
+ .mockReturnValueOnce([{}, [2, 1]]);
+
+ onDropNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(moveNodes).toHaveBeenCalledWith(editor, {
+ at: [2, 2],
+ to: [2, 1],
+ });
+ });
+
+ it('should not move if already in position for right', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('right');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [2, 1]])
+ .mockReturnValueOnce([{}, [2, 0]]);
+
+ onDropNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+
+ it('should not move if already in position for left', () => {
+ const { getHoverDirection } = require('../utils');
+ getHoverDirection.mockReturnValue('left');
+ (findNode as jest.Mock)
+ .mockReturnValueOnce([{}, [2, 0]])
+ .mockReturnValueOnce([{}, [2, 1]]);
+
+ onDropNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(moveNodes).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/packages/dnd/src/transforms/onDropNode.ts b/packages/dnd/src/transforms/onDropNode.ts
index 213bc4e686..960a13e2e1 100644
--- a/packages/dnd/src/transforms/onDropNode.ts
+++ b/packages/dnd/src/transforms/onDropNode.ts
@@ -1,7 +1,7 @@
import type { DropTargetMonitor } from 'react-dnd';
-import { type TEditor, findNode, moveNodes } from '@udecode/plate-common';
-import { focusEditor } from '@udecode/plate-common/react';
+import { type TElement, findNode, moveNodes } from '@udecode/plate-common';
+import { type PlateEditor, focusEditor } from '@udecode/plate-common/react';
import { Path } from 'slate';
import type { UseDropNodeOptions } from '../hooks';
@@ -9,79 +9,98 @@ import type { ElementDragItemNode } from '../types';
import { getHoverDirection } from '../utils';
-/** Callback called on drag an drop a node with id. */
+/** Callback called on drag and drop a node with id. */
export const getDropPath = (
- editor: TEditor,
+ editor: PlateEditor,
{
id,
+ canDropNode,
dragItem,
monitor,
nodeRef,
+ orientation = 'vertical',
}: {
dragItem: ElementDragItemNode;
monitor: DropTargetMonitor;
- } & Pick
+ } & Pick
) => {
- const direction = getHoverDirection({ id, dragItem, monitor, nodeRef });
+ const direction = getHoverDirection({
+ id,
+ dragItem,
+ monitor,
+ nodeRef,
+ orientation,
+ });
if (!direction) return;
- const dragEntry = findNode(editor, {
+ const dragEntry = findNode(editor, {
at: [],
match: { id: dragItem.id },
});
if (!dragEntry) return;
+ const dropEntry = findNode(editor, { at: [], match: { id } });
+
+ if (!dropEntry) return;
+ if (canDropNode && !canDropNode({ dragEntry, dragItem, dropEntry, editor })) {
+ return;
+ }
+
const [, dragPath] = dragEntry;
+ const [, hoveredPath] = dropEntry;
focusEditor(editor);
let dropPath: Path | undefined;
- if (direction === 'bottom') {
- dropPath = findNode(editor, { at: [], match: { id } })?.[1];
+ // Treat 'right' like 'bottom' (after hovered)
+ // Treat 'left' like 'top' (before hovered)
+ if (direction === 'bottom' || direction === 'right') {
+ // Insert after hovered node
+ dropPath = hoveredPath;
- if (!dropPath) return;
+ // If the dragged node is already right after hovered node, no change
if (Path.equals(dragPath, Path.next(dropPath))) return;
}
- if (direction === 'top') {
- const nodePath = findNode(editor, { at: [], match: { id } })?.[1];
-
- if (!nodePath) return;
-
- dropPath = [...nodePath.slice(0, -1), nodePath.at(-1)! - 1];
+ if (direction === 'top' || direction === 'left') {
+ // Insert before hovered node
+ dropPath = [...hoveredPath.slice(0, -1), hoveredPath.at(-1)! - 1];
+ // If the dragged node is already right before hovered node, no change
if (Path.equals(dragPath, dropPath)) return;
}
- if (direction) {
- const _dropPath = dropPath as Path;
- const before =
- Path.isBefore(dragPath, _dropPath) && Path.isSibling(dragPath, _dropPath);
- const to = before ? _dropPath : Path.next(_dropPath);
+ const _dropPath = dropPath as Path;
+ const before =
+ Path.isBefore(dragPath, _dropPath) && Path.isSibling(dragPath, _dropPath);
+ const to = before ? _dropPath : Path.next(_dropPath);
- return { dragPath, to };
- }
+ return { direction, dragPath, to };
};
export const onDropNode = (
- editor: TEditor,
+ editor: PlateEditor,
{
id,
+ canDropNode,
dragItem,
monitor,
nodeRef,
+ orientation = 'vertical',
}: {
dragItem: ElementDragItemNode;
monitor: DropTargetMonitor;
- } & Pick
+ } & Pick
) => {
const result = getDropPath(editor, {
id,
+ canDropNode,
dragItem,
monitor,
nodeRef,
+ orientation,
});
if (!result) return;
diff --git a/packages/dnd/src/transforms/onHoverNode.spec.ts b/packages/dnd/src/transforms/onHoverNode.spec.ts
new file mode 100644
index 0000000000..516f716643
--- /dev/null
+++ b/packages/dnd/src/transforms/onHoverNode.spec.ts
@@ -0,0 +1,142 @@
+/* eslint-disable @typescript-eslint/no-require-imports */
+import type { PlateEditor } from '@udecode/plate-common/react';
+import type { DropTargetMonitor } from 'react-dnd';
+
+import { collapseSelection, isExpanded } from '@udecode/plate-common';
+import { focusEditor } from '@udecode/plate-common/react';
+
+import type { DragItemNode } from '../types';
+
+import { DndPlugin } from '../DndPlugin';
+import { onHoverNode } from './onHoverNode';
+
+jest.mock('@udecode/plate-common', () => ({
+ ...jest.requireActual('@udecode/plate-common'),
+ collapseSelection: jest.fn(),
+ isExpanded: jest.fn(),
+}));
+
+jest.mock('@udecode/plate-common/react', () => ({
+ ...jest.requireActual('@udecode/plate-common/react'),
+ focusEditor: jest.fn(),
+}));
+
+jest.mock('../utils', () => ({
+ getHoverDirection: jest.fn(),
+ getNewDirection: jest.fn((dropLine, direction) => direction),
+}));
+
+jest.mock('./onDropNode', () => ({
+ getDropPath: jest.fn((editor, options) => ({
+ direction: options.orientation === 'horizontal' ? 'left' : 'bottom',
+ dragPath: [0],
+ to: [1],
+ })),
+}));
+
+describe('onHoverNode', () => {
+ const editor = {
+ getOptions: jest.fn(),
+ selection: {},
+ setOption: jest.fn(),
+ } as unknown as PlateEditor;
+ const monitor = {} as DropTargetMonitor;
+ const nodeRef = {};
+ const dragItem: DragItemNode = { id: 'drag' };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ (editor.getOptions as jest.Mock).mockReturnValue({
+ dropTarget: { id: null, line: '' },
+ });
+ });
+
+ it('should update plugin options when direction changes', () => {
+ const { getDropPath } = require('./onDropNode');
+ getDropPath.mockReturnValueOnce({
+ direction: 'bottom',
+ dragPath: [0],
+ to: [1],
+ });
+
+ (isExpanded as jest.Mock).mockReturnValue(false);
+
+ onHoverNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ });
+
+ expect(editor.setOption).toHaveBeenCalledWith(DndPlugin, 'dropTarget', {
+ id: 'hover',
+ line: 'bottom',
+ });
+ });
+
+ it('should collapse selection and focus editor if direction is returned and selection is expanded', () => {
+ const { getDropPath } = require('./onDropNode');
+ getDropPath.mockReturnValueOnce({
+ direction: 'top',
+ dragPath: [0],
+ to: [1],
+ });
+
+ (isExpanded as jest.Mock).mockReturnValue(true);
+
+ onHoverNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ });
+
+ expect(collapseSelection).toHaveBeenCalledWith(editor);
+ expect(focusEditor).toHaveBeenCalledWith(editor);
+ });
+
+ it('should handle horizontal orientation', () => {
+ const { getDropPath } = require('./onDropNode');
+ getDropPath.mockReturnValueOnce({
+ direction: 'left',
+ dragPath: [0],
+ to: [1],
+ });
+
+ (isExpanded as jest.Mock).mockReturnValue(false);
+
+ onHoverNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(editor.setOption).toHaveBeenCalledWith(DndPlugin, 'dropTarget', {
+ id: 'hover',
+ line: 'left',
+ });
+ });
+
+ it('should clear dropTarget when no direction is returned', () => {
+ const { getDropPath } = require('./onDropNode');
+ getDropPath.mockReturnValueOnce(undefined);
+
+ (editor.getOptions as jest.Mock).mockReturnValue({
+ dropTarget: { id: 'hover', line: 'bottom' },
+ });
+
+ onHoverNode(editor, {
+ id: 'hover',
+ dragItem,
+ monitor,
+ nodeRef,
+ });
+
+ expect(editor.setOption).toHaveBeenCalledWith(DndPlugin, 'dropTarget', {
+ id: null,
+ line: '',
+ });
+ });
+});
diff --git a/packages/dnd/src/transforms/onHoverNode.ts b/packages/dnd/src/transforms/onHoverNode.ts
index f3d0598268..3517e313d6 100644
--- a/packages/dnd/src/transforms/onHoverNode.ts
+++ b/packages/dnd/src/transforms/onHoverNode.ts
@@ -1,44 +1,60 @@
import type { DropTargetMonitor } from 'react-dnd';
-import {
- type TEditor,
- collapseSelection,
- isExpanded,
-} from '@udecode/plate-common';
-import { focusEditor } from '@udecode/plate-common/react';
+import { collapseSelection, isExpanded } from '@udecode/plate-common';
+import { type PlateEditor, focusEditor } from '@udecode/plate-common/react';
import type { UseDropNodeOptions } from '../hooks/useDropNode';
import type { DragItemNode } from '../types';
-import { getHoverDirection, getNewDirection } from '../utils';
+import { DndPlugin } from '../DndPlugin';
+import { getDropPath } from './onDropNode';
/** Callback called when dragging a node and hovering nodes. */
export const onHoverNode = (
- editor: TEditor,
+ editor: PlateEditor,
{
id,
+ canDropNode,
dragItem,
- dropLine,
monitor,
nodeRef,
- onChangeDropLine,
+ orientation = 'vertical',
}: {
dragItem: DragItemNode;
monitor: DropTargetMonitor;
- } & Pick<
- UseDropNodeOptions,
- 'dropLine' | 'id' | 'nodeRef' | 'onChangeDropLine'
- >
+ } & Pick
) => {
- const direction = getHoverDirection({
+ const { dropTarget } = editor.getOptions(DndPlugin);
+ const currentId = dropTarget?.id ?? null;
+ const currentLine = dropTarget?.line ?? '';
+
+ // Check if the drop would actually move the node.
+ const result = getDropPath(editor, {
id,
- dragItem,
+ canDropNode,
+ dragItem: dragItem as any,
monitor,
nodeRef,
+ orientation,
});
- const dropLineDir = getNewDirection(dropLine, direction);
- if (dropLineDir) onChangeDropLine(dropLineDir);
+ // If getDropPath returns undefined, it means no actual move would happen.
+ // In that case, don't show a drop target.
+ if (!result) {
+ if (currentId || currentLine) {
+ editor.setOption(DndPlugin, 'dropTarget', { id: null, line: '' });
+ }
+
+ return;
+ }
+
+ const { direction } = result;
+ const newDropTarget = { id, line: direction };
+
+ if (newDropTarget.id !== currentId || newDropTarget.line !== currentLine) {
+ // Only set if there's a real change
+ editor.setOption(DndPlugin, 'dropTarget', newDropTarget);
+ }
if (direction && isExpanded(editor.selection)) {
focusEditor(editor);
collapseSelection(editor);
diff --git a/packages/dnd/src/transforms/selectBlocksBySelectionOrId.ts b/packages/dnd/src/transforms/selectBlocksBySelectionOrId.ts
index d8666314dd..6d7939d541 100644
--- a/packages/dnd/src/transforms/selectBlocksBySelectionOrId.ts
+++ b/packages/dnd/src/transforms/selectBlocksBySelectionOrId.ts
@@ -1,7 +1,7 @@
import type { Range } from 'slate';
-import { type SlateEditor, getNodesRange, select } from '@udecode/plate-common';
-import { focusEditor } from '@udecode/plate-common/react';
+import { getNodesRange, select } from '@udecode/plate-common';
+import { type PlateEditor, focusEditor } from '@udecode/plate-common/react';
import { getBlocksWithId } from '../queries/getBlocksWithId';
import { selectBlockById } from './selectBlockById';
@@ -11,7 +11,7 @@ import { selectBlockById } from './selectBlockById';
* select the block with id. Else, select the blocks above the selection.
*/
export const selectBlocksBySelectionOrId = (
- editor: SlateEditor,
+ editor: PlateEditor,
id: string
) => {
if (!editor.selection) return;
diff --git a/packages/dnd/src/types.ts b/packages/dnd/src/types.ts
index f628e82f25..c54dd257a5 100644
--- a/packages/dnd/src/types.ts
+++ b/packages/dnd/src/types.ts
@@ -12,6 +12,6 @@ export interface FileDragItemNode {
items: DataTransferItemList;
}
-export type DropLineDirection = '' | 'bottom' | 'top';
+export type DropLineDirection = '' | 'bottom' | 'left' | 'right' | 'top';
-export type DropDirection = 'bottom' | 'top' | undefined;
+export type DropDirection = 'bottom' | 'left' | 'right' | 'top' | undefined;
diff --git a/packages/dnd/src/utils/getHoverDirection.spec.ts b/packages/dnd/src/utils/getHoverDirection.spec.ts
new file mode 100644
index 0000000000..012a4650b4
--- /dev/null
+++ b/packages/dnd/src/utils/getHoverDirection.spec.ts
@@ -0,0 +1,106 @@
+import type { DropTargetMonitor } from 'react-dnd';
+
+import type { DragItemNode } from '../types';
+
+import { getHoverDirection } from './getHoverDirection';
+
+describe('getHoverDirection', () => {
+ const nodeRef = {
+ current: {
+ getBoundingClientRect: jest.fn(),
+ },
+ } as any;
+
+ const mockMonitor = {
+ getClientOffset: jest.fn(),
+ } as unknown as DropTargetMonitor;
+
+ const dragItem: DragItemNode = { id: 'drag' };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return "top" when vertical and mouse is above middle', () => {
+ nodeRef.current.getBoundingClientRect.mockReturnValue({
+ bottom: 200,
+ top: 100,
+ });
+ (mockMonitor.getClientOffset as any).mockReturnValue({ x: 150, y: 120 });
+
+ const direction = getHoverDirection({
+ id: 'hover',
+ dragItem,
+ monitor: mockMonitor,
+ nodeRef,
+ orientation: 'vertical',
+ });
+
+ expect(direction).toBe('top');
+ });
+
+ it('should return "bottom" when vertical and mouse is below middle', () => {
+ nodeRef.current.getBoundingClientRect.mockReturnValue({
+ bottom: 200,
+ top: 100,
+ });
+ (mockMonitor.getClientOffset as any).mockReturnValue({ x: 150, y: 180 });
+
+ const direction = getHoverDirection({
+ id: 'hover',
+ dragItem,
+ monitor: mockMonitor,
+ nodeRef,
+ orientation: 'vertical',
+ });
+
+ expect(direction).toBe('bottom');
+ });
+
+ it('should return "left" when horizontal and mouse is left of middle', () => {
+ nodeRef.current.getBoundingClientRect.mockReturnValue({
+ left: 100,
+ right: 200,
+ });
+ (mockMonitor.getClientOffset as any).mockReturnValue({ x: 120, y: 150 });
+
+ const direction = getHoverDirection({
+ id: 'hover',
+ dragItem,
+ monitor: mockMonitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(direction).toBe('left');
+ });
+
+ it('should return "right" when horizontal and mouse is right of middle', () => {
+ nodeRef.current.getBoundingClientRect.mockReturnValue({
+ left: 100,
+ right: 200,
+ });
+ (mockMonitor.getClientOffset as any).mockReturnValue({ x: 180, y: 150 });
+
+ const direction = getHoverDirection({
+ id: 'hover',
+ dragItem,
+ monitor: mockMonitor,
+ nodeRef,
+ orientation: 'horizontal',
+ });
+
+ expect(direction).toBe('right');
+ });
+
+ it('should return undefined if dragId === id', () => {
+ const direction = getHoverDirection({
+ id: 'drag',
+ dragItem: { id: 'drag' },
+ monitor: mockMonitor,
+ nodeRef,
+ });
+
+ expect(direction).toBeUndefined();
+ });
+});
diff --git a/packages/dnd/src/utils/getHoverDirection.ts b/packages/dnd/src/utils/getHoverDirection.ts
index 67ed5d8346..65be13af99 100644
--- a/packages/dnd/src/utils/getHoverDirection.ts
+++ b/packages/dnd/src/utils/getHoverDirection.ts
@@ -16,6 +16,9 @@ export interface GetHoverDirectionOptions {
/** The node ref of the node being dragged. */
nodeRef: any;
+
+ /** The orientation of the drag operation. */
+ orientation?: 'horizontal' | 'vertical';
}
/**
@@ -27,6 +30,7 @@ export const getHoverDirection = ({
dragItem,
monitor,
nodeRef,
+ orientation = 'vertical',
}: GetHoverDirectionOptions): DropDirection => {
if (!nodeRef.current) return;
@@ -38,29 +42,40 @@ export const getHoverDirection = ({
// Determine rectangle on screen
const hoverBoundingRect = nodeRef.current?.getBoundingClientRect();
- // Get vertical middle
- const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
+ if (!hoverBoundingRect) {
+ return;
+ }
// Determine mouse position
const clientOffset = monitor.getClientOffset();
- if (!clientOffset) return;
+ if (!clientOffset) {
+ return;
+ }
+ if (orientation === 'vertical') {
+ // Get vertical middle
+ const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
- // Get pixels to the top
- const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
+ // Get pixels to the top
+ const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
- // Only perform the move when the mouse has crossed half of the items height
- // When dragging downwards, only move when the cursor is below 50%
- // When dragging upwards, only move when the cursor is above 50%
+ // Only perform the move when the mouse has crossed half of the items height
+ // When dragging downwards, only move when the cursor is below 50%
+ // When dragging upwards, only move when the cursor is above 50%
- // Dragging downwards
- // if (dragId < hoverId && hoverClientY < hoverMiddleY) {
- if (hoverClientY < hoverMiddleY) {
- return 'top';
- }
- // Dragging upwards
- // if (dragId > hoverId && hoverClientY > hoverMiddleY) {
- if (hoverClientY >= hoverMiddleY) {
- return 'bottom';
+ // Dragging downwards
+ if (hoverClientY < hoverMiddleY) {
+ return 'top';
+ }
+ // Dragging upwards
+ if (hoverClientY >= hoverMiddleY) {
+ return 'bottom';
+ }
+ } else {
+ // Horizontal orientation for columns
+ const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2;
+ const hoverClientX = (clientOffset as XYCoord).x - hoverBoundingRect.left;
+
+ return hoverClientX < hoverMiddleX ? 'left' : 'right';
}
};
diff --git a/packages/dnd/src/utils/getNewDirection.ts b/packages/dnd/src/utils/getNewDirection.ts
index 9d3bc78b2a..749291d4ce 100644
--- a/packages/dnd/src/utils/getNewDirection.ts
+++ b/packages/dnd/src/utils/getNewDirection.ts
@@ -14,4 +14,10 @@ export const getNewDirection = (
if (dir === 'bottom' && previousDir !== 'bottom') {
return 'bottom';
}
+ if (dir === 'left' && previousDir !== 'left') {
+ return 'left';
+ }
+ if (dir === 'right' && previousDir !== 'right') {
+ return 'right';
+ }
};
diff --git a/yarn.lock b/yarn.lock
index d21c3e7b69..45c187f143 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6422,7 +6422,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6440,7 +6440,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6451,16 +6451,16 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/plate-basic-elements@npm:40.2.6, @udecode/plate-basic-elements@workspace:^, @udecode/plate-basic-elements@workspace:packages/basic-elements":
+"@udecode/plate-basic-elements@npm:40.3.4, @udecode/plate-basic-elements@workspace:^, @udecode/plate-basic-elements@workspace:packages/basic-elements":
version: 0.0.0-use.local
resolution: "@udecode/plate-basic-elements@workspace:packages/basic-elements"
dependencies:
"@udecode/plate-block-quote": "npm:40.0.0"
- "@udecode/plate-code-block": "npm:40.0.0"
+ "@udecode/plate-code-block": "npm:40.3.4"
"@udecode/plate-common": "workspace:^"
"@udecode/plate-heading": "npm:40.2.6"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6477,7 +6477,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6494,7 +6494,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6511,7 +6511,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6528,7 +6528,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6546,7 +6546,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
react-textarea-autosize: "npm:^8.5.3"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6566,7 +6566,7 @@ __metadata:
delay: "npm:5.0.0"
p-defer: "npm:^4.0.1"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6577,14 +6577,14 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/plate-code-block@npm:40.0.0, @udecode/plate-code-block@workspace:^, @udecode/plate-code-block@workspace:packages/code-block":
+"@udecode/plate-code-block@npm:40.3.4, @udecode/plate-code-block@workspace:^, @udecode/plate-code-block@workspace:packages/code-block":
version: 0.0.0-use.local
resolution: "@udecode/plate-code-block@workspace:packages/code-block"
dependencies:
"@udecode/plate-common": "workspace:^"
prismjs: "npm:^1.29.0"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6601,7 +6601,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6612,14 +6612,14 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/plate-comments@npm:40.0.0, @udecode/plate-comments@workspace:^, @udecode/plate-comments@workspace:packages/comments":
+"@udecode/plate-comments@npm:40.3.0, @udecode/plate-comments@workspace:^, @udecode/plate-comments@workspace:packages/comments":
version: 0.0.0-use.local
resolution: "@udecode/plate-comments@workspace:packages/comments"
dependencies:
"@udecode/plate-common": "workspace:^"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6630,17 +6630,17 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/plate-common@npm:40.2.8, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common":
+"@udecode/plate-common@npm:40.3.1, @udecode/plate-common@workspace:^, @udecode/plate-common@workspace:packages/common":
version: 0.0.0-use.local
resolution: "@udecode/plate-common@workspace:packages/common"
dependencies:
- "@udecode/plate-core": "npm:40.2.8"
- "@udecode/plate-utils": "npm:40.2.8"
+ "@udecode/plate-core": "npm:40.3.1"
+ "@udecode/plate-utils": "npm:40.3.1"
"@udecode/react-hotkeys": "npm:37.0.0"
"@udecode/react-utils": "npm:40.2.8"
- "@udecode/slate": "npm:39.2.1"
- "@udecode/slate-react": "npm:40.2.8"
- "@udecode/slate-utils": "npm:40.2.7"
+ "@udecode/slate": "npm:40.3.1"
+ "@udecode/slate-react": "npm:40.3.1"
+ "@udecode/slate-utils": "npm:40.3.1"
"@udecode/utils": "npm:37.0.0"
peerDependencies:
react: ">=16.8.0"
@@ -6653,15 +6653,15 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/plate-core@npm:40.2.8, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core":
+"@udecode/plate-core@npm:40.3.1, @udecode/plate-core@workspace:^, @udecode/plate-core@workspace:packages/core":
version: 0.0.0-use.local
resolution: "@udecode/plate-core@workspace:packages/core"
dependencies:
"@udecode/react-hotkeys": "npm:37.0.0"
"@udecode/react-utils": "npm:40.2.8"
- "@udecode/slate": "npm:39.2.1"
- "@udecode/slate-react": "npm:40.2.8"
- "@udecode/slate-utils": "npm:40.2.7"
+ "@udecode/slate": "npm:40.3.1"
+ "@udecode/slate-react": "npm:40.3.1"
+ "@udecode/slate-utils": "npm:40.3.1"
"@udecode/utils": "npm:37.0.0"
clsx: "npm:^2.1.1"
is-hotkey: "npm:^0.2.0"
@@ -6694,7 +6694,7 @@ __metadata:
"@udecode/plate-table": "npm:40.0.0"
papaparse: "npm:^5.4.1"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6711,7 +6711,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6728,7 +6728,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.94.0"
@@ -6746,7 +6746,7 @@ __metadata:
diff-match-patch-ts: "npm:^0.6.0"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6765,7 +6765,7 @@ __metadata:
lodash: "npm:^4.17.21"
raf: "npm:^3.4.1"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dnd: ">=14.0.0"
react-dnd-html5-backend: ">=14.0.0"
@@ -6790,7 +6790,7 @@ __metadata:
"@udecode/plate-table": "npm:40.0.0"
validator: "npm:^13.12.0"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6809,7 +6809,7 @@ __metadata:
"@udecode/plate-combobox": "npm:40.0.0"
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6827,7 +6827,7 @@ __metadata:
"@excalidraw/excalidraw": "npm:0.16.4"
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6844,7 +6844,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6863,7 +6863,7 @@ __metadata:
"@floating-ui/react": "npm:^0.26.23"
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6881,7 +6881,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6898,7 +6898,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6915,7 +6915,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6932,7 +6932,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6951,7 +6951,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
html-entities: "npm:^2.5.2"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6971,7 +6971,7 @@ __metadata:
"@udecode/plate-list": "npm:40.0.0"
clsx: "npm:^2.1.1"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -6988,7 +6988,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7006,7 +7006,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
juice: "npm:^8.1.0"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7023,7 +7023,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7040,7 +7040,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7057,7 +7057,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7076,7 +7076,7 @@ __metadata:
"@udecode/plate-floating": "npm:40.0.0"
"@udecode/plate-normalizers": "npm:40.0.0"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7095,7 +7095,7 @@ __metadata:
"@udecode/plate-reset-node": "npm:40.0.0"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7116,7 +7116,7 @@ __metadata:
remark-parse: "npm:^11.0.0"
unified: "npm:^11.0.5"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7135,7 +7135,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
katex: "npm:0.16.11"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7153,7 +7153,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
js-video-url-parser: "npm:^0.5.1"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7171,7 +7171,7 @@ __metadata:
"@udecode/plate-combobox": "npm:40.0.0"
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7189,7 +7189,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7207,7 +7207,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7225,7 +7225,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
peerDependencies:
"@playwright/test": ">=1.42.1"
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7242,7 +7242,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7259,7 +7259,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7276,7 +7276,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7294,7 +7294,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
copy-to-clipboard: "npm:^3.3.3"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7312,7 +7312,7 @@ __metadata:
"@udecode/plate-combobox": "npm:40.0.0"
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7331,7 +7331,7 @@ __metadata:
"@udecode/plate-diff": "npm:40.0.0"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7349,7 +7349,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
tabbable: "npm:^6.2.0"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7368,7 +7368,7 @@ __metadata:
"@udecode/plate-resizable": "npm:40.0.0"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7384,7 +7384,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7413,7 +7413,7 @@ __metadata:
"@udecode/plate-node-id": "npm:40.0.0"
lodash: "npm:^4.17.21"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7430,7 +7430,7 @@ __metadata:
dependencies:
"@udecode/plate-common": "workspace:^"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7441,15 +7441,15 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/plate-utils@npm:40.2.8, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils":
+"@udecode/plate-utils@npm:40.3.1, @udecode/plate-utils@workspace:^, @udecode/plate-utils@workspace:packages/plate-utils":
version: 0.0.0-use.local
resolution: "@udecode/plate-utils@workspace:packages/plate-utils"
dependencies:
- "@udecode/plate-core": "npm:40.2.8"
+ "@udecode/plate-core": "npm:40.3.1"
"@udecode/react-utils": "npm:40.2.8"
- "@udecode/slate": "npm:39.2.1"
- "@udecode/slate-react": "npm:40.2.8"
- "@udecode/slate-utils": "npm:40.2.7"
+ "@udecode/slate": "npm:40.3.1"
+ "@udecode/slate-react": "npm:40.3.1"
+ "@udecode/slate-utils": "npm:40.3.1"
"@udecode/utils": "npm:37.0.0"
clsx: "npm:^2.1.1"
lodash: "npm:^4.17.21"
@@ -7472,7 +7472,7 @@ __metadata:
"@udecode/plate-common": "workspace:^"
yjs: "npm:^13.6.19"
peerDependencies:
- "@udecode/plate-common": ">=40.2.8"
+ "@udecode/plate-common": ">=40.3.1"
react: ">=16.8.0"
react-dom: ">=16.8.0"
slate: ">=0.112.0"
@@ -7489,14 +7489,14 @@ __metadata:
dependencies:
"@udecode/plate-alignment": "npm:40.0.0"
"@udecode/plate-autoformat": "npm:40.0.0"
- "@udecode/plate-basic-elements": "npm:40.2.6"
+ "@udecode/plate-basic-elements": "npm:40.3.4"
"@udecode/plate-basic-marks": "npm:40.0.0"
"@udecode/plate-block-quote": "npm:40.0.0"
"@udecode/plate-break": "npm:40.0.0"
- "@udecode/plate-code-block": "npm:40.0.0"
+ "@udecode/plate-code-block": "npm:40.3.4"
"@udecode/plate-combobox": "npm:40.0.0"
- "@udecode/plate-comments": "npm:40.0.0"
- "@udecode/plate-common": "npm:40.2.8"
+ "@udecode/plate-comments": "npm:40.3.0"
+ "@udecode/plate-common": "npm:40.3.1"
"@udecode/plate-csv": "npm:40.0.0"
"@udecode/plate-diff": "npm:40.0.0"
"@udecode/plate-docx": "npm:40.2.7"
@@ -7562,12 +7562,12 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/slate-react@npm:40.2.8, @udecode/slate-react@workspace:^, @udecode/slate-react@workspace:packages/slate-react":
+"@udecode/slate-react@npm:40.3.1, @udecode/slate-react@workspace:^, @udecode/slate-react@workspace:packages/slate-react":
version: 0.0.0-use.local
resolution: "@udecode/slate-react@workspace:packages/slate-react"
dependencies:
"@udecode/react-utils": "npm:40.2.8"
- "@udecode/slate": "npm:39.2.1"
+ "@udecode/slate": "npm:40.3.1"
"@udecode/utils": "npm:37.0.0"
peerDependencies:
react: ">=16.8.0"
@@ -7578,11 +7578,11 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/slate-utils@npm:40.2.7, @udecode/slate-utils@workspace:^, @udecode/slate-utils@workspace:packages/slate-utils":
+"@udecode/slate-utils@npm:40.3.1, @udecode/slate-utils@workspace:^, @udecode/slate-utils@workspace:packages/slate-utils":
version: 0.0.0-use.local
resolution: "@udecode/slate-utils@workspace:packages/slate-utils"
dependencies:
- "@udecode/slate": "npm:39.2.1"
+ "@udecode/slate": "npm:40.3.1"
"@udecode/utils": "npm:37.0.0"
lodash: "npm:^4.17.21"
peerDependencies:
@@ -7591,7 +7591,7 @@ __metadata:
languageName: unknown
linkType: soft
-"@udecode/slate@npm:39.2.1, @udecode/slate@workspace:^, @udecode/slate@workspace:packages/slate":
+"@udecode/slate@npm:40.3.1, @udecode/slate@workspace:^, @udecode/slate@workspace:packages/slate":
version: 0.0.0-use.local
resolution: "@udecode/slate@workspace:packages/slate"
dependencies: