similiquedicta
Version:
A Plugin Architecture on top of Draft.JS
112 lines (94 loc) • 4.11 kB
text/typescript
import { DraftHandleValue, EditorState, SelectionState } from 'draft-js';
import { PluginFunctions } from '@draft-js-plugins/editor';
import {
DndUploadPluginConfig,
PlaceholderBlockType,
FileToUpload
} from '.';
import modifyBlockData from './modifiers/modifyBlockData';
import removeBlock from './modifiers/removeBlock';
import { readFiles } from './utils/file';
import { insertPlaceholder } from './components/insertPlaceholder';
let placeholderBlocksList:Array<PlaceholderBlockType> = [];
export default function onDropFile(config: DndUploadPluginConfig) {
return function onDropFileInner(
selection: SelectionState,
files: Blob[],
{ getEditorState, setEditorState }: PluginFunctions
): DraftHandleValue {
// Get upload function from config.
const { handleUpload } = config;
if (handleUpload) {
const formData = new FormData();
const data: { files: File[]; formData: FormData } = { files: [], formData };
for (const file of files) {
// eslint-disable-line no-restricted-syntax
if (file && file instanceof File) {
data.formData.append('files', file);
data.files.push(file);
}
}
setEditorState(EditorState.acceptSelection(getEditorState(), selection));
// Read files on client side
readFiles(data.files).then((placeholders) => {
// Add upload placeholders for each image before uploading
let _editorState = getEditorState();
placeholderBlocksList = [];
placeholders.forEach((placeholder) => {
const customBlockProps = insertPlaceholder(_editorState, placeholder.name);
const { state, key, text, blockKey } = customBlockProps;
placeholderBlocksList.push({key, text, blockKey});
_editorState = state;
});
setEditorState(_editorState);
handleUpload(data,
// TODO: what does retainSrc do? Is it useful to keep this option.
(uploadedFiles:Array<FileToUpload> /*, { retainSrc }*/):void => {
/* Success! */
/*
* TODO:
* This removes all placeholders and inserts all uploaded images,
* Since fail() function removes placeholders for the files that have been marked as failing by the fail()
* function, those placeholders can be safely removed here, but perhaps it would be useful
* to modify `placeholderBlocksList` at some point on the fail() function.
*/
let editorState = getEditorState();
placeholderBlocksList.forEach(element => {
editorState = removeBlock(editorState, element.blockKey);
});
setEditorState(editorState);
if(uploadedFiles) {
uploadedFiles.forEach((file) => {
if (config.addImage) {
editorState = config.addImage(editorState, file.src);
}
});
setEditorState(editorState);
}
} , (file: FileToUpload):void => {
/* On fail */
// Failed to upload a given file.
// Remove placeholder for the file.
// Any other special handling of this use case should be done manually by the user.
let editorState = getEditorState();
const blockInList = placeholderBlocksList.find(p => p.text === file.name);
if(blockInList !== undefined) {
editorState = removeBlock(editorState, blockInList.blockKey);
setEditorState(editorState);
}
}, (percent:number, file:FileToUpload):void => {
/* On progress */
let newEditorState = getEditorState();
const block = placeholderBlocksList.find(p => p.text === file.name);
if(block !== undefined) {
const blockData = { name: file.name, progress: `${percent}%`};
newEditorState = modifyBlockData(newEditorState, block.key, blockData);
setEditorState(newEditorState);
}
});
});
return 'handled';
}
return 'not-handled';
}
}