반응형
.ql-editor blockquote {
border-left: 4px solid #ccc; /* 좌측 강조선 */
margin: 0;
padding: 10px 15px !important;
color: #555;
background-color: #f9f9f9;
font-style: italic;
font-size: medium;
}
.ql-syntax {
background-color: #f4f4f4 !important; /* 배경색 */
padding: 15px !important; /* 여백 */
font-family: "Courier New", Courier, monospace !important;
color: #555 !important;
font-size: small;
}
"use client";
import { useMemo } from "react";
import dynamic from "next/dynamic";
import ImageResize from "quill-image-resize";
import "react-quill/dist/quill.snow.css";
import hljs from "highlight.js";
import "highlight.js/styles/github.css";
hljs.configure({
languages: ["javascript", "ruby", "python", "java", "cpp", "kotlin", "sql"],
});
// ReactQuill을 동적으로 import하여 서버 측 렌더링 비활성화
const ReactQuill = dynamic(() => import("react-quill"), { ssr: false });
if (typeof window !== "undefined") {
const { Quill } = require("react-quill");
// 기존 이미지 포맷 가져오기
var BaseImageFormat = Quill.import("formats/image");
// 이미지 포맷 속성 정의
const ImageFormatAttributesList = [
"alt",
"height",
"width",
"style",
"align",
];
// 이미지 포맷 클래스 확장
class ImageFormat extends BaseImageFormat {
static formats(domNode: any) {
return ImageFormatAttributesList.reduce(function (
formats: any,
attribute
) {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
},
{});
}
format(name: string, value: any) {
if (ImageFormatAttributesList.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
}
// 확장된 이미지 포맷 등록
Quill.register(ImageFormat, true);
Quill.register("modules/imageResize", ImageResize);
}
export default function Editor({
value,
onChange,
}: {
value: string;
onChange: Function;
}) {
// ReactQuill 모듈과 포맷을 useMemo로 메모이제이션
const quillModules = useMemo(
() => ({
syntax: {
highlight: (text: any) => hljs.highlightAuto(text).value,
},
toolbar: [
[{ header: "1" }, { header: "2" }, { font: [] }],
[{ size: [] }],
["bold", "italic", "underline", "strike", "blockquote", "code-block"],
[
{ list: "ordered" },
{ list: "bullet" },
{ indent: "-1" },
{ indent: "+1" },
],
[{ color: [] }, { background: [] }],
["link", "image"],
["clean"],
],
imageResize: {},
clipboard: {
matchVisual: false, // 기본적으로 비주얼 데이터를 줄이는 옵션
},
}),
[]
);
const quillFormats = useMemo(
() => [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"strike",
"blockquote",
"list",
"bullet",
"indent",
"link",
"image",
"color",
"background",
"width",
"style",
"code-block",
],
[]
);
return (
<div className="flex pb-10 relative">
<ReactQuill
style={{ width: "100%", minHeight: 300, maxHeight: 700 }}
value={value}
onChange={(e: any) => onChange(e)}
modules={quillModules}
formats={quillFormats}
/>
</div>
);
}
반응형