Slide 1

Slide 1 text

© 2025 Wantedly, Inc. 目次機能実装から理解するLexical Editor Wantedly Tech Night #9 2025-03-19 Kyuhwan Lee

Slide 2

Slide 2 text

© 2025 Wantedly, Inc. 自己紹介 李 圭煥 (Kyuhwan Lee) @wtdlee Wantedly, Inc. (2023/10~) Visit User Growth フロントエンドエンジニア

Slide 3

Slide 3 text

© 2025 Wantedly, Inc. 目次 ● Lexicalについて ● Lexicalの基本概念 ○ Editor ○ EditorState ○ Lexical Nodes ○ Commands ○ Editor Plugins ● 実際の実装:目次

Slide 4

Slide 4 text

© 2025 Wantedly, Inc. Lexicalについて

Slide 5

Slide 5 text

© 2025 Wantedly, Inc. Lexicalについて ● 安定性、アクセシビリティ、性能にフォーカスした拡張可能な JavaScript Webテキスト編集フレームワーク ○ WYSIWYG(What You See Is What You Get) エディターフレームワーク イメージ:https://lexical.dev/

Slide 6

Slide 6 text

© 2025 Wantedly, Inc. Lexicalについて ● 安定性 ○ 必要な機能をプラグインとして追加 ○ カスタムレンダリングエンジン ● アクセシビリティ ○ Screen Reader ○ ARIA属性管理 ● 性能 ○ カスタムレンダリングエンジン ○ JSON基盤の状態管理

Slide 7

Slide 7 text

© 2025 Wantedly, Inc. Lexicalの基本概念

Slide 8

Slide 8 text

© 2025 Wantedly, Inc. Lexicalの設計 ● Editor ○ Lexicalのエンジン ○ Lexicalの全体的な動作の処理やイベントを管理 ● EditorState ○ Editor内部の状態(JSON)が記録されているオブジェクト ○ editor.update()で変更可能

Slide 9

Slide 9 text

© 2025 Wantedly, Inc. Lexicalの設計 ● Lexical Nodes ○ LexicalのドキュメントはNode基盤 ○ 基本Node ■ TextNode ■ ParagraphNode ■ HeadingNode ■ ListNode, ListItemNode ○ カスタムNodeの追加でEditor内部の要素として利用可能

Slide 10

Slide 10 text

© 2025 Wantedly, Inc. Lexicalの設計 ● Commands ○ Editorの特定機能を実行する ○ editor.registerCommand()で登録、editor.dispatchCommand()で実行 ○ createCommandでカスタムコマンドも生成可能 User Action EditorState Command Registry 入力 Command実行 (Editorの状態変更) アップデート View

Slide 11

Slide 11 text

© 2025 Wantedly, Inc. Lexicalの設計 ● Editor Plugins ○ 特定機能を追加する際に利用可能 ○ ex) ■ RichTextPlugin:Bold, Italicなど基本的な書式 ■ HistoryPlugin:Undo/Redo ■ MarkdownShortcutPlugin:Markdown入力 ○ 目次実装では「TableOfContentsPlugin」を利用

Slide 12

Slide 12 text

© 2025 Wantedly, Inc. 実際の実装:目次 - Wantedly 開発の場合

Slide 13

Slide 13 text

© 2025 Wantedly, Inc. https://playground.lexical.dev/?showTableOfContents=true Lexical Playgroundでは固定箇所に表示されている が、ウォンテッドリーストーリーでは Nodeとして好きな 箇所に挿入できるようにしたい。 また、コンテンツに含まれていて詳細画面描画にも使 えるようになる必要があった。

Slide 14

Slide 14 text

© 2025 Wantedly, Inc. 各種機能の挿入ボタン。 Inserterと呼ばれている

Slide 15

Slide 15 text

© 2025 Wantedly, Inc. Inserterをクリックすると 目次が挿入できるボタンが表示

Slide 16

Slide 16 text

© 2025 Wantedly, Inc. 目次ボタンをクリックすると Lexical Custom Nodeが追加

Slide 17

Slide 17 text

© 2025 Wantedly, Inc. まず、クリックした際のイベント( Command)を登録 する必要がある

Slide 18

Slide 18 text

© 2025 Wantedly, Inc. Node.tsx ● Command 定義 ○ createCommandで識別できるToken(Key)を生成する ○

Slide 19

Slide 19 text

© 2025 Wantedly, Inc. TableofcontentsPlugin.tsx ● Plugin 定義 ○ 生成したTokenがどんな挙動を起こすのかを定義する このCommandは 目次Nodeを生成して 一番近い要素に追加する

Slide 20

Slide 20 text

© 2025 Wantedly, Inc. InserterPlugin.tsx ● Command 実行 ○ 目次ボタンをクリックした際に、定義した Commandが実行されるようにする

Slide 21

Slide 21 text

© 2025 Wantedly, Inc. Node.tsx ● Serialized Type 定義 ○ NodeはClass基盤だが、EditorStateがJSON形式なので NodeもSerializeできる必要がある

Slide 22

Slide 22 text

© 2025 Wantedly, Inc. Node.tsx ● Node 定義

Slide 23

Slide 23 text

© 2025 Wantedly, Inc. Editor.tsx ● Pluginを挿入