Skip to content

Commit

Permalink
feat(text editor): block pasted inline images
Browse files Browse the repository at this point in the history
  • Loading branch information
john-traas authored and civing committed Jun 5, 2024
1 parent 5bcd0b5 commit 28d562c
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Plugin, PluginKey } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Slice, Fragment, Node } from 'prosemirror-model';

export const pluginKey = new PluginKey('imageRemoverPlugin');

export const createImageRemoverPlugin = () => {
return new Plugin({
key: pluginKey,
props: {
handlePaste: (view, event, slice) => {
return processPasteEvent(view, event, slice);
},
},
});
};

/**
* Check if a given ProseMirror node or fragment contains any image nodes.
* @param node - The ProseMirror node or fragment to check.
* @returns A boolean indicating whether the node contains any image nodes.
*/
const isImageNode = (node: Node | Fragment): boolean => {
if (node instanceof Node) {
if (node.type.name === 'image') {
return true;
}

let found = false;
node.content.forEach((child) => {
if (isImageNode(child)) {
found = true;
}
});

return found;
} else if (node instanceof Fragment) {
let found = false;
node.forEach((child) => {
if (isImageNode(child)) {
found = true;
}
});

return found;
}

return false;
};

/**
* Filter out image nodes from a ProseMirror fragment.
* @param fragment - The ProseMirror fragment to filter.
* @returns A new fragment with image nodes removed.
*/
const filterImageNodes = (fragment: Fragment): Fragment => {
const filteredChildren: Node[] = [];

fragment.forEach((child) => {
if (!isImageNode(child)) {
if (child.content.size > 0) {
const filteredContent = filterImageNodes(child.content);
const newNode = child.copy(filteredContent);
filteredChildren.push(newNode);
} else {
filteredChildren.push(child);
}
}
});

return Fragment.fromArray(filteredChildren);
};

const processPasteEvent = (
view: EditorView,
event: ClipboardEvent,
slice: Slice,
): boolean => {
const clipboardData = event.clipboardData;
if (!clipboardData) {
return false;
}

const filteredSlice = new Slice(
filterImageNodes(slice.content),
slice.openStart,
slice.openEnd,
);

if (filteredSlice.content.childCount < slice.content.childCount) {
const { state, dispatch } = view;
const tr = state.tr.replaceSelection(filteredSlice);
dispatch(tr);

return true;
}

return false;
};
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,14 @@ const processPasteEvent = (

const text = clipboardData.getData('text/plain');

if (!isValidUrl(text)) {
return false;
}
// Process as a link if the text is a valid URL
if (isValidUrl(text)) {
pasteAsLink(view, text);

pasteAsLink(view, text);
return true;
}

return true;
return false;
};

const pasteAsLink = (view: EditorView, href: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
EditorLinkMenuEventDetail,
createLinkPlugin,
} from './plugins/link-plugin';
import { createImageRemoverPlugin } from './plugins/image-remover-plugin';

/**
* The ProseMirror adapter offers a rich text editing experience with markdown support.
Expand Down Expand Up @@ -265,6 +266,7 @@ export class ProsemirrorAdapter {
keymap(this.menuCommandFactory.buildKeymap()),
this.createMenuStateTrackingPlugin(this.actionBarItems),
createLinkPlugin(this.handleNewLinkSelection),
createImageRemoverPlugin(),
],
});
}
Expand Down

0 comments on commit 28d562c

Please sign in to comment.