Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
39 changes: 38 additions & 1 deletion packages/core/editor/src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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',
Expand All @@ -323,6 +358,8 @@ const Editor = ({
onBlur={onBlur}
onCopy={onCopy}
onCut={onCopy}
onPaste={onPaste}
onDrop={onDrop}
>
<RenderBlocks editor={editor} marks={marks} placeholder={placeholder} />
{selectionBoxRoot !== false && (
Expand Down
2 changes: 2 additions & 0 deletions packages/core/editor/src/plugins/createYooptaPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class YooptaPlugin<TElementMap extends Record<string, SlateElement>, TOpt

const extendedOptions = { ...this.plugin.options, ...options };
const elements = { ...this.plugin.elements };
const clipboardPasteOrDropRules = { ...this.plugin.clipboardPasteOrDropRules };

if (renders) {
Object.keys(renders).forEach((elementType) => {
Expand Down Expand Up @@ -84,6 +85,7 @@ export class YooptaPlugin<TElementMap extends Record<string, SlateElement>, TOpt
...this.plugin,
elements: elements,
options: extendedOptions as PluginOptions<TOptions>,
clipboardPasteOrDropRules: clipboardPasteOrDropRules,
});
}
}
6 changes: 6 additions & 0 deletions packages/core/editor/src/plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ export type EventHandlers = {
) => EditorEventHandlers[key] | void;
};

export type PluginClipboardPasteOrDropRules = {
mimeTypes: string[];
handler: (file: File, editor: YooEditor) => Promise<SlateElement>;
};

export type PluginEventHandlerOptions = {
hotkeys: HOTKEYS_TYPE;
defaultBlock: YooptaBlockData;
Expand All @@ -89,6 +94,7 @@ export type Plugin<TElementMap extends Record<string, SlateElement>, TPluginOpti
events?: PluginEvents;
options?: PluginOptions<TPluginOptions>;
parsers?: Partial<Record<PluginParserTypes, PluginParsers>>;
clipboardPasteOrDropRules?: PluginClipboardPasteOrDropRules;
};

export type PluginParsers = {
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/image/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ type InsertImageOptions = ImageElementOptions & {
};

export type ImageCommands = {
buildImageElements: (editor: YooEditor, options?: Partial<ImageElementOptions>) => ImageElement;
buildImageElements: (editor?: YooEditor, options?: Partial<ImageElementOptions>) => ImageElement;
insertImage: (editor: YooEditor, options?: Partial<InsertImageOptions>) => void;
deleteImage: (editor: YooEditor, blockId: string) => void;
updateImage: (editor: YooEditor, blockId: string, props: Partial<ImageElementProps>) => 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 };
},
Expand Down
11 changes: 10 additions & 1 deletion packages/plugins/image/src/plugin/index.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -37,6 +37,15 @@ const Image = new YooptaPlugin<ImageElementMap, ImagePluginOptions>({
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: {
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/video/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ type InsertVideoOptions = VideoElementOptions & {
};

export type VideoCommands = {
buildVideoElements: (editor: YooEditor, options?: Partial<VideoElementOptions>) => VideoElement;
buildVideoElements: (editor?: YooEditor, options?: Partial<VideoElementOptions>) => VideoElement;
insertVideo: (editor: YooEditor, options?: Partial<InsertVideoOptions>) => void;
deleteVideo: (editor: YooEditor, blockId: string) => void;
updateVideo: (editor: YooEditor, blockId: string, props: Partial<VideoElementProps>) => 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 };
},
Expand Down
11 changes: 10 additions & 1 deletion packages/plugins/video/src/plugin/index.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -45,6 +45,15 @@ const Video = new YooptaPlugin<VideoElementMap, VideoPluginOptions>({
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: {
Expand Down