アップロード完了時の選択状態維持とDOM構造の簡略化
Lexxyのアップロード処理に複数の改善が加わった。ファイル名の欠落バグ修正、画像の不要なコンテナ要素の除去、アップロード完了後の選択状態の引き継ぎが主な変更点だ。
背景
これまでのアップロードフローには、段階的に発見された複数の問題があった。まずファイル名(fileName)がアップロード中ノードに保持されておらず、ファイル種別クラス名の生成に支障をきたしていた。次に、画像のDOM表現に不要な attachment__container DIVが挿入されており、CSSの設計を複雑にしていた。さらに、アップロード完了時に元のノードを置換すると選択状態が失われ、UXの一貫性が損なわれていた。
これらは独立した問題であるが、いずれもアップロード体験全体の品質に関わるため、本PRで一括して対処している。
技術的な変更
ファイル名の保持と選択状態の引き継ぎ
ActionTextAttachmentUploadNode のコンストラクタに this.fileName = file.name が追加され、ファイル名がノードに保持されるようになった。ファイル名はファイル種別の判定などに利用されるが、これまでアップロード中のノードでは欠落していた。
アップロード完了時の #showUploadedAttachment メソッドでは、ノード置換前に選択状態を確認し、置換後に引き継ぐロジックが追加された。
変更前:
#showUploadedAttachment(blob) {
this.editor.update(() => {
this.replace(this.#toActionTextAttachmentNodeWith(blob))
}, { tag: SILENT_UPDATE_TAGS })
}
変更後:
#showUploadedAttachment(blob) {
this.editor.update(() => {
const shouldTransferNodeSelection = this.isSelected()
const replacementNode = this.#toActionTextAttachmentNodeWith(blob)
this.replace(replacementNode)
if (shouldTransferNodeSelection) {
const nodeSelection = $createNodeSelectionWith(replacementNode)
$setSelection(nodeSelection)
}
}, { tag: SILENT_UPDATE_TAGS })
}
$createNodeSelectionWith は lexical_helper から、$setSelection は lexical パッケージからそれぞれインポートされ、同一の editor.update トランザクション内で選択状態の移譲が完結する。
画像コンテナ要素の除去
#createDOMForImage メソッドが簡略化され、img 要素を div.attachment__container で包む処理が削除された。
変更前:
#createDOMForImage(options = {}) {
const img = createElement("img", { src: this.src, draggable: false, alt: this.altText, ...this.#imageDimensions, ...options })
const container = createElement("div", { className: "attachment__container" })
container.appendChild(img)
return container
}
変更後:
#createDOMForImage(options = {}) {
const img = createElement("img", { src: this.src, draggable: false, alt: this.altText, ...this.#imageDimensions, ...options })
return img
}
DOM要素数が削減されることで、CSSのセレクタ設計が単純になる。
CSSのリファクタリング
lexxy-content.css では .attachment の display が table から block に変更された。従来は display: table を用いてキャプション幅を添付ファイルに合わせる手法が採られていたが、この依存が解消された。合わせて .attachment__caption も display: table-caption から display: block に変更され、caption-side: bottom が削除されている。
lexxy-editor.css では、アップロード中のプログレスバー表示がファイル種別ごとに分岐する形に整理された。通常のファイル(.attachment)では max-inline-size: 10ch で横幅を制限し、プレビュー表示(.attachment--preview)では position: absolute で画像上に重ねて表示する。さらに .attachment--file に対して削除ボタン(lexxy-node-delete-button)の位置指定が追加され、縦方向中央揃えが実現している。
設計判断
選択状態の移譲を同一トランザクション内で完結させた 点が、この変更の核心となる設計判断だ。replace 後に別の editor.update で選択状態を設定する方式も考えられるが、それでは2回のレンダリングが発生し、ちらつきや競合状態のリスクが生じる。SILENT_UPDATE_TAGS を付与した単一のトランザクション内で置換と選択移譲を行うことで、エディタの状態遷移がアトミックになっている。
display: table を用いたキャプション幅の制御は、CSSの古典的なトリックだが副作用も多い。display: block への移行はこの依存を断ち切り、コンテナDIVの除去とともにDOM・CSSの両面でシンプルな構造を目指す方向性を示している。
まとめ
アップロード完了時の選択状態引き継ぎ、コンテナDIVの除去、display: table からの脱却は、それぞれ独立した改善でありながら、いずれも「アップロード前後で体験の一貫性を保つ」という一つの方向性に沿っている。JavaScriptの状態管理とCSSの設計が同時に整理されたことで、今後のスタイル変更やノード操作の拡張が容易になる。