Skip to content

Commit

Permalink
drop
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfeng33 committed Nov 4, 2024
1 parent 7a5b009 commit 0cd4eeb
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 61 deletions.
2 changes: 2 additions & 0 deletions apps/www/src/lib/plate/demo/plugins/DragOverCursorPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const DragOverCursorPlugin = createPlatePlugin({
},
onDragOver: ({ editor, event, plugin }) => {
if (editor.getOptions(DndPlugin).isDragging) return;
// Only show cursor for text drag
if (!event.dataTransfer?.types.includes('text/plain')) return;

const range = findEventRange(editor, event);

Expand Down
20 changes: 18 additions & 2 deletions apps/www/src/registry/default/example/playground-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ export const usePlaygroundEditor = (id: any = '', scrollSelector?: string) => {
}),
...(id === 'list' ? [ListPlugin] : []),
ImagePlugin.extend({
options: {
disableUploadInsert: true,
},
render: { afterEditable: ImagePreview },
}),
MediaEmbedPlugin,
Expand Down Expand Up @@ -294,7 +297,20 @@ export const usePlaygroundEditor = (id: any = '', scrollSelector?: string) => {
BlockMenuPlugin.configure({
render: { aboveEditable: BlockContextMenu },
}),
DndPlugin.configure({ options: { enableScroller: true } }),
DndPlugin.configure({
options: {
enableScroller: true,
onDropFiles: (editor, props) => {
console.log(props.dropPath, 'fj');

editor
.getTransforms(ImagePlugin)
.insertImageFromFiles(props.dragItem.files, {
at: props.dropPath,
});
},
},
}),
EmojiPlugin,
exitBreakPlugin,
NodeIdPlugin,
Expand All @@ -318,7 +334,7 @@ export const usePlaygroundEditor = (id: any = '', scrollSelector?: string) => {
TrailingBlockPlugin.configure({
options: { type: ParagraphPlugin.key },
}),
DragOverCursorPlugin,
// DragOverCursorPlugin,

// Collaboration
CommentsPlugin.configure({
Expand Down
21 changes: 18 additions & 3 deletions packages/dnd/src/DndPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
import React from 'react';

import type { PluginConfig } from '@udecode/plate-common';
import type { DropTargetMonitor } from 'react-dnd';
import type { Path } from 'slate';

import { createTPlatePlugin } from '@udecode/plate-common/react';
import {
type PlateEditor,
createTPlatePlugin,
} from '@udecode/plate-common/react';

import type { DragItemNode, FileDragItemNode } from './types';

import { type ScrollerProps, DndScroller } from './components/Scroller';

export type DndConfig = PluginConfig<
'dnd',
{
draggingId?: string | null;
enableFile?: boolean;
enableScroller?: boolean;
isDragging?: boolean;
scrollerProps?: Partial<ScrollerProps>;
onDropFiles?: (
editor: PlateEditor,
props: {
id: string;
dragItem: FileDragItemNode;
monitor: DropTargetMonitor<DragItemNode, unknown>;
nodeRef: any;
dropPath?: Path;
}
) => void;
}
>;

export const DndPlugin = createTPlatePlugin<DndConfig>({
key: 'dnd',
options: {
draggingId: null,
enableFile: false,
isDragging: false,
},
handlers: {
Expand Down
8 changes: 3 additions & 5 deletions packages/dnd/src/hooks/useDndNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { NativeTypes, getEmptyImage } from 'react-dnd-html5-backend';

import type { DropTargetMonitor } from 'react-dnd';

import { type PlateEditor, useEditorPlugin } from '@udecode/plate-common/react';
import { type PlateEditor, useEditorRef } from '@udecode/plate-common/react';

import type { DragItemNode } from '../types';

import { DndPlugin } from '../DndPlugin';
import { useDraggableStore } from '../components/useDraggable';
import { type UseDragNodeOptions, useDragNode } from './useDragNode';
import { type UseDropNodeOptions, useDropNode } from './useDropNode';
Expand Down Expand Up @@ -48,8 +47,7 @@ export const useDndNode = ({
type,
onDropHandler,
}: UseDndNodeOptions) => {
const { editor, getOption } = useEditorPlugin(DndPlugin);

const editor = useEditorRef();
const [dropLine, setDropLine] = useDraggableStore().use.dropLine();

const [{ isDragging }, dragRef, preview] = useDragNode(editor, {
Expand All @@ -59,7 +57,7 @@ export const useDndNode = ({
});
const [{ isOver }, drop] = useDropNode(editor, {
id,
accept: getOption('enableFile') ? [type, NativeTypes.FILE] : [type],
accept: [type, NativeTypes.FILE],
dropLine,
nodeRef,
onChangeDropLine: setDropLine,
Expand Down
47 changes: 44 additions & 3 deletions packages/dnd/src/hooks/useDropNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ import {

import type { PlateEditor } from '@udecode/plate-common/react';

import type { DragItemNode, DropLineDirection } from '../types';
import { Path } from 'slate';

import { onDropNode } from '../transforms/onDropNode';
import type {
DragItemNode,
DropLineDirection,
ElementDragItemNode,
FileDragItemNode,
} from '../types';

import { DndPlugin } from '../DndPlugin';
import { getDropPath, onDropNode } from '../transforms/onDropNode';
import { onHoverNode } from '../transforms/onHoverNode';

export interface UseDropNodeOptions
Expand Down Expand Up @@ -77,6 +85,34 @@ export const useDropNode = (
isOver: monitor.isOver(),
}),
drop: (dragItem, monitor) => {
// Don't call onDropNode if this is a file drop

if (!(dragItem as ElementDragItemNode).id) {
const result = getDropPath(editor, {
id,
dragItem: dragItem as any,
monitor,
nodeRef,
});

const onDropFiles = editor.getOptions(DndPlugin).onDropFiles;

if (!result || !onDropFiles) return;

//FIXME
const isFirstPath = Path.equals(result.to, [0]);

const dropPath = isFirstPath ? [0] : Path.previous(result.to);

return onDropFiles(editor, {
id,
dragItem: dragItem as FileDragItemNode,
dropPath: dropPath,
monitor,
nodeRef,
});
}

const handled =
!!onDropHandler &&
onDropHandler(editor, {
Expand All @@ -88,7 +124,12 @@ export const useDropNode = (

if (handled) return;

onDropNode(editor, { id, dragItem, monitor, nodeRef });
onDropNode(editor, {
id,
dragItem: dragItem as ElementDragItemNode,
monitor,
nodeRef,
});
},
hover(item: DragItemNode, monitor: DropTargetMonitor) {
onHoverNode(editor, {
Expand Down
40 changes: 33 additions & 7 deletions packages/dnd/src/transforms/onDropNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import { focusEditor } from '@udecode/plate-common/react';
import { Path } from 'slate';

import type { UseDropNodeOptions } from '../hooks';
import type { DragItemNode } from '../types';
import type { ElementDragItemNode } from '../types';

import { getHoverDirection } from '../utils';

/** Callback called on drag an drop a node with id. */
export const onDropNode = (
export const getDropPath = (
editor: TEditor,
{
id,
dragItem,
monitor,
nodeRef,
}: {
dragItem: DragItemNode;
dragItem: ElementDragItemNode;
monitor: DropTargetMonitor;
} & Pick<UseDropNodeOptions, 'id' | 'nodeRef'>
) => {
Expand Down Expand Up @@ -61,9 +61,35 @@ export const onDropNode = (
Path.isBefore(dragPath, _dropPath) && Path.isSibling(dragPath, _dropPath);
const to = before ? _dropPath : Path.next(_dropPath);

moveNodes(editor, {
at: dragPath,
to,
});
return { dragPath, to };
}
};

export const onDropNode = (
editor: TEditor,
{
id,
dragItem,
monitor,
nodeRef,
}: {
dragItem: ElementDragItemNode;
monitor: DropTargetMonitor;
} & Pick<UseDropNodeOptions, 'id' | 'nodeRef'>
) => {
const result = getDropPath(editor, {
id,
dragItem,
monitor,
nodeRef,
});

if (!result) return;

const { dragPath, to } = result;

moveNodes(editor, {
at: dragPath,
to,
});
};
10 changes: 9 additions & 1 deletion packages/dnd/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
export interface DragItemNode {
export type DragItemNode = ElementDragItemNode | FileDragItemNode;

export interface ElementDragItemNode {
/** Required to identify the node. */
id: string;
[key: string]: unknown;
}

export interface FileDragItemNode {
dataTransfer: DataTransfer[];
files: FileList;
items: DataTransferItemList;
}

export type DropLineDirection = '' | 'bottom' | 'top';

export type DropDirection = 'bottom' | 'top' | undefined;
41 changes: 25 additions & 16 deletions packages/media/src/lib/image/BaseImagePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { type PluginConfig, createTSlatePlugin } from '@udecode/plate-common';
import {
type PluginConfig,
bindFirst,
createTSlatePlugin,
} from '@udecode/plate-common';

import type { MediaPluginOptions, TMediaElement } from '../media';

import { insertImageFromFiles } from './transforms/insertImageFromFiles';
import { withImage } from './withImage';

export interface TImageElement extends TMediaElement {
Expand Down Expand Up @@ -38,20 +43,24 @@ export const BaseImagePlugin = createTSlatePlugin<ImageConfig>({
isElement: true,
isVoid: true,
},
}).extend(({ plugin }) => ({
parsers: {
html: {
deserializer: {
parse: ({ element }) => ({
type: plugin.node.type,
url: element.getAttribute('src'),
}),
rules: [
{
validNodeName: 'IMG',
},
],
})
.extendEditorTransforms(({ editor }) => ({
insertImageFromFiles: bindFirst(insertImageFromFiles, editor),
}))
.extend(({ plugin }) => ({
parsers: {
html: {
deserializer: {
parse: ({ element }) => ({
type: plugin.node.type,
url: element.getAttribute('src'),
}),
rules: [
{
validNodeName: 'IMG',
},
],
},
},
},
},
}));
}));
33 changes: 33 additions & 0 deletions packages/media/src/lib/image/transforms/insertImageFromFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { InsertNodesOptions, SlateEditor } from '@udecode/plate-common';

import { ImagePlugin } from '../../../react';
import { insertImage } from './insertImage';

export const insertImageFromFiles = (
editor: SlateEditor,
files: FileList,
options: InsertNodesOptions = {}
) => {
for (const file of files) {
const reader = new FileReader();
const [mime] = file.type.split('/');

if (mime === 'image') {
reader.addEventListener('load', async () => {
if (!reader.result) {
return;
}

const uploadImage = editor.getOptions(ImagePlugin).uploadImage;

const uploadedUrl = uploadImage
? await uploadImage(reader.result)
: reader.result;

insertImage(editor, uploadedUrl, options);
});

reader.readAsDataURL(file);
}
}
};
Loading

0 comments on commit 0cd4eeb

Please sign in to comment.