Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Media #3708

Merged
merged 26 commits into from
Nov 5, 2024
Merged

Media #3708

Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chilly-peaches-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/plate-ai': patch
---

Missing export
5 changes: 5 additions & 0 deletions .changeset/rare-zebras-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/plate-dnd': patch
---

Add `enableFile` option to check whether to enable the DnD plugin for files dragged in from outside the browser.
10 changes: 10 additions & 0 deletions .changeset/sour-bananas-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@udecode/plate-media': minor
---

New `initialHeight` and `initialWidth` in `TImageElement` This will display a loading placeholder while the image is still loading, which helps maintain a consistent height.
Mew `placeholderId` Used to track what was converted from that placeholder plugin.
New `insertMedia` Used for inserting the placeholder at once.
New `validateFiles` utils for validate the files meet the `mediaConfig`.
If validation fails,stop insert placeholder and save the error message in uploadErrorMessage.
NEw `option.multiple` `uploadMaxFileCount` Used to limit the number of placeholders inserted.
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
1 change: 1 addition & 0 deletions packages/ai/src/react/copilot/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
*/

export * from './callCompletionApi';
export * from './getNextWord';
export * from './triggerCopilotSuggestion';
export * from './withoutAbort';
19 changes: 18 additions & 1 deletion packages/dnd/src/DndPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
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';

Expand All @@ -13,6 +20,16 @@ export type DndConfig = PluginConfig<
enableScroller?: boolean;
isDragging?: boolean;
scrollerProps?: Partial<ScrollerProps>;
onDropFiles?: (
editor: PlateEditor,
props: {
id: string;
dragItem: FileDragItemNode;
monitor: DropTargetMonitor<DragItemNode, unknown>;
nodeRef: any;
dropPath?: Path;
}
) => void;
felixfeng33 marked this conversation as resolved.
Show resolved Hide resolved
}
>;

Expand Down
5 changes: 2 additions & 3 deletions packages/dnd/src/hooks/useDndNode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getEmptyImage } from 'react-dnd-html5-backend';
import { NativeTypes, getEmptyImage } from 'react-dnd-html5-backend';

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

Expand Down Expand Up @@ -48,7 +48,6 @@ export const useDndNode = ({
onDropHandler,
}: UseDndNodeOptions) => {
const editor = useEditorRef();

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

const [{ isDragging }, dragRef, preview] = useDragNode(editor, {
Expand All @@ -58,7 +57,7 @@ export const useDndNode = ({
});
const [{ isOver }, drop] = useDropNode(editor, {
id,
accept: 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;
46 changes: 29 additions & 17 deletions packages/media/src/lib/image/BaseImagePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
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 {}
export interface TImageElement extends TMediaElement {
initialHeight?: number;
initialWidth?: number;
}

export type ImageConfig = PluginConfig<
'img',
Expand Down Expand Up @@ -35,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
Loading