هناك سؤال تجيب عنه كل مكتبة واجهة في الخفاء، عادة دون أن تخبرك: كم من الـ markup يخصني؟
عندما يطلق أحد مكتبة واجهة، يقرر نيابة عنك أين يقع الحد بين السلوك والمظهر. السلوك هو ما لا تراه: مفاتيح الأسهم، حلقات التركيز التي تتحرك في الوقت المناسب، سمات ARIA التي تقرأها قارئات الشاشة، آلة الحالة الصغيرة التي تقرر متى يُعد العنصر مفتوحاً. المظهر هو كل شيء آخر.
بعض المكتبات تلصق الاثنين. المكوّن يملك الضغطات والسهم معاً. تحصل على أكورديون جميل جاهز، بافتراضات معقولة، وهذه القصة كاملة حتى تريد سهماً مختلفاً. ثم تقرأ المصدر. ثم تفرع. ثم تكتب override في CSS ربما يعمل.
مكتبات أخرى تفصلهما. السلوك في حزمة headless. المظهر في كودك، أو في طبقة تنسيق ألحقها أحد لاحقاً. يمكنك أي سهم تريد. ويمكنك أيضاً كتابة مئة سطر HTML وARIA قبل أن يظهر شيء على الشاشة. كلا النهجين صادق. وكلاهما يقرر نيابة عنك كم من الـ markup لك، وهذا القرار صعب التراجع عنه. أول مرة يتغيّر التصميم، تكتشف أي خيار اشتريت فعلاً.
بنيت Corex لأنني أردت أن يبقى هذا الخيار مفتوحاً. ليس بدّل المكتبة مفتوحاً. عدّل هذا الملف مفتوحاً. هذا ما تعنيه التشريح في هذه المكتبة، وهي الكلمة الوحيدة التي أريد تعريفها قبل أن نكمل.
خيار ثالث، عن قصد
كل مكوّن Corex تفاعلي هو داخلياً شيئان. آلة حالة صغيرة، مكتوبة مرة، تعرف كيف يجب أن يتصرّف المكوّن. ومجموعة أجزاء مسماة، بعلامات data-scope وdata-part، تتصل بها الآلة. الآلة لا تنظر إلى HTML المحيط. لا تهتم بغلافاتك أو classes أو فقراتك الإضافية. تحتاج فقط أن تكون أجزاؤها موجودة، بالأسماء الصحيحة.
هذا الفصل هو سبب هذا المنشور بالكامل. يعطيك، كمؤلف الصفحة، مجالاً للتفاوض. يمكنك أن تترك Corex يصدّر الـ markup، كما تفعل معظم المكتبات، ولا تنظر إلى HTML الذي يولّده. أو تكتب الـ markup يدوياً، ونفس الآلة ما زالت تجد أجزاءها وتشغّل نفس السلوك فوقها.
اخترت كلمة التشريح عن قصد. التشريح هو مما يُبنى به الشيء، لا ما يفعله. سلوك الأكورديون واحد بغض النظر عن كم HEEx قررت كتابته اليوم. التشريح هو الشكل حول ذلك السلوك، وهو لك أن تشكّله.
من أين جاءت الفكرة
التشريح ليس شيئاً اخترعته لـ Phoenix. يأتي من نسخة Corex الثابتة vanilla على corex-ui.com. في جانب vanilla لا يوجد مكوّن Phoenix تستدعيه. تكتب HTML يدوياً. كل data-scope، كل data-part، كل غلاف، كل تسمية، كل سمة تبحث عنها الآلة. ثم توصّل hook JavaScript، وتتولى الآمة.
هذا الإعداد يعمل، وهو الأساس الذي يقوم عليه كل شيء آخر. وهو أيضاً قاسٍ. نسيت سمة واحدة فلا يفتح الأكورديون. أخطأت في قيمة فيقفز التركيز لمكان غريب. المكتبة لا تستطيع مساعدتك، لأن كل ما تراه هو HTML الذي كتبته.
Phoenix Corex هو نفس النموذج ملفوفاً بطبقة مكوّن. الافتراضات تصدر هيكلاً صحيحاً. تحصل دائماً على scopes وparts وسمات صحيحة، حتى في صفحة لم تضبط شيئاً. السلم الذي سأصفه طريقة للخروج من تلك الافتراضات، قطعة بقطعة، دون أن تفقد ضمان أن الـ markup الذي تحتاجه الآلة ما زال موجوداً تحته.
يمكنك اعتبار vanilla Corex مسؤولية كاملة من السطر الأول: كل markup لك. Phoenix Corex يبدأ والـ markup مكتوب لك بشكل صحيح، ويسمحك باستعادته، قطعة بقطعة.
السلم، بلغة بسيطة
التشريح يأخذ عادة خمسة أشكال مألوفة. تصطف في سلم. لا أعتبرها ميزات. أعتبرها اعترافات صادقة عن الشاشة التي أبنيها، بالترتيب الذي أصل إليه عادة.
في الطرف الأخف، تثق بالمكوّن أن يقرر كل شيء. تعطيه قائمة عناوين وأجسام وتنصرف. المكوّن يرسم المحفّزات واللوحات والمؤشرات والشكل كاملاً. هذه الدرجة التي ألجأ إليها أولاً في كل مرة. تبدو كاستدعاء دالة واحدة في قالب، لأنها كذلك.
<.accordion id="faq" items={@topics} />
عندما يطلب التصميم تفصيلاً صغيراً متكرراً لا يتضمنه الافتراض، تحتفظ بالقائمة وتطلب من المكوّن تفويض قطعة واحدة من الـ markup إليك. المكوّن ما زال يملك البنية؛ أنت تساهم بنفس القطعة الصغيرة في كل صف. أصغر تصعيد ممكن، وفي أغلب الأحيان يكفي.
<.accordion id="faq" items={@topics}>
<:indicator>
<.heroicon name="hero-chevron-right" />
</:indicator>
</.accordion>
عندما يصبح التفصيل المتكرر لكل صف، تحتفظ بالقائمة وتبدأ كتابة الـ slot بنفسك، مع وصول لبيانات الصف. عمود الفقرات ما زال القائمة. الزينة لك.
<.accordion id="faq" items={@topics}>
<:trigger :let={item}>
<.heroicon name={item.meta.icon} />
{item.label}
</:trigger>
</.accordion>
عندما تتوقف العناصر عن الشعور كقائمة، تتوقف عن استخدام واحدة. تعلن مجموعة ثابتة من اللوحات يدوياً، تسميها بقيم مشتركة، والمكوّن يخيط المحفّزات والأجسام. هذه الدرجة عندما أحصي الصفوف على يد وأن كل لوحة عالم صغير بصورتها وفقرتها ودعوتها للعمل.
<.accordion id="faq" value="shipping">
<:trigger value="shipping">Shipping</:trigger>
<:content value="shipping">
<p>We ship within <strong>3 business days</strong>.</p>
</:content>
</.accordion>
وعندما يصبح ذلك جامداً جداً، عندما تحتاج غلافات وتداخل وكتلة تسويق بين عنصرين، تخلع القفازات وتؤلف المكوّن من مكوّناته الفرعية. نفس الآلة، markup لك، من الغلاف الخارجي إلى الداخل.
<.accordion :let={ctx} compound id="faq">
<.accordion_root ctx={ctx}>
<.accordion_item value="shipping">
<.accordion_trigger>Shipping</.accordion_trigger>
<.accordion_content>
<p>We ship within 3 business days.</p>
</.accordion_content>
</.accordion_item>
</.accordion_root>
</.accordion>
أريد أن أكون صادقاً عن الثمن. كل خطوة في السلم تعني HEEx أكثر في ملفك، ومسؤولية أكبر عن الـ markup. لا أتسلق للمتعة. أتسلق عندما البديل هو قتال التجريد، وأتوقف عندما تطابق الدرجة التصميم.
ما لا يتغيّر
ما أجده متحرراً، وسبب أنني أبني بهذه الطريقة، أن لا شيء من هذه القرارات قرار وقت تشغيل.
المستخدم لا يرى الدرجة التي اخترتها. مفاتيح الأسهم تتحرك بنفس الطريقة. ترتيب Tab واحد. العنصر الصحيح يُعلَن لقارئ الشاشة في اللحظة الصحيحة. الأكورديون هو الأكورديون. عمل الآلة غير مرئي من الخارج ومتماثل من الداخل.
اختيار الدرجة محادثة بينك وبين نفسك المستقبلية فقط. ليست محادثة عن المنتج. محادثة عن كم HEEx تريد صيانته في هذا الملف، لهذا التصميم الذي قد يغيّره المصمم في السبرنت القادم.
المحادثة تصبح أسهل كلما أجريتها. بعد أشهر توقفت عن السؤال أي درجة صحيحة وبدأت أسأل أي درجة صادقة عن الشاشة أمامي. تقريباً دائماً الإجابة الصادقة هي الأصغر. أحياناً لا، وهذا مقبول. المكتبة لا تعاقبك على أي خيار.
عندما لا يكون السلم الاستعارة المناسبة
التشريح عن شكل أول render. يصف HTML الموجود قبل أن ينقر أحد. ليس جواباً لكل سؤال سيواجهه المكوّن.
إن كان الخادم يجب أن يكون مصدر الحقيقة، لأنك تتحقق أو تزامن عبر تبويبات أو تعرض حالات مختلفة لمستخدمين مختلفين، تلجأ للوضع controlled. محادثة منفصلة عن من يملك الحالة، كتبت عنها في عقلان. إن كانت قائمة الخيارات أكبر من أن تُشحن للعميل، تلجأ لـ combobox مغذّى من الخادم، له منشور خاص. إن كان نظام التصميم يجب أن يبدو متسقاً عبر ثلاثين مكوّناً وثلاث ثيمات، تلجأ للرموز، وهذا هنا.
تلك الطبقات فوق التشريح. لا تستبدله. يمكنك تشغيل أي منها أو إيقافها دون إعادة كتابة شكل الـ markup. هذا الجزء الذي أفتخر به، والجزء الذي استغرق الأطول لإنجازه.
الراحة
لو ضغطت هذا المنشور في جملة واحدة، فهي أن التشريح هو اللحظة التي تدرك فيها أن المكتبة لن تحبسك.
لم أفهم كم أحتاج ذلك حتى شحنت صفحة Corex غيّر فيها المصمم رأيه ثلاث مرات في أسبوع. في كل مرة تحركت درجة أو اثنتين على السلم. الـ hook لم يلاحظ. المستخدم لم يلاحظ. أنا لاحظت، لأن كل إعادة كتابة شعرت كتحرير مستند، لا كإعادة هيكلة نظام. لم ينكسر شيء. لم تتحول اختبارات للأحمر لأن الـ markup نما غلافاً إضافياً. الأكورديون فتح وأغلق بنفس الطريقة يوم الاثنين.
هذا ما أريده من مكتبة واجهة الآن. ليس افتراضاً مثالياً. ليس مقابض لا نهائية. حرية أن يبقى رأيي حديثاً مع تطور التصميم، دون أن أدفع ثمنه مرتين.
لرؤية الأشكال جنباً إلى جنب، عرض تشريح الأكورديون هو المسار الأقصر. افتحه، تنقّل بين المستويات، انظر HTML المُصدر في كل خطوة. ستفهم ما أعنيه. نفس الآلة. أشكال مختلفة. قرارك في كل مرة.
التالي في السلسلة: كيف يحافظ نفس المكوّن على رأسه عندما يظن الخادم والعميل أنهما يملكان حالته، في عقلان. ثم الـ hook نفسه للجسر بين Phoenix والآلة. ثم الرموز والتباين والمظهر. ثم مشكلة المطارات، عندما يكون الكتالوج أكبر من أن يُشحن.