Open sonaimenext opened 3 years ago
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 : usman@alfain.tech
Love you.
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.
Hi all, I am working with react-quill and my image handler is not working below is my code --
import dynamic from "next/dynamic";
import "react-quill/dist/quill.snow.css";
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
const handleEditorImageUpload = async () => {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.click();
input.onchange = async () => {
const file = input.files[0];
const uploadedImage = await uploadImage(file);
if (uploadedImage) {
let quill = quillRef.current.getEditor();
const range = quill.getSelection();
quill.insertEmbed(range.index, "image", uploadedImage.url);
}
};
};
let toolbarOptions = [
[{ header: [1, 2, 3, 4, 5, 6] }, { font: [] }],
[{ list: "ordered" }, { list: "bullet" }],
["bold", "italic", "underline", "strike"],
["image"],
];
const modules = useMemo(
() => ({
toolbar: {
container: toolbarOptions,
handlers: { image: handleEditorImageUpload },
},
}),
[]
);
<ReactQuill
ref={quillRef}
value={body}
onChange={setBody}
modules={modules}
placeholder="Write your blog content here..."
/>
It is giving me an error of getEditor Error - TypeError: quillRef.current.getEditor is not a function
Any help would be much appreciated!
Ticket due diligence
v2.0.0-beta.1
ReactQuill version
FAQ
Is this a bug in Quill or ReactQuill?
Bugs
When I use imageHandler, ReactQuill's content show and disappeared immediately.
My code