저희는 노션과 같은 직관적이고 자연스러운 온라인 블록 기반 텍스트 에디터를 개발중입니다.
그 과정에서 블록 간의 수정 및 삭제 등 기능 개발을 위해 여러 블록을 드래그로 선택할 수 있어야 했습니다.
하지만 브라우저 기본 SelectionAPI 로는 블록으로 나누어진 컴포넌트 특성 상 여러 블록을 선택할 수 없는 문제가 발생했습니다
그래서 이 글을 통해 위 상황에서 겪은 문제를 해결해 나가는 과정을 공유하고자 합니다.
브라우저 기본 SelectionAPI는 하나의 요소에서만 작동합니다. 하지만 블록 기반 텍스트 에디터에서는 각 블록이 독립적인 컴포넌트로 나뉘어져 있어 selection이 끊기게 됩니다.
해결 방법을 찾던 중, 자체 제작된 에디터들은 브라우저 기본 selection 대신 가상 selection을 만들어 사용한다는 사실을 알게 되었고, 저희도 이를 구현하기로 했습니다.
가상 selection이란, 선택한 부분을 직접 화면에 실시간으로 표현하여 브라우저 기본 SelectionAPI와 유사하게 그려주는 방식입니다.
가상 selection을 구현하기 위해서는 마우스 커서의 위치와 시작지점, 끝점과 같은 상태를 저장해야하고, 이를 사용자가 알 수 있게 실시간으로 배경을 적용해 줄 수 있어야 합니다.
드래그 시작/종료 위치 추적
드래그 시작과 끝 지점을 추적하기 위해 selection 상태를 만들었습니다.
// offset 구하기
const cursorOffset = document.caretPositionFromPoint(event.clientX, event.clientY)?.offset as number;
const [selection, setSelection] = useState<ISelectionPosition>({
start: { blockIndex: 0, offset: cursorOffset },
end: { blockIndex: 0, offset: cursorOffset },
});
여기서 blockIndex는 몇 번째 블록인지 offset은 블록 내에서 커서의 위치를 의미합니다.
마우스 이벤트로 selection 추적
이 정보를 기반으로 어떤 블록이 선택되었는지를 판단합니다.
selection 방향 감지
드래그가 위 → 아래 방향인지, 아래 → 위 방향인지에 따라 색칠 방식이 달라집니다.
이를 기준으로 어느 블록이 시작, 중간, 끝 블록인지 판단합니다.