تخطَّ إلى المحتوى

نظام العناصر النائبة

يتيح نظام العناصر النائبة للقرّاء تخصيص عناوين IP وأرقام ASN والقيم الأخرى الخاصة بالنشر عبر الوثائق. يكتب المؤلفون رموزًا في ملفات Markdown؛ ويستبدلها المتصفح بالقيم التي يُدخلها المستخدم في وقت التشغيل.

تتبع الرموز نمط التعبير النمطي (regex) التالي:

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

يبدأ الرمز وينتهي بحرف x صغير ويحتوي على مُعرّف بأحرف كبيرة. على سبيل المثال، xCUSTOMER_ASNx يُشير إلى العنصر النائب CUSTOMER_ASN.

يتم تعريف التعبير النمطي في src/scripts/placeholder-dom.ts:

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

تعريفات العناصر النائبة

Section titled “تعريفات العناصر النائبة”

يتم الإعلان عن جميع العناصر النائبة في 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 إدارة جميع حالات العناصر النائبة.

يتم حفظ القيم في localStorage تحت المفتاح f5xc-placeholders. يُوفّر المخزن أربع دوال:

الدالةالغرض
getDefaults()تُرجع خريطة لكل مفتاح عنصر نائب مع قيمته الافتراضية (default) من ملف JSON
loadValues()تقرأ من localStorage، وتعود إلى getDefaults() كقيمة احتياطية
saveValues(values)تكتب الخريطة الحالية في localStorage
clearValues()تحذف إدخال localStorage

تُنظّم FIELD_GROUPS مفاتيح العناصر النائبة في أقسام مُسمّاة لواجهة النموذج:

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',
// ...
};

يتم إنتاج عنصرين نائبين محسوبين:

المفتاح المحسوبمُشتق منمثال
PROTECTED_MASK_V4PROTECTED_CIDR_V4 عبر جدول بحث cidrToMask255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + PROTECTED_CIDR_V4 عبر cidrToShort192.0.2.0/24

تدمج getAllValues() القيم المُدخلة من المستخدم مع القيم المحسوبة، مما يُوفّر خريطة كاملة للاستبدال.

تُرسل emitChange() حدث placeholder-change من نوع CustomEvent على document مع خريطة القيم الكاملة كـ detail:

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

يُحرّك هذا الحدث كلاً من تحديثات عناصر span في DOM وإعادة عرض مخططات Mermaid.

يُوفّر src/components/PlaceholderForm.tsx واجهة التحرير.

  • الحالة: useState مُهيّأة من loadValues()
  • عند التحميل: يستدعي useEffect دالة emitChange() لتفعيل الاستبدال الأولي في 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) التي تستخدم document.createTreeWalker مع NodeFilter.SHOW_TEXT
  3. لكل عقدة نصية تُطابق نمط الرمز، تُقسّمها إلى جزء مستند يحتوي على عُقد نصية عادية وعناصر <span data-ph="KEY" class="ph-value">
  4. تستبدل العقدة النصية الأصلية بالجزء

بعد المسح، يحتوي 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];
}
});

هذا يتجنب إعادة مسح الشجرة — فهو يُحدّث محتوى نص عنصر span مباشرة.

يُسجّل السكريبت مُستمعَين:

الحدثالمُعالجالغرض
placeholder-changehandleChangeتحديث عناصر span وإعادة عرض مخططات Mermaid
astro:page-loadinitإعادة مسح DOM بعد التنقل على جانب العميل في Astro

كيفية: إضافة عنصر نائب جديد

Section titled “كيفية: إضافة عنصر نائب جديد”
  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 في وقت التشغيل.

كيفية: إضافة قيمة محسوبة

Section titled “كيفية: إضافة قيمة محسوبة”

القيم المحسوبة مُشتقة من عناصر نائبة أخرى. لإضافة واحدة:

  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 أو مجموعة حقول — فهي غير مرئية في النموذج.