コンテンツにスキップ

プレースホルダーシステム

プレースホルダーシステムにより、読者はドキュメント全体でIPアドレス、ASN、およびその他のデプロイ固有の値をカスタマイズできます。著者はMarkdownにトークンを記述し、ブラウザが実行時にそれらをユーザーが指定した値に置き換えます。

トークンは以下の正規表現パターンに従います:

x([A-Z][A-Z0-9_]+)x

トークンは小文字の x で始まり、小文字の x で終わり、大文字の識別子を含みます。例えば、xCUSTOMER_ASNxCUSTOMER_ASN プレースホルダーを参照します。

正規表現は src/scripts/placeholder-dom.ts で定義されています:

const PH_REGEX = /x([A-Z][A-Z0-9_]+)x/g;

すべてのプレースホルダーは src/data/placeholders.json で宣言されています。各エントリは以下の形式を持ちます:

{
"CUSTOMER_ASN": {
"type": "text",
"default": "64496",
"description": "Your public ASN (registered with ARIN/RIR)"
}
}
フィールド必須説明
typeはい自由入力の場合は "text"、選択メニューの場合は "dropdown"
defaultはい読者が変更する前に表示される初期値
descriptionはいフォームに表示されるラベル
optionsドロップダウンの場合のみ許可される値の配列

src/lib/placeholder-store.ts がすべてのプレースホルダーの状態を管理します。

値は localStoragef5xc-placeholders キーに永続化されます。ストアは4つの関数を公開しています:

関数目的
getDefaults()JSONからすべてのプレースホルダーキーとその default 値のマップを返します
loadValues()localStorageから読み取り、失敗した場合は getDefaults() にフォールバックします
saveValues(values)現在のマップをlocalStorageに書き込みます
clearValues()localStorageのエントリを削除します

FIELD_GROUPS はフォームUIのラベル付きセクションにプレースホルダーキーを整理します:

export const FIELD_GROUPS: FieldGroup[] = [
{ label: 'Data Center & Scrubbing Centers', keys: ['DC_NAME', 'CENTER_1', 'CENTER_2'] },
{ label: 'Protected Prefixes', keys: ['PROTECTED_CIDR_V4', 'PROTECTED_NET_V4', ...] },
{ label: 'BGP', keys: ['CUSTOMER_ASN', 'F5_XC_ASN', 'BGP_PASSWORD'] },
// ... その他のグループ
];

一部の値は直接入力されるのではなく、ユーザー入力から導出されます。getComputedValues() はルックアップテーブルからこれらを計算します:

const cidrToMask: Record<string, string> = {
'/24 (256 IPs)': '255.255.255.0',
'/23 (512 IPs)': '255.255.254.0',
// ...
};

2つの算出プレースホルダーが生成されます:

算出キー導出元
PROTECTED_MASK_V4cidrToMask ルックアップによる PROTECTED_CIDR_V4255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + cidrToShort による PROTECTED_CIDR_V4192.0.2.0/24

getAllValues() はユーザー入力値と算出値をマージし、置換用の完全なマップを提供します。

emitChange()document 上で placeholder-change CustomEventをディスパッチし、完全な値マップを detail として含みます:

export function emitChange(values: Record<string, string>) {
document.dispatchEvent(
new CustomEvent('placeholder-change', { detail: getAllValues(values) }),
);
}

このイベントは、DOMのspan更新とMermaidの再レンダリングの両方を駆動します。

src/components/PlaceholderForm.tsx が編集UIを提供します。

  • 状態: loadValues() から初期化された useState
  • マウント時: useEffectemitChange() を呼び出して初回のDOM置換をトリガーします
  • handleChange: Reactの状態を更新し、saveValues()emitChange() を呼び出します
  • handleReset: clearValues() を呼び出し、状態を getDefaults() にリセットし、変更を発行します
  • レンダリング: FIELD_GROUPS を反復し、グループごとに <fieldset> をレンダリングします。各キーは <input>(テキスト型)または <select>(ドロップダウン型)のいずれかを取得します
  • レイアウト: フォームは <details> 要素でラップされ、デフォルトでは折りたたまれています

src/components/PlaceholderFormWrapper.astro がReactコンポーネントをAstroページに接続します:

<PlaceholderForm client:only="react" />
<script>
import '../scripts/placeholder-dom.ts';
</script>

client:only="react" はAstroにコンポーネントを純粋にクライアント側でハイドレーションするよう指示します(SSRなし)。<script> タグはDOMウォーカーをインポートし、このラッパーを含むすべてのページで実行されるようにします。

ラッパーはフォームスタイリング用のグローバルCSS(.ph-form-wrapper.ph-grid.ph-value など)も挿入します。

src/scripts/placeholder-dom.ts がクライアント側のトークン置換を処理します。

ページ読み込み時に init() が実行されます:

  1. .sl-markdown-content をルートとして選択します(フォールバックとして document.body
  2. walkTextNodes(root, values) を呼び出し、NodeFilter.SHOW_TEXT を使用した document.createTreeWalker を使用します
  3. トークン正規表現に一致する各テキストノードについて、プレーンテキストノードと <span data-ph="KEY" class="ph-value"> 要素のドキュメントフラグメントに分割します
  4. 元のテキストノードをフラグメントで置き換えます

ウォーク後、DOMには生のトークンの代わりに data-ph 属性を持つspanが含まれます。

フォームが placeholder-change イベントを発行すると、updateSpans() が実行されます:

document.querySelectorAll<HTMLSpanElement>('span[data-ph]').forEach((span) => {
const name = span.getAttribute('data-ph')!;
if (values[name] !== undefined) {
span.textContent = values[name];
}
});

これによりツリーの再ウォークを回避し、spanのテキストコンテンツを直接更新します。

スクリプトは2つのリスナーを登録します:

イベントハンドラー目的
placeholder-changehandleChangespanを更新し、Mermaidダイアグラムを再レンダリングします
astro:page-loadinitAstroクライアント側ナビゲーション後にDOMを再ウォークします

手順:新しいプレースホルダーの追加

Section titled “手順:新しいプレースホルダーの追加”
  1. src/data/placeholders.jsonJSONエントリを追加します:

    "MY_NEW_VALUE": {
    "type": "text",
    "default": "example",
    "description": "Description shown in the form"
    }
  2. src/lib/placeholder-store.tsフィールドグループにキーを追加します。既存グループの keys 配列に追加するか、FIELD_GROUPS に新しいグループを作成します。

  3. コンテンツでトークンを使用します:任意の .mdx ファイルに xMY_NEW_VALUEx と記述します。DOMウォーカーが実行時にそれを置き換えます。

算出値は他のプレースホルダーから導出されます。追加するには:

  1. 必要に応じて src/lib/placeholder-store.tscidrToMask のパターンに従ってルックアップテーブルを追加します。

  2. getComputedValues() を拡張して新しい導出キーを含めます:

    export function getComputedValues(values: Record<string, string>): Record<string, string> {
    // ... 既存のロジック
    return {
    PROTECTED_MASK_V4: mask,
    PROTECTED_PREFIX_V4: `${net}${short}`,
    MY_COMPUTED: derivedValue, // ここに追加
    };
    }
  3. 他のトークンと同様に、コンテンツで xMY_COMPUTEDx を使用します。算出値は placeholders.json のエントリやフィールドグループは不要です — フォームからは見えません。