ข้ามไปยังเนื้อหา

ระบบ Placeholder

ระบบ placeholder ช่วยให้ผู้อ่านสามารถปรับแต่งที่อยู่ IP, ASN และค่าเฉพาะของการใช้งานต่างๆ ตลอดทั้งเอกสาร ผู้เขียนจะเขียนโทเค็นใน Markdown ของตน จากนั้นเบราว์เซอร์จะแทนที่ด้วยค่าที่ผู้ใช้กำหนดขณะรันไทม์

โทเค็นเป็นไปตามรูปแบบ regex:

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

โทเค็นเริ่มต้นและลงท้ายด้วยตัว x พิมพ์เล็ก และภายในเป็นตัวระบุตัวพิมพ์ใหญ่ ตัวอย่างเช่น xCUSTOMER_ASNx อ้างอิงถึง placeholder CUSTOMER_ASN

regex ถูกกำหนดไว้ใน src/scripts/placeholder-dom.ts:

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

Placeholder ทั้งหมดถูกประกาศไว้ใน src/data/placeholders.json แต่ละรายการมีรูปแบบดังนี้:

{
"CUSTOMER_ASN": {
"type": "text",
"default": "64496",
"description": "Your public ASN (registered with ARIN/RIR)"
}
}
ฟิลด์จำเป็นคำอธิบาย
typeใช่"text" สำหรับช่องกรอกข้อมูลอิสระ, "dropdown" สำหรับเมนูแบบเลือก
defaultใช่ค่าเริ่มต้นที่แสดงก่อนที่ผู้อ่านจะเปลี่ยนแปลงอะไร
descriptionใช่ป้ายกำกับที่แสดงในฟอร์ม
optionsเฉพาะ dropdownอาร์เรย์ของค่าที่อนุญาต

src/lib/placeholder-store.ts จัดการสถานะ placeholder ทั้งหมด

ค่าต่างๆ ถูกบันทึกใน localStorage ภายใต้คีย์ f5xc-placeholders store เปิดเผยฟังก์ชันสี่ตัว:

ฟังก์ชันวัตถุประสงค์
getDefaults()คืนค่า map ของคีย์ placeholder ทุกตัวไปยังค่า default จาก JSON
loadValues()อ่านจาก localStorage, ใช้ getDefaults() เป็นค่าสำรอง
saveValues(values)เขียน map ปัจจุบันลง localStorage
clearValues()ลบรายการ localStorage

FIELD_GROUPS จัดระเบียบคีย์ placeholder เป็นส่วนที่มีป้ายกำกับสำหรับ 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',
// ...
};

Placeholder ที่คำนวณได้มีสองตัว:

คีย์ที่คำนวณได้คำนวณจากตัวอย่าง
PROTECTED_MASK_V4PROTECTED_CIDR_V4 ผ่านตารางค้นหา cidrToMask255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + PROTECTED_CIDR_V4 ผ่าน cidrToShort192.0.2.0/24

getAllValues() รวมค่าที่ผู้ใช้กรอกกับค่าที่คำนวณได้ ให้ map ที่สมบูรณ์สำหรับการแทนที่

emitChange() ส่ง CustomEvent placeholder-change บน document พร้อมด้วย map ค่าทั้งหมดเป็น 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 สำหรับการแก้ไข

  • สถานะ: useState เริ่มต้นจาก loadValues()
  • เมื่อ mount: useEffect เรียก emitChange() เพื่อทริกเกอร์การแทนที่ DOM เริ่มต้น
  • handleChange: อัปเดตสถานะ React, เรียก saveValues() และ emitChange()
  • handleReset: เรียก clearValues(), รีเซ็ตสถานะเป็น getDefaults(), ส่งเหตุการณ์เปลี่ยนแปลง
  • การเรนเดอร์: วนซ้ำ FIELD_GROUPS, เรนเดอร์ <fieldset> ต่อกลุ่ม แต่ละคีย์จะได้ <input> (ประเภท text) หรือ <select> (ประเภท dropdown)
  • เลย์เอาต์: ฟอร์มถูกห่อด้วยอิลิเมนต์ <details> ซึ่งพับเก็บโดยค่าเริ่มต้น

src/components/PlaceholderFormWrapper.astro เชื่อมต่อคอมโพเนนต์ React กับหน้า Astro:

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

client:only="react" บอก Astro ให้ hydrate คอมโพเนนต์บนฝั่งไคลเอนต์เท่านั้น (ไม่มี SSR) แท็ก <script> นำเข้า DOM walker เพื่อให้มันทำงานในทุกหน้าที่รวม wrapper นี้

Wrapper ยังฉีด CSS ทั่วไปสำหรับการจัดรูปแบบฟอร์ม (.ph-form-wrapper, .ph-grid, .ph-value เป็นต้น)

src/scripts/placeholder-dom.ts จัดการการแทนที่โทเค็นฝั่งไคลเอนต์

เมื่อโหลดหน้า init() จะทำงาน:

  1. เลือก .sl-markdown-content เป็น root (ใช้ document.body เป็นค่าสำรอง)
  2. เรียก walkTextNodes(root, values) ซึ่งใช้ document.createTreeWalker กับ NodeFilter.SHOW_TEXT
  3. สำหรับแต่ละ text node ที่ตรงกับ regex ของโทเค็น จะแยกออกเป็น document fragment ของ text node ธรรมดาและอิลิเมนต์ <span data-ph="KEY" class="ph-value">
  4. แทนที่ text node เดิมด้วย fragment

หลังจากการเดิน DOM จะมี span ที่มีแอตทริบิวต์ data-ph แทนที่โทเค็นดิบ

เมื่อฟอร์มส่งเหตุการณ์ 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];
}
});

วิธีนี้หลีกเลี่ยงการเดิน tree ซ้ำ — มันอัปเดตเนื้อหาข้อความของ span โดยตรง

สคริปต์ลงทะเบียนตัวรับฟังสองตัว:

เหตุการณ์ตัวจัดการวัตถุประสงค์
placeholder-changehandleChangeอัปเดต span และเรนเดอร์ไดอะแกรม Mermaid ใหม่
astro:page-loadinitเดิน DOM ใหม่หลังจากการนำทางฝั่งไคลเอนต์ของ Astro
  1. เพิ่มรายการ JSON ใน src/data/placeholders.json:

    "MY_NEW_VALUE": {
    "type": "text",
    "default": "example",
    "description": "Description shown in the form"
    }
  2. เพิ่มคีย์ไปยังกลุ่มฟิลด์ ใน src/lib/placeholder-store.ts สามารถเพิ่มไปยังอาร์เรย์ keys ของกลุ่มที่มีอยู่ หรือสร้างกลุ่มใหม่ใน FIELD_GROUPS

  3. ใช้โทเค็นในเนื้อหา: เขียน xMY_NEW_VALUEx ในไฟล์ .mdx ใดก็ได้ DOM walker จะแทนที่มันขณะรันไทม์

ค่าที่คำนวณได้ถูกคำนวณจาก placeholder อื่นๆ ในการเพิ่ม:

  1. เพิ่มตารางค้นหา (หากจำเป็น) ใน src/lib/placeholder-store.ts ตามรูปแบบของ cidrToMask

  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 หรือกลุ่มฟิลด์ — มันจะมองไม่เห็นในฟอร์ม