All files / lib/components/QuillEditor index.tsx

0% Statements 0/38
0% Branches 0/17
0% Functions 0/9
0% Lines 0/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95                                                                                                                                                                                             
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
import { useQuill } from 'react-quilljs';
import React from 'react';
import { DeltaStatic } from 'quill';
import dynamic from 'next/dynamic';
import { usePostImageUploadMutation } from '@lib/api/mutations';
 
interface QuillEditorProps {
  value?: DeltaStatic;
  onChange: (delta?: DeltaStatic) => unknown;
  disabled?: boolean;
}
 
const modules = {
  toolbar: [
    ['bold', 'italic', 'underline', 'strike'],
    [{ header: [1, 2, 3, false] }],
    [{ align: '' }, { align: 'center' }, { align: 'right' }, { align: 'justify' }],
    [{ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' }],
    ['link', 'image'],
    ['clean']
  ]
};
 
function QuillEditor({ value, onChange, disabled }: QuillEditorProps) {
  const { quill, quillRef } = useQuill({ modules });
 
  const { mutateAsync } = usePostImageUploadMutation();
 
  React.useEffect(() => {
    if (!quill) return;
 
    if (value) {
      // Hack: setContents 의 인자에 delta operation 을 넣을 수 있습니다.
      // 참조: https://quilljs.com/docs/api/#setcontents
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      quill.setContents(value);
    }
 
    const insertToEditor = (url: string) => {
      const range = quill.getSelection();
      quill.insertEmbed(range?.index || 0, 'image', url);
    };
 
    const saveToServer = async (file: File) => {
      const res = await mutateAsync({ file });
      if (!res.data.url) return;
 
      insertToEditor(res.data.url);
    };
 
    const selectLocalImage = () => {
      const input = document.createElement('input');
      input.setAttribute('type', 'file');
      input.setAttribute('accept', 'image/*');
      input.click();
 
      input.onchange = () => {
        const file = input.files?.[0];
        saveToServer(file as File);
      };
    };
 
    function textChange() {
      if (!quill) return;
 
      const delta = quill.getContents();
 
      if (!delta || (delta.ops?.[0].insert === '\n' && quill.getLength() < 2)) {
        onChange(undefined);
      } else {
        onChange(delta);
      }
    }
 
    quill.on('text-change', textChange);
    quill.getModule('toolbar').addHandler('image', selectLocalImage);
 
    if (disabled) {
      quill.disable();
    }
 
    // eslint-disable-next-line consistent-return
    return () => {
      quill.off('text-change', textChange);
    };
  }, [quill]);
 
  return <div ref={quillRef} />;
}
 
export default dynamic(async () => QuillEditor, { ssr: false });