Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React quill in nextjs and typescript custom image handler issue? #849

Open
siamahnaf opened this issue Oct 29, 2022 · 5 comments
Open

React quill in nextjs and typescript custom image handler issue? #849

siamahnaf opened this issue Oct 29, 2022 · 5 comments

Comments

@siamahnaf
Copy link

I am try to use react-quill in my typescript nextjs project. Here I am finding typing and ref issue that I can't solve. Please help me. Here is code example-

import React, { useState, useRef } from 'react';
import dynamic from 'next/dynamic';
import { Container } from "@mui/material";
const ReactQuill = dynamic(import('react-quill'), {
    ssr: false,
    loading: () => <p>Loading ...</p>,
})
import 'react-quill/dist/quill.snow.css';


const Editor = () => {
    const [value, setValue] = useState('');
    const quillRef = useRef<any>();

    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();

        };
    }

    const modules = {
        toolbar: {
            container: [
                [{ font: [] }, { 'size': [] }, { 'header': [1, 2, 3, 4, 5, 6] }],
                ['bold', 'italic', 'underline', 'strike'],
                [{ 'color': [] }, { 'background': [] }],
                [{ 'script': 'sub' }, { 'script': 'super' }],
                [{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'],
                [
                    { list: 'ordered' },
                    { list: 'bullet' },
                    { indent: '-1' },
                    { indent: '+1' },
                ],
                [{ 'direction': 'rtl' }, { 'align': [] }],
                ['link', 'image', 'clean'],
            ],
            'handlers': {
                image: imageHandler
            }
        }
    }
    return (
        <Container maxWidth="xxxl" disableGutters>
            <ReactQuill
                ref={quillRef} // Here I am finding an issue.
                value={value}
                modules={modules}
                onChange={setValue}
                placeholder="Start typing!"
            />
        </Container>
    );
};

export default Editor;

Screenshot 2022-10-26 133941

I am try to use react-quill in my typescript nextjs project. Here I am finding typing and ref issue that I can't solve. Please help me.

Here is code example-

import React, { useState, useRef } from 'react';
import dynamic from 'next/dynamic';
import { Container } from "@mui/material";
const ReactQuill = dynamic(import('react-quill'), {
ssr: false,
loading: () =>

Loading ...

,
})
import 'react-quill/dist/quill.snow.css';

const Editor = () => {
const [value, setValue] = useState('');
const quillRef = useRef();

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();

    };
}

const modules = {
    toolbar: {
        container: [
            [{ font: [] }, { 'size': [] }, { 'header': [1, 2, 3, 4, 5, 6] }],
            ['bold', 'italic', 'underline', 'strike'],
            [{ 'color': [] }, { 'background': [] }],
            [{ 'script': 'sub' }, { 'script': 'super' }],
            [{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'],
            [
                { list: 'ordered' },
                { list: 'bullet' },
                { indent: '-1' },
                { indent: '+1' },
            ],
            [{ 'direction': 'rtl' }, { 'align': [] }],
            ['link', 'image', 'clean'],
        ],
        'handlers': {
            image: imageHandler
        }
    }
}
return (
    <Container maxWidth="xxxl" disableGutters>
        <ReactQuill
            ref={quillRef} // Here I am finding an issue.
            value={value}
            modules={modules}
            onChange={setValue}
            placeholder="Start typing!"
        />
    </Container>
);

};

export default Editor;

enter image description here

Here is CodeSandBox-

https://codesandbox.io/s/still-hill-xkb1pj

Can any one give me a proper typescript solutions.

@ruanhailiang
Copy link

ruanhailiang commented Nov 22, 2022

import { message } from 'antd';
import React, { useRef } from 'react';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import './TextEditor.less';
import { serverUrl } from '@/utils/constant';
import request from '@/utils/request';

const formats = [
  'header',
  'bold',
  'italic',
  'underline',
  'strike',
  'blockquote',
  'list',
  'bullet',
  'indent',
  'link',
  'image',
  'code',
  'color',
  'background',
  'code-block',
  'align',
];

interface OnChangeHandler {
  (e: any): void;
}

type Props = {
  value: string;
  placeholder: string;
  onChange: OnChangeHandler;
};

const TextEditor: React.FC<Props> = ({ value, onChange, placeholder }) => {
  const reactQuillRef: any = useRef(null);
  const imageHandler = async () => {
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.setAttribute('multiple', 'multiple');
    input.click();
    input.onchange = async () => {
      Array.from(input.files).forEach((item) => {
        const formData = new FormData();
        formData.append('file', item);
        formData.append('subjectId', '123');
        const hide = message.loading('uploading', 0);
        request(`${serverUrl}/training/picture/upload`, {
          method: 'post',
          headers: { 'Content-Type': 'multipart/form-data' },
          data: formData,
        }).then(({ data }) => {
          console.log('data', data);
          const quill = reactQuillRef?.current?.getEditor(); 
          const cursorPosition = quill.getSelection().index; 
          const link = data.path;
          console.log(quill.insertEmbed);
          alert(1);
          // max-width: 100%;
          quill.insertEmbed(cursorPosition, 'image', link); 
          quill.setSelection(cursorPosition + 1); 
          hide();
        });
      });
    };
  };

  const modules = React.useMemo(
    () => ({
      toolbar: {
        container: [
          // [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
          [{ header: [1, 2, 3, 4, 5, 6, false] }], 
          ['bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block'],
          [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
          ['link', 'image'], 
          [{ color: [] }, { background: [] }, { align: [] }],
          ['clean'],
        ],
        handlers: {
          image: imageHandler, 
        },
      },
    }),
    [],
  );

  return (
    <>
      <ReactQuill
        ref={reactQuillRef}
        theme="snow"
        value={value || ''}
        modules={modules}
        formats={formats}
        onChange={onChange}
        placeholder={placeholder}
      />
    </>
  );
};

export default TextEditor;

@eungwang1
Copy link

The ref type is not defined in quill 's props.
So I requested a pull request.

#873

@Immacio
Copy link

Immacio commented Apr 21, 2023

const ReactQuill = dynamic(
  async () => {
    const { default: RQ } = await import('react-quill');
    const { default: ImageUploader } = await import('./ImageUploader/ImageUploader');
    RQ.Quill.register('modules/imageUploader', ImageUploader);
    return RQ;
  },
  {
    ssr: false,
  },
);

<ReactQuill {...restProps} onChange={onChange} modules={modules} theme="snow" />;

Had to manually add the ImageUploader files and modules locally into my project folder

@Neiso
Copy link

Neiso commented Jun 15, 2023

Ugly workaround with nextjs but worked for me:

export default function Editor({ control, formValue }: EditorProps) {
  const [value, setValue] = useState<string>("");

  function imageHandler() {
    const input = document.createElement('input');
    input.setAttribute('type', 'file');
    input.setAttribute('accept', 'image/*');
    input.click();
    input.onchange = async () => {
      if (!input.files) return
      const cursorPosition = this.quill.selection.cursor.selection.lastRange.index;
// upload your file here
      const link = 'https://i.imgur.com/m8wOp65_d.webp?maxwidth=300&shape=thumb&fidelity=high';
      this.quill.editor.insertEmbed(cursorPosition, 'image', link);
    };
  };

  const modules = useMemo(
    () => ({
      toolbar: {
        container: TOOLBAR_OPTIONS,
        handlers: {
          image: imageHandler,
        },
      },
    }),
    [],
  );

  return (
    <Controller
      control={control}
      name={formValue}
      defaultValue={null}
      render={(
        {
          field: { onChange, },
          fieldState: { error },
        }
      ) => (
        <div>
          <ReactQuill
            className={error ? "!border-error" : ''}
            theme="snow"
            placeholder="Start writing..."
            modules={modules}
            value={value}
            onChange={(content: string) => {
              setValue(content);
              onChange(content)
            }}
          />
          {error && <p className="text-error">{error.message}</p>}
        </div>
      )}
    />
  );
}

@PedroMiotti
Copy link

I based my solution on @eungwang1 opened PR. Here it is:

const Editor = () => {
  const [value, setValue] = useState("<p style={{color:'#bdbdbd'}}>Start writing...</p>");

  const quillRef: React.LegacyRef<ReactQuill> = useRef(null);

  function handler() {
    console.log(value);
  } 

  const imageHandler = useCallback(() => {
    // Create an input element of type 'file'
    const input = document.createElement("input");
    input.setAttribute("type", "file");
    input.setAttribute("accept", "image/*");
    input.click();

    // When a file is selected
    input.onchange = () => {
      if (!input.files) return;

      const file = input.files[0];
      const reader = new FileReader();

      // Read the selected file as a data URL
      reader.onload = () => {
        const imageUrl = reader.result;
        if (!quillRef?.current) return;

        const quillEditor = quillRef.current.getEditor();

        // Get the current selection range and insert the image at that index
        const range = quillEditor.getSelection(true);
        quillEditor.insertEmbed(range.index, "image", imageUrl, "user");
      };

      reader.readAsDataURL(file);
    };
  }, []);

  const modules = useMemo(
    () => ({
      toolbar: {
        container: [
          [{ header: [2, 3, 4, false] }],
          ["bold", "italic", "underline", "blockquote"],
          [{ color: [] }],
          [
            { list: "ordered" },
            { list: "bullet" },
            { indent: "-1" },
            { indent: "+1" },
          ],
          ["link", "image"],
          ["clean"],
        ],
        handlers: {
          image: imageHandler,
        },
      },
      clipboard: {
        matchVisual: true,
      },
    }),
    [imageHandler]
  );

  const formats = [
    "header",
    "bold",
    "italic",
    "underline",
    "strike",
    "blockquote",
    "list",
    "bullet",
    "indent",
    "link",
    "image",
    "color",
    "clean",
  ];

  return (
    <Flex>
      <QuillEditor
        ref={quillRef}
        className={styles.editor}
        theme="bubble"
        value={value}
        defaultValue={"<p>Start writing...</p>"}
        formats={formats}
        modules={modules}
        onChange={(value, _, __, editor) => console.log(value, editor.getText())}
      />
    </Flex>
  );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants