Skip to Content

Editor

Lexical 기반 리치 텍스트 에디터 컴포넌트


개요

Foundation Editor는 Lexical 엔진을 기반으로 한 리치 텍스트 에디터입니다. 서식, 이미지, 표, 레이아웃 등 풍부한 콘텐츠 편집 기능을 제공하며, 플러그인 아키텍처를 통해 기능을 선택적으로 구성할 수 있습니다.

주요 특징

  • Toolbar: 서식, 정렬, 링크, 삽입 등 편집 도구
  • 서식 선택 메뉴: Paragraph, Heading, List, Code Block, Quote
  • Insert 메뉴: 이미지, 표, 컬럼 레이아웃, 임베드
  • 서식 적용: 글꼴, 크기, 굵기, 기울임, 밑줄, 색상 등
  • 텍스트 정렬: 좌/중/우/양쪽 정렬
  • 읽기 전용: readOnly 모드 지원
  • 플러그인 아키텍처: 서식/콘텐츠 제한 가능
  • HTML 입/출력: HTML 문자열로 초기화(editorHtmlState) 및 변경 콜백(onHtmlChange) 지원
  • 명령적 API: ref(EditorHandle)로 외부에서 텍스트 삽입·포커스 제어
  • 접근성: 키보드 네비게이션 지원

사용 예시

기본 에디터

500

0 characters

|

0 words

읽기 전용

readOnly 설정 시 Toolbar와 Actions가 비활성화되고 콘텐츠 수정이 제한됩니다.

500

0 characters

|

0 words

오류 상태

error 설정 시 테두리가 destructive 색상으로 변경됩니다.

500

0 characters

|

0 words

Serialized State 관리

onSerializedChange로 EditorState를 JSON 구조로 관리합니다.

import { Editor } from "@vortex/ui-foundation" import { useState } from "react" function MyEditor() { const [state, setState] = useState(undefined) return ( <Editor editorSerializedState={state} onSerializedChange={setState} /> ) }

HTML 입/출력 (editorHtmlState / onHtmlChange)

JSON 외에 HTML 문자열로도 양방향 입/출력이 가능합니다. 초기화는 editorHtmlState prop으로 마운트 시점에 1회 적용되고 ($generateNodesFromDOM으로 노드 트리 변환), 변경 통보는 onHtmlChange 콜백으로 매 업데이트마다 lexical → HTML 문자열($generateHtmlFromNodes)이 전달됩니다.

onHtmlChange를 명시하지 않으면 변환을 수행하지 않으므로 추가 비용이 없습니다. 마운트 시 적용 우선순위는 editorState > editorSerializedState > editorHtmlState 입니다. 외부에서 다시 로드하려면 key 변경으로 컴포넌트를 강제 remount 해야 합니다.

import { Editor } from "@vortex/ui-foundation" import { useState } from "react" function MyEditor() { const [html, setHtml] = useState("") return ( <Editor editorHtmlState='<p>안녕 <strong>World</strong></p>' onHtmlChange={setHtml} /> // → html === '<p class="...">안녕 <strong>World</strong></p>' // 이 값을 그대로 contentsBody로 API에 전송 가능 ) }

Ref Handle - 외부에서 텍스트 삽입

useRef<EditorHandle>(null)로 ref를 만들어 <Editor ref={...} />에 연결하면 외부에서 현재 커서(또는 선택 영역) 위치에 텍스트를 삽입할 수 있습니다. 내부적으로 lexical의 RangeSelection.insertText를 사용하므로 deprecated된 document.execCommand("insertText") 에 의존하지 않습니다.

  • 선택 영역이 있으면 그 영역을 대체합니다.
  • 선택 영역이 없으면 root 마지막 element에 append합니다(빈 root이면 paragraph를 새로 만들어 삽입).
  • readOnly 상태에서는 호출이 무시됩니다.

SMS / LMS / MMS 등 다른 채널의 textarea 기반 치환 문자 삽입과 동일한 UX를 Lexical contenteditable 환경에서도 제공할 수 있습니다.

import { Editor, type EditorHandle } from "@vortex/ui-foundation" import { useRef } from "react" function MessageEditor() { const editorRef = useRef<EditorHandle>(null) const fields = ["회원명", "날짜", "주문번호"] as const return ( <> {fields.map((field) => ( <button key={field} onClick={() => editorRef.current?.insertText(`$$${field}$$`)} > + {field} </button> ))} <Editor ref={editorRef} /> </> ) }

API Reference

Props

PropTypeDefaultDescription
readOnlybooleanfalse읽기 전용 여부
errorboolean | string-에러 상태
classNamestring-추가 CSS 클래스
editorStateEditorState-Lexical EditorState (제어)
editorSerializedStateSerializedEditorState-JSON 직렬화 상태 (제어)
editorHtmlStatestring-마운트 시 HTML 문자열로 초기화 (LexicalComposer 마운트 시점에 1회 적용)

초기화 우선순위: editorState > editorSerializedState > editorHtmlState

Events

EventTypeDescription
onChange(editorState: EditorState) => voidEditorState 변경 핸들러
onSerializedChange(state: SerializedEditorState) => voidJSON 직렬화 변경 핸들러
onHtmlChange(html: string) => voidHTML 문자열 변경 핸들러 (명시 시에만 변환)

Ref Handle (EditorHandle)

forwardRef로 받은 ref를 통해 명령적으로 에디터를 제어합니다.

MethodTypeDescription
insertText(text: string) => void현재 커서/선택 영역 위치에 텍스트 삽입. 선택 영역이 있으면 대체, 없으면 root 마지막에 append. readOnly 시 무시
focus() => void에디터에 포커스
getEditor() => LexicalEditor | null내부 LexicalEditor 인스턴스에 직접 접근 (고급 사용처용)

기본 사용법

import { Editor } from "@vortex/ui-foundation" // 기본 <Editor /> // 읽기 전용 <Editor readOnly /> // 에러 상태 <Editor error />

접근성

키보드 네비게이션

  • Tab: Toolbar → 에디터 영역 이동
  • Tab / Shift+Tab: 에디터 내 들여쓰기/내어쓰기
  • / (슬래시): 컴포넌트 피커 메뉴 호출
  • Arrow Keys: 캘린더, 드롭다운 내 탐색

권장 사항

  • ✅ 폼 필드에서 사용 시 iCignal Editor 권장 (label, error, required 지원)
  • ✅ focus-within 링으로 포커스 상태 시각적 구분
  • ❌ label 없이 단독 사용 지양

관련 컴포넌트

Last updated on