Skip to content

외부 버튼 활용하기

예제 바로 확인하기

다음 명령어로 프로젝트를 로컬 개발 환경에서 실행하면 예제를 바로 확인할 수 있습니다.

URLDescriptionCommand
https://localhost:3032예제 데모 페이지yarn dev
https://localhost:3033/react-video-thumbnail-extractor/가이드 문서yarn docs
sh
yarn dev
sh
npm dev
sh
pnpm dev

React Quill 패키지 설치하기

다음 명령어를 사용해 React Quill 에디터 패키지를 설치하세요. 예제에서는 react-quill-new 패키지 3.3.3 버전 을 사용합니다. React Quill GitHub

가급적 React 18+를 지원하는 react-quill-new 패키지를 설치해 주세요.

sh
yarn add react-quill-new@3.3.3
sh
npm install react-quill-new@3.3.3
sh
pnpm add react-quill-new@3.3.3

예제 코드 소스

React Quill 패키지의 테마 스타일 파일과 컴포넌트 파일을 함께 import하는 것, 잊지 마세요!

  1. onSuccess 콜백 함수에서 받은 썸네일 목록에서 Base64 URL만 추출한 후에 에디터에 추가합니다.
tsx
// ...중략
const handleSuccess = (thumbnails: Thumbnail[]) => {
  // 썸네일 목록 배열에서 React Quill 에디터가 지원하는 Base64 URL만 추출
  const thumbnailUrls = thumbnails.map((thumbnail) => thumbnail.base64Url);

  handleInsertThumbnails(thumbnailUrls);
  closeModal();
};
  1. Base64 URL만 있는 배열로 <img> 태그를 만든 후, 에디터 Value를 설정합니다.
tsx
const handleInsertThumbnails = (thumbnailUrls: string[]) => {
  if (thumbnailUrls.length < 1) return;

  const imageTags = thumbnailUrls.map((url) => `<p><img src="${url}" /></p><p></p>`).join('');

  setEditorContent((prevContent) => prevContent + imageTags);
};

/react-quill/DemoBasic.tsx

tsx
import 'react-quill-new/dist/quill.snow.css';
import './style.css';
import '@devskyui/react-video-thumbnail-extractor/style.css';

import type {
  Thumbnail,
  ThumbnailValidationResult,
  VideoThumbnailExtractorMessagesType
} from '@devskyui/react-video-thumbnail-extractor';
import {
  VideoThumbnailExtractorMessages,
  VideoThumbnailExtractorModal
} from '@devskyui/react-video-thumbnail-extractor';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { AiOutlineVideoCameraAdd } from 'react-icons/ai';
import ReactQuill from 'react-quill-new';

const messages: VideoThumbnailExtractorMessagesType['ko'] = VideoThumbnailExtractorMessages.ko;

export const DemoBasic = () => {
  const [editorContent, setEditorContent] = useState('');
  const [isOpen, setIsOpen] = useState(false);
  const openModal = () => {
    setIsOpen(true);
  };
  const closeModal = () => {
    setIsOpen(false);
  };

  const handleAddedThumbnail = (thumbnail: Thumbnail) => {
    const time = thumbnail.displayTime;
    toast.success(`${time} ${messages.THUMBNAIL_ADD_SUCCESS}`);
  };

  const handleRemovedThumbnail = (thumbnail: Thumbnail) => {
    const time = thumbnail.displayTime;
    toast.success(`${time} ${messages.THUMBNAIL_REMOVE_SUCCESS}`);
  };

  const handleSuccess = (thumbnails: Thumbnail[]) => {
    const thumbnailUrls = thumbnails.map((thumbnail) => thumbnail.base64Url);

    handleInsertThumbnails(thumbnailUrls);
    closeModal();
  };

  const handleError = (result: ThumbnailValidationResult) => {
    const message = messages?.[result?.code];
    if (message) {
      toast.error(message);
    }
  };

  const handleInsertThumbnails = (thumbnailUrls: string[]) => {
    if (thumbnailUrls.length < 1) return;

    const imageTags = thumbnailUrls.map((url) => `<p><img src="${url}" /></p><p></p>`).join('');

    setEditorContent((prevContent) => prevContent + imageTags);
  };

  return (
    <>
      <ReactQuill
        value={editorContent}
        onChange={setEditorContent}
        placeholder="에디터 밖 버튼을 눌러 이미지를 삽입해 보세요."
      />
      <button
        onClick={() => openModal()}
        className="mt-2.5 flex items-center justify-center gap-1 rounded-full bg-primary px-4 py-2 text-primaryDark transition-colors duration-300 hover:bg-primaryDark hover:text-white"
      >
        <AiOutlineVideoCameraAdd />
        <span className="text-xs font-bold">동영상에서 썸네일 가져오기</span>
      </button>
      <VideoThumbnailExtractorModal
        isOpen={isOpen}
        maxFileSize={300}
        maxThumbnailLength={4}
        onThumbnailAdded={handleAddedThumbnail}
        onThumbnailRemoved={handleRemovedThumbnail}
        onError={handleError}
        onSuccess={handleSuccess}
        onCancel={() => closeModal()}
      />
    </>
  );
};