목차
1. 라이브러리 설치
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit @tiptap/extension-text-style
2. 폰트 사이즈 수정 라이브러리 만들기
위 라이브러리에는 font-size 수정하는 내장함수가 포함이 안 되어 있다.♧ Tiptab 라이브러리 자체가 필요한 것들을 따로 설치해서 사용해야 되는 거 같다.(리액트 같은 느낌)
직접 커스텀 해서 만들거나 npm install tiptap-extension-font-size 라이브러리를 설치해서 사용하여야 한다.
나는 필요시 수정도 할 수 있을 거 같아 스택오버플로우를 참고해 라이브러리를 따로 만들었다.
src/editor/lib/font-size.js
import { Extension } from '@tiptap/react';
export const FontSize = Extension.create({
name: 'fontSize',
addOptions() {
return {
types: ['textStyle'],
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
fontSize: {
default: null,
parseHTML: (element) =>
element.style.fontSize.replace(/['"]+/g, ''),
renderHTML: (attributes) => {
if (!attributes.fontSize) {
return {};
}
return {
style: `font-size: ${attributes.fontSize}`,
};
},
},
},
},
];
},
addCommands() {
return {
setFontSize:
(fontSize) =>
({ chain }) => {
return chain().setMark('textStyle', { fontSize: fontSize }).run();
},
unsetFontSize:
() =>
({ chain }) => {
return chain()
.setMark('textStyle', { fontSize: null })
.removeEmptyTextStyle()
.run();
},
};
},
});
커스텀 만드는 방법 : https://tiptap.dev/guide/custom-extensions/나도 위 사이트 보고 이해 안되서 다른 코드를 보고 참고했음😁
이미 만들어진 익스텐션들 : https://tiptap.dev/api/extensions
3. 라이브러리를 불러와 사용해보자
src/editor/index.jsx
import React, { useState, useCallback } from 'react';
import { useEditor, EditorContent } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import TextStyle from '@tiptap/extension-text-style';
import { FontSize } from './lib/font-size';
import Toolbar from './Toolbar.jsx';
import { StyledEditor } from './styledEditor.js';
export default function Editor() {
const [fontSize, setFontSize] = useState('16px');
const editor = useEditor({
extensions: [StarterKit, TextStyle, FontSize],
content: '',
});
const handleFontSizeChange = useCallback(
(event) => {
if (!editor) return null;
const button = event.target;
const fontSize = button.innerText;
setFontSize(`${fontSize}px`);
editor.chain().focus().setFontSize(`${fontSize}px`).run();
},
[editor]
);
return (
<StyledEditor>
<Toolbar editor={editor} onFontSizeChange={handleFontSizeChange} />
<EditorContent editor={editor} />
</StyledEditor>
);
}
src/editor/Toolbar.jsx
import React from 'react';
const fontSize = ['14', '16', '18', '24', '28', '30', '34', '38'];
export default function Toolbar({ editor, onFontSizeChange }) {
if (!editor) {
return null;
}
return (
<div className="toolbar">
{fontSize.map((value) => (
<button
key={value}
onClick={onFontSizeChange}
className={
editor.isActive('textStyle', { fontSize: `${value}px` })
? 'is-active'
: ''
}
>
{value}
</button>
))}
</div>
);
}
npm i @emotion/styled @emotion/react
에디터를 스타일링 하기위해 emotion을 택했다.처음엔 .css 파일로 작성하려 했는데 나중에 props로 state를 전달 받아 사용해야 될 거 같아 설치하게 되었다.
src/editor/styledEditor.js
import styled from '@emotion/styled';
export const StyledEditor = styled.div`
.toolbar {
margin-bottom: 10px;
display: flex;
button {
cursor: pointer;
background-color: #fff;
font-size: 16px;
border-radius: 5px;
margin: 0 1px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid black;
&:hover {
background-color: rgba(0, 0, 0, 0.07);
}
&.is-active {
background-color: #333;
color: #fff;
}
}
}
/* 에디터 */
.ProseMirror {
padding: 20px;
height: 400px;
border: 1px solid;
overflow-y: auto;
overflow-x: hidden;
&:focus {
outline: none;
}
p {
margin: 10px 0;
}
ol {
padding-left: 30px;
}
}
`;
https://stackblitz.com/edit/react-8fcue8?file=src/editor/styledEditor.js
위 주소로 들어가면 font-size 수정이 가능하다. 하지만 보통 에디터들처럼 버그? 투성이다.
- 커서 사이즈 버그(빈 에디터일 때 38px 폰트를 클릭해도 커서는 커지지 않고 그대로임)
- 실행 시 첫 폰트 사이즈를 따로 지정해 줘야 됨
- 텍스트를 쳤다가 빈 에디터로 만들면(계속 삭제) 폰트 사이즈가 초기화됨
4. 버그 수정
1. 커서 사이즈 버그 수정
src/editor/styledEditor.js
// 기존 스타일 코드
// ...
/* 에디터 */
.ProseMirror {
padding: 20px;
height: 400px;
border: 1px solid;
overflow-y: auto;
overflow-x: hidden;
&:focus {
outline: none;
}
p {
margin: 10px 0;
/* 텍스트 없을 때 커서 사이즈 유지 */
&:last-child:has(br) {
font-size: ${(props) => props.editorFontSize};
}
}
ol {
padding-left: 30px;
}
}
src/editor/index.jsx
// 기존 코드
// ...
return (
<StyledEditor editorFontSize={fontSize}>
<Toolbar editor={editor} onFontSizeChange={handleFontSizeChange} />
<EditorContent editor={editor} />
</StyledEditor>
);
✔️ 해결 방법
개발자 도구로 에디터 html을 까보면 빈 에디터일 때 <p><br></p>를 볼 수 있다.
&:has(br) 이렇게 하면 p 태그 중 br 태그만 포함 된 p 태그를 선택할 수 있다. 하지만 모든 br 상위인 p 태그를 선택하게 되면 나중에 새로운 버그를 만들어 낼 거 같아 &:last-child:has(br) 이렇게 마지막 br 상위인 p 태그를 선택하게 되었다.
마지막으로 fontSize를 props으로 받아와 font-size를 지정해 주었다.
2. 실행시 첫 폰트사이즈 지정
src/editor/index.jsx
// 기존 코드
// ...
const editor = useEditor({
extensions: [StarterKit, TextStyle, FontSize],
content: '',
onCreate({ editor }) {
editor.chain().focus().setFontSize(`${fontSize}`).run();
},
});
// 기존 코드
// ...
✔️ 해결 방법
https://tiptap.dev/api/events#create
위 주소에 보면 에디터의 이벤트에 대한 설명이 자세히 나와있다.
create (에디터가 생성되고 난 후) 이벤트를 활용해
editor.chain().focus().setFontSize(`${fontSize}`).run(); (폰트 사이즈를 결정짓는 함수)를 사용하였다.
3. 텍스트를 쳤다가 빈 에디터로 만들면(계속 삭제 시킴) 폰트 사이즈가 초기화는 현상 수정
src/editor/index.jsx
// 기존 코드
// ...
const editor = useEditor({
extensions: [StarterKit, TextStyle, FontSize],
content: '',
onCreate({ editor }) {
editor.chain().focus().setFontSize(fontSize).run();
},
onUpdate({ editor }) {
if (editor.isEmpty) {
const currentFontSize = editor.getAttributes('textStyle').fontSize;
const newFontSize = currentFontSize || fontSize;
editor.chain().focus().setFontSize(newFontSize).run();
setFontSize(newFontSize);
}
},
});
// 기존 코드
// ...
✔️ 해결 방법
https://tiptap.dev/api/events#update
마찬가지로 위 주소에 보면 에디터의 이벤트에 대한 설명이 자세히 나와있다.
update(내용이 바뀌었을 때) 이벤트를 이용해 내용 바뀌는 것을 감지한다.
if (editor.isEmpty) 이 부분은 에디터가 빈 값일 대 true를 반환해준다.
(에디터가 빈 값일 때만 실행해 주면 되기 때문 아래 함수 호출을 최소화할 수 있다.)
const currentFontSize = editor.getAttributes('textStyle').fontSize; <- 현재 fontSize를 반환해준다.
const newFontSize = currentFontSize || fontSize;
currentFontSize는 빈 텍스트일 때 null을 반환해서 or 연산자를 이용해 null일 경우 state 값인 fontSize를 지정해 주게 한다.
editor.chain().focus().setFontSize(newFontSize).run();
setFontSize(newFontSize);
현재 폰트 사이즈를 newFontSize 값을 넣음으로 써 최신 값이 유지가 된다.
마지막으로 fontSize는 css에서 커서 사이즈를 조절하기 때문 setFontSize을 써서 fontSize를 최신화해주었다.
완성 코드
https://stackblitz.com/edit/react-1bqplt?file=src/editor/index.jsx
반응형
'프레임워크 > React' 카테고리의 다른 글
React의 state와 props (0) | 2023.10.08 |
---|---|
react-router-dom v6 사용법 (0) | 2023.10.03 |
react-quill Custom (0) | 2023.04.17 |
커스텀 훅(Custom Hook) (0) | 2023.04.08 |
useImperativeHandle (0) | 2023.04.07 |