react-quill
react-quill copied to clipboard
Custom image handler
Ticket due diligence
- [ ] I have verified that the issue persists under ReactQuill
v2.0.0-beta.1
ReactQuill version
- [x ] v2.0.0-beta.1
FAQ
Is this a bug in Quill or ReactQuill?
Bugs
When I use imageHandler, ReactQuill's content show and disappeared immediately.
My code
const imageHandler = async () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
var file: any = input && input.files ? input.files[0] : null;
var formData = new FormData();
formData.append("file", file);
let quillObj = quillRef.current.getEditor();
await UploadService.uploadFile(formData)
.then((res) => {
let data = get(res, "data.data.url");
const range = quillObj.getEditorSelection();
quillObj.getEditor().insertEmbed(range.index, 'image', data);
})
.catch((err) => {
message.error("This is an error message");
return false;
});
};
}
const modules = {
toolbar: {
container: [
[{ 'header': [1,2,3,4,5,6,false] }, { 'font': [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{ 'list': 'ordered' }, { 'list': 'bullet' },
{ 'indent': '-1' }, { 'indent': '+1' }],
['link', 'image', 'video'],
['clean'],
[{ 'align': [] }],
],
'handlers': {
image: imageHandler
}
},
clipboard: {
matchVisual: false,
},
imageResize: {
parchment: Quill.import('parchment'),
modules: ['Resize', 'DisplaySize']
}
}
<ReactQuill
ref={quillRef}
value={val}
onChange={handleChange}
modules={modules}
formats={formats}
// tag="textarea"
theme="snow"
style={{
height: '500px'
}}
/>
Thank You π±βπβ€
thanks for the code! it helped me a lot.
Notes!
in your image handler, I wrote that code in mine and when did some console logs, I saw that there were no methods like getEditorSelection() and getEditor()
later I found that instead of these methods, there are two others that exist and do the same job:
for getting range/selection:
const range = quillObj.getSelection();
for inserting an embed:
quillObj.editor.insertEmbed(range.index, 'image', data);
after this the imageHandler
const imageHandler = async () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
var file: any = input && input.files ? input.files[0] : null;
var formData = new FormData();
formData.append("file", file);
let quillObj = quillRef.current.getEditor();
await UploadService.uploadFile(formData)
.then((res) => {
let data = get(res, "data.data.url");
const range = quillObj.getSelection();
quillObj.editor.insertEmbed(range.index, 'image', data);
})
.catch((err) => {
message.error("This is an error message");
return false;
});
};
}
When I add
'handlers': {
image: imageHandler
}
I get the warning: addRange(): The given range isn't in document.
Any help would be cool.
if anyone still having an error accessing the Editor's getSelection function, here's the solution I have found. useRef does not work in dynamic import in Next.js. here's the solution:
const QuillNoSSRWrapper = dynamic(
async () => {
const { default: RQ } = await import('react-quill');
// eslint-disable-next-line react/display-name
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{ ssr: false }
);
handler
const quillImageCallback = async () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
const file = input.files ? input.files[0] : null;
let data = null;
const formData = new FormData();
const quillObj = quillRef?.current?.getEditor();
const range = quillObj?.getSelection();
if (file) {
formData.append('file', file);
formData.append('resource_type', 'raw');
const responseUpload = await fetch(
`${process.env.NEXT_PUBLIC_IMAGE_UPLOAD}/upload`,
{ method: 'POST', body: formData }
);
data = await responseUpload.json();
if (data.error) {
console.error(data.error);
}
quillObj.editor.insertEmbed(range.index, 'image', data?.secure_url);
}
};
};
const imageHandler = async () => { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); input.click(); input.onchange = async () => { var file: any = input && input.files ? input.files[0] : null; var formData = new FormData(); formData.append("file", file); let quillObj = quillRef.current.getEditor(); await UploadService.uploadFile(formData) .then((res) => { let data = get(res, "data.data.url"); const range = quillObj.getEditorSelection(); quillObj.getEditor().insertEmbed(range.index, 'image', data); }) .catch((err) => { message.error("This is an error message"); return false; }); }; }
can you write all the code please where is modules how can we use this thing
Hello, I've been trying to upload image with react-quill & nextJS. Actually, I try a lot just what you do, but not working. if you let me know what is the problem, I will very thanks to you.
const QuillWrapper = ({ theme, id, value, onChange, readonly }) => { const ref = useRef(); const quillRef = useRef(); const quill = quillRef.current; const [uploadLoading, setUploadLoading] = useState(false); const imageHandler = useCallback(() => { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('multiple', 'multiple'); input.setAttribute('accept', 'image/*'); input.click();
input.onchange = async () => {
const files = input.files;
const fileDatas = [];
const fileFormData = new FormData();
let checkEmptyFormData = true;
[].forEach.call(files, (f) => {
if (f.type.indexOf('image') < 0) {
NotificationManager.warning('μ΄λ―Έμ§ νμΌλ§ λ±λ‘ κ°λ₯ν©λλ€.', '', 2000);
setUploadLoading(false);
return;
} else if (f.size > 19999999) {
NotificationManager.warning('20MB μ΄νμ νμΌλ§ λ±λ‘ κ°λ₯ν©λλ€.', '', 2000);
setUploadLoading(false);
return;
} else {
fileFormData.append('file', f);
fileFormData.append('type', 'image');
checkEmptyFormData = false;
}
});
if (!checkEmptyFormData) {
try{
await uploadFileMulti(fileFormData)
.then((result) => {
result?.files?.map((v) => {
fileDatas.push(v);
fileDatas.map((file) => {
const range = quillRef.current?.getEditor().getSelection();
let quill = quillRef.current?.getEditor();
if (range.index !== null && range.index !== undefined) {
quill.editor.insertEmbed(range.index, 'image', `${process.env.NEXT_PUBLIC_BACKURL}${file?.file_path}`)
quill.setSelection(range.index + 1);
}
})
});
})
} catch(error) {
console.error(error)
}
}
};
}, [quillRef]);
let toolbarOptions = [[{ size: ['small', false, 'large', 'huge'] }], ['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }], ['image']];
const modules = useMemo(() => ({ toolbar: { container: toolbarOptions, handlers: { image: imageHandler } }, }), []);
const formats = ['header', 'font', 'size', 'bold', 'italic', 'underline', 'strike', 'align', 'blockquote', 'list', 'bullet', 'indent', 'background', 'color', 'link', 'width'];
useEffect(() => { ref.current.setAttribute('spellcheck', false); }, []); };
I ran into the exact issue and was able to fix this by doing:
const quillRef = useRef(null)
useEffect(() => {
// @ts-ignore
quillRef.current
.getEditor()
.getModule('toolbar')
.addHandler('image', () => {
const input = document.createElement('input')
input.setAttribute('type', 'file')
input.setAttribute('accept', 'image/*')
input.click()
input.onchange = async () => {
if (!input.files || !input?.files?.length || !input?.files?.[0])
return
// @ts-ignore
const editor = quillRef?.current?.getEditor()
const file = input.files[0]
// Do what you want with the file
const range = editor.getSelection(true)
editor.insertEmbed(
range.index,
'image',
'<image-url>'
)
}
})
}, [quillRef])
return (
<ReactQuill
ref={quillRef}
...
Hello Coding Guru's! I got stuck in this from the past week. Get me out of it Please.
i wanna impliment custom image handling in react-quill. my whole toolbar just got disappeared after a small change in the modules handlers.
Hope you Got the solution..........................
@sonaimenext
@mubashirjamali101
@mdbaniani
@brodwen83
@codigoisaac
@me
Sharing is caring. Mail : [email protected]
Love you.
BOOOOOOOM
Got the solution, keep the modules and formats out of the component mean after imports , register the quill image uploader then place the modules and formats. below is my code also, `import React from "react"; import ReactQuill, { Quill } from "react-quill"; import "react-quill/dist/quill.snow.css";
// #1 import quill-image-uploader import ImageUploader from "quill-image-uploader";
import "quill-image-uploader/dist/quill.imageUploader.min.css";
Quill.register("modules/imageUploader", ImageUploader);
const modules = { toolbar: { container: [ ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }], ["blockquote", "code-block"], [{ image: "customImageHandler" }], ["link"], [{ color: [] }, { background: [] }], [{ indent: "-1" }, { indent: "+1" }], [{ align: [] }], [{ header: [1, 2, 3, 4, 5, 6, false] }], ["clean"], ], }, imageUploader: { upload: (file) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve( "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/JavaScript-logo.png/480px-JavaScript-logo.png" ); }, 3500); }); }, }, };
const formats = [ "header", "bold", "italic", "underline", "strike", "blockquote", "list", "bullet", "indent", "link", "image", "imageBlot", ];
function RichTextFeild({ options, setOptions, showQuestion }) { return ( <> <ReactQuill theme="snow" modules={modules} formats={formats} value={options} onChange={setOptions} /> </> ); }
export default RichTextFeild; `
you can write image uploader according to your needs and wants. buddy, hurrah. ππ
BOOOOOOOM
Got the solution, keep the modules and formats out of the component mean after imports , register the quill image uploader then place the modules and formats. below is my code also, `import React from "react"; import ReactQuill, { Quill } from "react-quill"; import "react-quill/dist/quill.snow.css";
// #1 import quill-image-uploader import ImageUploader from "quill-image-uploader";
import "quill-image-uploader/dist/quill.imageUploader.min.css";
Quill.register("modules/imageUploader", ImageUploader);
const modules = { toolbar: { container: [ ["bold", "italic", "underline", "strike"], [{ list: "ordered" }, { list: "bullet" }], ["blockquote", "code-block"], [{ image: "customImageHandler" }], ["link"], [{ color: [] }, { background: [] }], [{ indent: "-1" }, { indent: "+1" }], [{ align: [] }], [{ header: [1, 2, 3, 4, 5, 6, false] }], ["clean"], ], }, imageUploader: { upload: (file) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve( "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/JavaScript-logo.png/480px-JavaScript-logo.png" ); }, 3500); }); }, }, };
const formats = [ "header", "bold", "italic", "underline", "strike", "blockquote", "list", "bullet", "indent", "link", "image", "imageBlot", ];
function RichTextFeild({ options, setOptions, showQuestion }) { return ( <> </> ); }
export default RichTextFeild; `
you can write image uploader according to your needs and wants. buddy, hurrah. ππ
Nice solution. But doesn't work for typescript
Note: THIS IS A TRICK NOT A COMPLETE SOLUTION, THIS DOES NOT USE IMAGE HANDLER, BASED SOLELY ON TEXT VALUE PROVIDED BY THE EDITOR INSTANCE.
Hi all,
pretty late to the discussion. I wanted to process the images my way too. So I didn't do it the perfect way, the registering a custom-handler way. Instead, what I did was, I got the text (HTML) from the editor, did a bit of text processing and got the image tag with Base64 as its source, got that source pop-out from it and made a blob from it (whole thing in vanilla javascript).
That's how I got it done. Tell me if anyone wants more detail on that.
MY CASE WAS DIFFERENT: In my case, client didn't want to let the image remain at it's place, instead they wanted to pop it out, save it separately and show it to the end-user separately.
Hi @sonaimenext , I see that you have a ImageResize module, could you share with me about that part of code, I'm working with resize module and all I found is I have to install another 3rd-party package. And those aren't written by the author of quill or react-quill
When I add
'handlers': { image: imageHandler }I get the warning:
addRange(): The given range isn't in document.Any help would be cool.
Wrap your imageHandler function in an useCallback.