Discourse用のスケルトンレイアウトローダーの構築

こんにちは :waving_hand:

基本的なアイデア

目標は、ハードコードされたスケルトンテンプレートに依存するのではなく、実際の Discourse UI から生成されるスケルトンローダーを構築することでした。

ビルダーを使用すると、管理者がページ上の実際の要素を選択し、それらをスケルトン領域に変換できます。

例えば:

.title
.avatar
.topic-excerpt
.btn
.category-breadcrumb

コンポーネントはこれらのセレクタを使用して、実行時にスケルトンを生成します。

スケルトンのプレビュー

興味深い点は、管理者がセレクタを手動で書く必要がないことです。ビルダーは選択された要素を分析し、複数の候補セレクタを生成します。


有用なセレクタの生成は予想よりも難しかった

最初に直面した問題の一つがセレクタの生成でした。

素朴な実装では、以下のようなものが生成されることがあります:

.container.list-container.--topic-list .row.full-width .contents ...

技術的には有効ですが、再利用可能なスケルトン設定としてはあまりにも具体的すぎます。

アイコンの場合、生成されたセレクタに SVG 関連のクラスなどの実装詳細が含まれることもあり、さらに悪化します。

本当に欲しかったのは、以下のようなものです:

.badge-category__name

または:

.badge-category__wrapper .d-icon

DOM パス全体を記述するセレクタではなく、これに近いものです。

そのため、ビルダーは現在複数の候補を生成し、以下のような基準に基づいてスコアリングしています:

  • セレクタの深さ
  • クラスの数
  • 繰り返し一致
  • 状態に関連するクラス
  • 技術的な SVG/アイコンクラス
  • セレクタが選択された要素とまだ一致しているか

その結果、管理者が選択または手動で編集できる推奨セレクタのリストが得られます。


非表示の要素

スケルトンが表示されている間、単に消えるべき要素のための別のピッカーもあります。

例えば:

.alert.alert-info

これは、構築/テスト中に存在するがスケルトンのレイアウトに影響を与えてはならない、アナウンスメントバナーや一時的な通知などに役立ちました。

ここで興味深い問題は、要素を非表示にしても、空のスペースを残さないことです。

したがって、除外された要素は単なる display: none のリストとして扱われるだけでなく、ジオメトリの計算も、その要素が最終的なレイアウトの一部ではないことを理解する必要があります。

スケルトンのプレビュー


ナビゲーション

おそらく最大の課題はナビゲーションでした。

望ましい動作は以下の通りです:

click

  ↓

スケルトンを即座に表示

  ↓

Discourse がルートを変更

  ↓

目的地の DOM が出現

  ↓

スケルトンを非表示

魅力的な解決策は、ナビゲーションのライフサイクルに深くフックし、DOM が完全に安定するまで待つことでした。

しかし、それは誤ったアプローチでした。

ある時点で、スケルトンは実際のコンテンツがすでに存在しているにもかかわらず、数秒間表示され続けることがありました。

教訓はシンプルでした:

スケルトンは DOM の準備完了ゲートになってはならない。

目的地に引き継ぐのに十分な実際のコンテンツがある場合、スケルトンは退くべきです。

これにより、ナビゲーションの体感速度に大きな違いが出ました。


ビューポート

Discourse にはすでにレスポンシブなビューポートシステムがあるため、コンポーネントは同じブレークポイント抽象化を使用しています:

xs
sm
md
lg
xl
2xl

スケルトン設定はさらに以下のようにグループ化できます:

mobile → xs / sm
tablet → md
desktop → lg / xl / 2xl
all → everything

つまり、コンポーネントは実際のピクセル値を一切知る必要がありません。

Discourse がブレークポイントの値を変更した場合、スケルトンコンポーネントは新しいハードコードされた数値に合わせて書き換える必要はありません。


ジオメトリのキャッシュ

セレクタは何をレンダリングすべきかを示しますが、スケルトンの形状がどこに出現すべきかまでは示しません。

そのため、ジオメトリのキャプチャを追加しました。

ビルダーは実際のレンダリング領域を測定し、そのジオメトリを保存することで、SPA ナビゲーション中にローダーが即座に目的地のスケルトンをレンダリングできるようにします。

後続のアクセスで参照ジオメトリが継続的に変更されることを望まない場合のための、明示的なジオメトリロックオプションもあります。

これはもう一つの重要な区別でした:

セレクタ定義レンダリングされたジオメトリは、2つの異なるものです。


ドラフト

必要になったもう一つのものは、ドラフト状態でした。

このワークフローは望ましくありません:

ビルダーを開く

→ 10分かけて設定する

→ ビルダーを閉じる

→ すべて消える

そのため、ビルダーは進行中のドラフトを実際のテーマ設定とは別に保持します。

ドラフトはページ/ルート/ビューポートの組み合わせにスコープされるため、例えば:

topic-list / lg
topic-list / md
topic-list / xs

が互いに誤って上書きされることはありません。

ビルダーを閉じても作業は破棄されません。


元に戻す (Undo)

ビルダーがよりインタラクティブになるにつれ、Undo システムはほぼ必須になりました。

ビルダーは設定状態のスナップショットを保存します:

{
  "regions": \[\],
  "excludes": \[\]
}

DOM 操作の履歴を維持しようとするのではなく。

これにより、Undo システムは推論しやすくなり、実際のページ DOM から独立したまま保たれます。


このプロジェクトは現在も開発中です。hopefully すぐにテーマコンポーネントとして利用可能になる予定です! :slightly_smiling_face:

「いいね!」 3