chore: Move editor into codebase (#2930)

This commit is contained in:
Tom Moor
2022-01-19 18:43:15 -08:00
committed by GitHub
parent 266f8c96c4
commit 062016b164
216 changed files with 12417 additions and 382 deletions

View File

@@ -0,0 +1,143 @@
import { NodeSelection } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import uploadPlaceholderPlugin, {
findPlaceholder,
} from "../lib/uploadPlaceholder";
import { ToastType } from "../types";
let uploadId = 0;
export type Options = {
dictionary: any;
replaceExisting?: boolean;
uploadImage: (file: File) => Promise<string>;
onImageUploadStart?: () => void;
onImageUploadStop?: () => void;
onShowToast?: (message: string, code: string) => void;
};
const insertFiles = function (
view: EditorView,
event: Event | React.ChangeEvent<HTMLInputElement>,
pos: number,
files: File[],
options: Options
): void {
// filter to only include image files
const images = files.filter((file) => /image/i.test(file.type));
if (images.length === 0) return;
const {
dictionary,
uploadImage,
onImageUploadStart,
onImageUploadStop,
onShowToast,
} = options;
if (!uploadImage) {
console.warn(
"uploadImage callback must be defined to handle image uploads."
);
return;
}
// okay, we have some dropped images and a handler lets stop this
// event going any further up the stack
event.preventDefault();
// let the user know we're starting to process the images
if (onImageUploadStart) onImageUploadStart();
const { schema } = view.state;
// we'll use this to track of how many images have succeeded or failed
let complete = 0;
// the user might have dropped multiple images at once, we need to loop
for (const file of images) {
const id = `upload-${uploadId++}`;
const { tr } = view.state;
// insert a placeholder at this position, or mark an existing image as being
// replaced
tr.setMeta(uploadPlaceholderPlugin, {
add: {
id,
file,
pos,
replaceExisting: options.replaceExisting,
},
});
view.dispatch(tr);
// start uploading the image file to the server. Using "then" syntax
// to allow all placeholders to be entered at once with the uploads
// happening in the background in parallel.
uploadImage(file)
.then((src) => {
// otherwise, insert it at the placeholder's position, and remove
// the placeholder itself
const newImg = new Image();
newImg.onload = () => {
const result = findPlaceholder(view.state, id);
// if the content around the placeholder has been deleted
// then forget about inserting this image
if (result === null) {
return;
}
const [from, to] = result;
view.dispatch(
view.state.tr
.replaceWith(from, to || from, schema.nodes.image.create({ src }))
.setMeta(uploadPlaceholderPlugin, { remove: { id } })
);
// If the users selection is still at the image then make sure to select
// the entire node once done. Otherwise, if the selection has moved
// elsewhere then we don't want to modify it
if (view.state.selection.from === from) {
view.dispatch(
view.state.tr.setSelection(
new NodeSelection(view.state.doc.resolve(from))
)
);
}
};
newImg.onerror = (error) => {
throw error;
};
newImg.src = src;
})
.catch((error) => {
console.error(error);
// cleanup the placeholder if there is a failure
const transaction = view.state.tr.setMeta(uploadPlaceholderPlugin, {
remove: { id },
});
view.dispatch(transaction);
// let the user know
if (onShowToast) {
onShowToast(dictionary.imageUploadError, ToastType.Error);
}
})
.finally(() => {
complete++;
// once everything is done, let the user know
if (complete === images.length && onImageUploadStop) {
onImageUploadStop();
}
});
}
};
export default insertFiles;