diff --git a/packages/core/editor/src/components/Editor/Editor.tsx b/packages/core/editor/src/components/Editor/Editor.tsx
index 40dccba3a..6c943d4be 100644
--- a/packages/core/editor/src/components/Editor/Editor.tsx
+++ b/packages/core/editor/src/components/Editor/Editor.tsx
@@ -169,7 +169,6 @@ const Editor = ({
navigator.clipboard.write([clipboardItem]).then(() => {
const html = new DOMParser().parseFromString(htmlString, 'text/html');
- console.log('HTML copied\n', html.body);
});
if (HOTKEYS.isCut(event)) {
@@ -306,6 +305,42 @@ const Editor = ({
}
};
+ const handleClipboardContent = async (file: File) => {
+ const plugin = Object.keys(editor.plugins).find((plugin) => {
+ const pluginInstance = editor.plugins[plugin];
+ const mimeTypes = pluginInstance.clipboardPasteOrDropRules?.mimeTypes;
+ if (!mimeTypes) return false;
+ return mimeTypes.includes(file.type);
+ });
+
+ if (!plugin) return;
+
+ const pluginInstance = editor.plugins[plugin];
+ const block = await pluginInstance.clipboardPasteOrDropRules?.handler(file, editor);
+ if (!block) return;
+
+ const blockData = Blocks.buildBlockData({ type: pluginInstance.type, value: [block] });
+ Blocks.insertBlock(editor, pluginInstance.type, { focus: true, blockData });
+ };
+
+ const onPaste = (e: ClipboardEvent) => {
+ const items = e.clipboardData.items;
+ for (const item of items) {
+ const file = item.getAsFile();
+ if (!file) continue;
+ handleClipboardContent(file);
+ }
+ };
+
+ const onDrop = (e: DragEvent) => {
+ const items = e.dataTransfer?.files;
+ if (!items) return;
+ for (const file of items) {
+ if (!file) continue;
+ handleClipboardContent(file);
+ }
+ };
+
const editorStyles: CSSProperties = getEditorStyles({
...style,
userSelect: selectionBox.selection ? 'none' : 'auto',
@@ -323,6 +358,8 @@ const Editor = ({
onBlur={onBlur}
onCopy={onCopy}
onCut={onCopy}
+ onPaste={onPaste}
+ onDrop={onDrop}
>
{selectionBoxRoot !== false && (
diff --git a/packages/core/editor/src/plugins/createYooptaPlugin.tsx b/packages/core/editor/src/plugins/createYooptaPlugin.tsx
index ed926514f..c282121df 100644
--- a/packages/core/editor/src/plugins/createYooptaPlugin.tsx
+++ b/packages/core/editor/src/plugins/createYooptaPlugin.tsx
@@ -38,6 +38,7 @@ export class YooptaPlugin, TOpt
const extendedOptions = { ...this.plugin.options, ...options };
const elements = { ...this.plugin.elements };
+ const clipboardPasteOrDropRules = { ...this.plugin.clipboardPasteOrDropRules };
if (renders) {
Object.keys(renders).forEach((elementType) => {
@@ -84,6 +85,7 @@ export class YooptaPlugin, TOpt
...this.plugin,
elements: elements,
options: extendedOptions as PluginOptions,
+ clipboardPasteOrDropRules: clipboardPasteOrDropRules,
});
}
}
diff --git a/packages/core/editor/src/plugins/types.ts b/packages/core/editor/src/plugins/types.ts
index 54bf8523c..fee725cf2 100644
--- a/packages/core/editor/src/plugins/types.ts
+++ b/packages/core/editor/src/plugins/types.ts
@@ -64,6 +64,11 @@ export type EventHandlers = {
) => EditorEventHandlers[key] | void;
};
+export type PluginClipboardPasteOrDropRules = {
+ mimeTypes: string[];
+ handler: (file: File, editor: YooEditor) => Promise;
+};
+
export type PluginEventHandlerOptions = {
hotkeys: HOTKEYS_TYPE;
defaultBlock: YooptaBlockData;
@@ -89,6 +94,7 @@ export type Plugin, TPluginOpti
events?: PluginEvents;
options?: PluginOptions;
parsers?: Partial>;
+ clipboardPasteOrDropRules?: PluginClipboardPasteOrDropRules;
};
export type PluginParsers = {
diff --git a/packages/plugins/image/src/commands/index.ts b/packages/plugins/image/src/commands/index.ts
index 4b39abceb..166d9a9d2 100644
--- a/packages/plugins/image/src/commands/index.ts
+++ b/packages/plugins/image/src/commands/index.ts
@@ -11,14 +11,14 @@ type InsertImageOptions = ImageElementOptions & {
};
export type ImageCommands = {
- buildImageElements: (editor: YooEditor, options?: Partial) => ImageElement;
+ buildImageElements: (editor?: YooEditor, options?: Partial) => ImageElement;
insertImage: (editor: YooEditor, options?: Partial) => void;
deleteImage: (editor: YooEditor, blockId: string) => void;
updateImage: (editor: YooEditor, blockId: string, props: Partial) => void;
};
export const ImageCommands: ImageCommands = {
- buildImageElements: (editor: YooEditor, options = {}) => {
+ buildImageElements: (editor?: YooEditor, options = {}) => {
const imageProps = { ...options.props, nodeType: 'void' };
return { id: generateId(), type: 'image', children: [{ text: '' }], props: imageProps as ImageElementProps };
},
diff --git a/packages/plugins/image/src/plugin/index.tsx b/packages/plugins/image/src/plugin/index.tsx
index bdd1088cc..8a7257fc5 100644
--- a/packages/plugins/image/src/plugin/index.tsx
+++ b/packages/plugins/image/src/plugin/index.tsx
@@ -1,4 +1,4 @@
-import { generateId, SlateElement, YooptaPlugin } from '@yoopta/editor';
+import { generateId, SlateElement, YooptaPlugin, YooEditor } from '@yoopta/editor';
import { ImageCommands } from '../commands';
import { ImageElementMap, ImageElementProps, ImagePluginElements, ImagePluginOptions } from '../types';
import { ImageRender } from '../ui/Image';
@@ -37,6 +37,15 @@ const Image = new YooptaPlugin({
accept: 'image/png, image/jpeg, image/gif, image/webp',
maxSizes: { maxWidth: 650, maxHeight: 550 },
},
+ clipboardPasteOrDropRules: {
+ mimeTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
+ handler: async (file: File, editor: YooEditor) => {
+ const pluginOptions = editor.plugins.Image.options as ImagePluginOptions;
+ const data = await pluginOptions.onUpload(file);
+ const block = ImageCommands.buildImageElements(undefined, { props: data });
+ return block;
+ },
+ },
parsers: {
html: {
deserialize: {
diff --git a/packages/plugins/video/src/commands/index.ts b/packages/plugins/video/src/commands/index.ts
index 5849c9ab9..4a9a6079e 100644
--- a/packages/plugins/video/src/commands/index.ts
+++ b/packages/plugins/video/src/commands/index.ts
@@ -11,14 +11,14 @@ type InsertVideoOptions = VideoElementOptions & {
};
export type VideoCommands = {
- buildVideoElements: (editor: YooEditor, options?: Partial) => VideoElement;
+ buildVideoElements: (editor?: YooEditor, options?: Partial) => VideoElement;
insertVideo: (editor: YooEditor, options?: Partial) => void;
deleteVideo: (editor: YooEditor, blockId: string) => void;
updateVideo: (editor: YooEditor, blockId: string, props: Partial) => void;
};
export const VideoCommands: VideoCommands = {
- buildVideoElements: (editor: YooEditor, options = {}) => {
+ buildVideoElements: (editor?: YooEditor, options = {}) => {
const videoProps = { ...options.props, nodeType: 'void' };
return { id: generateId(), type: 'video', children: [{ text: '' }], props: videoProps as VideoElementProps };
},
diff --git a/packages/plugins/video/src/plugin/index.tsx b/packages/plugins/video/src/plugin/index.tsx
index 63505cacd..a3105e783 100644
--- a/packages/plugins/video/src/plugin/index.tsx
+++ b/packages/plugins/video/src/plugin/index.tsx
@@ -1,4 +1,4 @@
-import { generateId, YooptaPlugin } from '@yoopta/editor';
+import { generateId, YooptaPlugin, YooEditor } from '@yoopta/editor';
import { VideoCommands } from '../commands';
import { VideoElementMap, VideoPluginOptions } from '../types';
import { VideoRender } from '../ui/Video';
@@ -45,6 +45,15 @@ const Video = new YooptaPlugin({
description: 'Upload from device, insert from Youtube, Vimeo, DailyMotion, Loom, Wistia',
},
},
+ clipboardPasteOrDropRules: {
+ mimeTypes: ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime'],
+ handler: async (file: File, editor: YooEditor) => {
+ const pluginOptions = editor.plugins.Video.options as VideoPluginOptions;
+ const data = await pluginOptions.onUpload(file);
+ const block = VideoCommands.buildVideoElements(undefined, { props: data });
+ return block;
+ },
+ },
commands: VideoCommands,
parsers: {
html: {