انتقل إلى المحتوى
uiify

البنية

يتكوّن uiify من خمس طبقات، تُنشر كل منها مستقلة ولا تستورد أي طبقة أعلى منها. وتحكمها قاعدة واحدة: يجب أن يعمل المكوّن قبل الإماهة.

الفجوة قبل الإماهة

يترك SSR المتدفق فجوة بين أول رسم واكتمال الإماهة، ولكل حد متأخر تحت Suspense فجوته الخاصة. خلالها يظهر مكوّن الأقسام القابلة للطي المبني على useState دون أن يستجيب، بينما يعمل نظيره المبني على <details> منذ أول رسم.

تعرض صفحتان المكوّن نفسه دون أي وسم <script>: إحداهما مبنية على `<details>`، والأخرى على `useState`. افتحهما: تُفتح أقسام الأولى، ولا تُفتح أقسام الثانية. اعرض المصدر للتأكد.

الهدف هو ألا يعيد المكوّن العرض دون حاجة، وألا ينتشر تحديث الحالة إلى كل ما يشمله المزوّد، وألا تفرض JavaScript الخاصة بالمكتبة أي حساب متزامن للتخطيط؛ وليس إلغاء العرض نفسه، فلا بد من عرض الحالة في موضع ما. لا تُحمّل JavaScript افتراضيًا، وتُضاف بأقل حجم ممكن حين تتطلبها إمكانية الوصول فقط.

المستويات

يحدّد كل مكوّن مستواه في ملف بيان تقرؤه عملية البناء. ويحدّد هذا البيان ميزانية الحجم، وفحص نقاء RSC، ومجموعة Playwright، وشارة المستوى في صفحة كل مكوّن.

المستوىالميزانيةدون JavaScriptالمعنى
Tier 0
RSC — دون JavaScript على العميل
صفر كيلوبايتيجتاز مجموعة اختبارات تعطيل JavaScript كاملة.توجد الحالة في عنصر أصلي، أو لا توجد حالة أصلًا. يُحظر كل Hook في React، بما في ذلك useMemo وuseRef.
Tier 1
هيكل RSC + جزيرة صغيرة على العميل
كيلوبايت واحد كحد أقصىسلوك محدود موثّق لكل مكوّن.يظل العنصر الأصلي مسؤولًا عن الحالة. تزامن JavaScript سمات ARIA فقط، أو تضيف ما تفتقده المنصة، مثل استشعار نية التحويم أو الإغلاق التلقائي.
Tier 2
مكوّن على العميل — SSR قبل الإماهة
بحسب المكوّنيعرض بنية HTML مفيدة قبل الإماهة.نقطة دخول واحدة للعميل. يصدر الخادم بنية تعتمد على العناصر الأصلية متى أمكن؛ وينتظر التنقل بلوحة المفاتيح والترشيح ومزامنة ARIA اكتمال الإماهة.

تساعد المستويات أيضًا في الانتقال التدريجي: تُفعّل شروط التحقق لكل مكوّن على حدة، فتُطبّق قيود البنية مكوّنًا بعد آخر.

تدرّج الطبقات

@gunkin/uiify/hooks       SSR-safe React hooks — the substrate
@gunkin/uiify/core        headless behaviour: focus scope, dismiss layers, anchoring
@gunkin/uiify/elements    native-mechanism wrappers — zero client JavaScript
@gunkin/uiify/components  unstyled components, RSC by default
@gunkin/uiify/styles      optional CSS design layer

لا تستورد الطبقات الأدنى أي طبقة أعلى منها. وتخضع @gunkin/uiify/elements لأشد القيود: لا تستورد إلا @gunkin/uiify/core/render وأنواع React.

لماذا تُنشر العناصر كطبقة عامة

لا تستورد معظم التطبيقات @gunkin/uiify/elements مباشرة. وننشرها لسببين.

أولًا، كي يمكن التحقق من التصميم. حين تقول صفحة مكوّن إنه _مبني على elements/dialog_، تعرض صفحة ذلك العنصر بنية HTML الدقيقة وعقد data-* الذي يعتمد عليه.

ثانيًا، لإتاحة التركيب: إذا لم يناسب تقسيم المكوّن تصميمك، فابنِ تقسيمك الخاص بالاعتماد على التوصيلات نفسها، دون إعادة تنفيذ الآلية.

لا تنضم وحدة إلى الطبقة إلا إذا استوفت الشروط الخمسة: آلية أصلية واحدة، ودون سلوك JavaScript، ويستخدمها مكوّنان أو أكثر في @gunkin/uiify/components، وحالة يمكن قراءتها بمحدّد CSS، وتقليل تكرار بنية HTML بدلًا من إضافة وسائط غير مباشرة.

تُسمّى الوحدات بأسماء ميزات المنصة — details وdialog وpopover وinvoker — لا مفاهيم الواجهة. ولا يشترك مكوّن وعنصر في الاسم؛ لهذا يُبنى Modal على dialog. تصفّح العناصر

لماذا يُحظر سياق React في Tier 0

لا يفيد نقل الحالة إلى DOM إذا ظلت الأجزاء تتواصل عبر سياق React. لا تستطيع Server Components استخدام السياق؛ لذا لا يمكن عرض Root وItem يشتركان في مزوّد على الخادم، مهما كانت بنيتهما أصلية.

يجب أن تبقى التوصيلات سليمة بعد التسلسل؛ لذا تتصل الأجزاء عبر HTML: يجمعها <details name>، ويتيح commandfor وpopovertarget مراجع يحلّها المتصفح، وتُشتق المعرّفات من الخصائص. ولا يمكن لقيمة useId الوصول إلى الأجزاء الشقيقة دون سياق.

<Accordion id="faq">
  <Accordion.Item value="shipping">   {/* id="faq-shipping" */}

المقابل: يحتاج الجذر إلى id. وحين لا تكون هناك حاجة للتركيب، يستقبل المكوّن البيانات ويعرض شجرته بنفسه. انظر مكوّنات الخادم