بناء مكون نخب

نظرة عامة أساسية حول كيفية إنشاء مكوّن مربّع حوار منبثق قابل للتكيّف وسهل الاستخدام

في هذه المشاركة، أريد مشاركة أفكار حول كيفية إنشاء مكوّن مربّع حوار منبثق. جرِّب الإصدار التجريبي.

العرض التوضيحي

إذا كنت تفضّل الفيديو، يمكنك الاطّلاع على نسخة من هذه المشاركة على YouTube:

نظرة عامة

النوافذ المنبثقة هي رسائل قصيرة غير تفاعلية وغير متزامنة للمستخدمين. ويتم استخدامها بشكل عام كنمط لملاحظات الواجهة لإعلام المستخدم بنتائج إجراء معيّن.

التفاعلات

تختلف الإشعارات المنبثقة عن الإشعارات والتنبيهات و الطلبات لأنّه لا يمكن التفاعل معها، وليس من المفترض إغلاقها أو إبقاؤها ظاهرة. وتهدف الإشعارات إلى عرض معلومات أكثر أهمية أو رسائل متزامنة تتطلب التفاعل، أو لإرسال رسائل على مستوى النظام (بدلاً من إرسال رسائل على مستوى الصفحة). تكون الإشعارات المنبثقة أكثر استسلامًا من استراتيجيات الإشعارات الأخرى.

Markup

يُعدّ عنصر <output> خيارًا جيدًا لخبز الخبز لأنّه يتم تعريفه لبرامج قراءة الشاشة. توفّر لغة HTML الصحيحة قاعدة آمنة يمكننا تحسينها باستخدام JavaScript و CSS، وسيكون هناك الكثير من JavaScript.

نخب

<output class="gui-toast">Item added to cart</output>

يمكن أن يكون أكثر شمولاً من خلال إضافة role="status". يقدّم ذلك بديلاً في حال عدم منح المتصفّح عناصر <output> دورًا ضمنيًا وفقًا للمواصفات.

<output role="status" class="gui-toast">Item added to cart</output>

حاوية الخبز

يمكن عرض أكثر من إشعار عائم في الوقت نفسه. لتنظيم عدة خبز محمص، يتم استخدام حاوية. تتحكّم هذه الحاوية أيضًا في موضع الرسائل المنبثقة على الشاشة.

<section class="gui-toast-group">
  <output role="status">Wizard Rose added to cart</output>
  <output role="status">Self Watering Pot added to cart</output>
</section>

التنسيقات

اخترت تثبيت الإشعارات المنبثقة في inset-block-end مساحة العرض، وفي حال إضافة المزيد من الإشعارات المنبثقة، يتم تجميعها من تلك الحافة على الشاشة.

حاوية واجهة المستخدم الرسومية

تقوم حاوية الخبز بجميع أعمال التخطيط لتقديم الخبز المحمص. وهي تُستخدم fixed في إطار العرض وتستخدم السمة المنطقية inset لتحديد الحواف التي يجب التثبيت عليها، بالإضافة إلى جزء صغير من padding من حافة block-end نفسها.

.gui-toast-group {
  position: fixed;
  z-index: 1;
  inset-block-end: 0;
  inset-inline: 0;
  padding-block-end: 5vh;
}

لقطة شاشة تعرض حجم مربّع &quot;أدوات مطوّري البرامج&quot; ومساحة الحشو التي تمّت إضافتها على عنصر ‎.gui-toast-container

بالإضافة إلى تحديد موضع حاوية مربّع المعلومات المنبثق في إطار العرض، تكون حاوية مربّع المعلومات المنبثق حاوية شبكة يمكنها محاذاة مربّعات المعلومات المنبثقة وتوزيعها. يتمّ وضع العناصر في وسط الشاشة كمجموعة باستخدام justify-content وفي وسط الشاشة بشكلٍ فردي باستخدام justify-items. يُرجى رمي gap قليلاً حتى لا تلمس رسائل الخبز المحمص.

.gui-toast-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1vh;
}

لقطة شاشة تعرض شبكة CSS التي تمّت إضافتها على مجموعة النوافذ المنبثقة الصغيرة، وهذه المرة
يتمّ إبراز المساحة والفواصل بين العناصر الفرعية للنوافذ المنبثقة الصغيرة.

واجهة المستخدم الرسومية الجديدة

يتضمّن خبز محمص فردي بعض padding، وبعض الزوايا الأخف من خلال علامة border-radius، ووظيفة min() للمساعدة في تحديد حجم المحتوى على الأجهزة الجوّالة وأجهزة الكمبيوتر المكتبي. يمنع الحجم المتوافق مع جميع الأجهزة في ملف CSS التالي تضخّم النوافذ المنبثقة إلى أكثر من %90 من إطار العرض أو 25ch.

.gui-toast {
  max-inline-size: min(25ch, 90vw);
  padding-block: .5ch;
  padding-inline: 1ch;
  border-radius: 3px;
  font-size: 1rem;
}

لقطة شاشة لعنصر .gui-toast واحد مع عرض المساحة المتروكة
ونصف قطر الحد.

الأنماط

بعد ضبط التنسيق والموضع، أضِف ملف CSS يساعد في التكيّف مع إعدادات المستخدم وتفاعلاته.

حاوية الخبز

لا تكون الإشعارات المنبثقة تفاعلية، ولا يؤدي النقر عليها أو التمرير السريع عليها إلى أي إجراء، ولكنها تستهلك حاليًا أحداث المؤشر. امنع الإشعارات المبسّطة من سرقة النقرات باستخدام خدمة مقارنة الأسعار (CSS) التالية.

.gui-toast-group {
  pointer-events: none;
}

إشعار منبثق لواجهة المستخدم الرسومية

يمكنك منح الإشعارات المنبثقة مظهرًا متوافقًا فاتحًا أو داكنًا باستخدام خصائص مخصّصة ونظام الألوان HSL و query media preference.

.gui-toast {
  --_bg-lightness: 90%;

  color: black;
  background: hsl(0 0% var(--_bg-lightness) / 90%);
}

@media (prefers-color-scheme: dark) {
  .gui-toast {
    color: white;
    --_bg-lightness: 20%;
  }
}

Animation

يجب أن يقدم إشعار منبثق جديد نفسه مع صورة متحركة عند دخوله إلى الشاشة. يتم تنفيذ عملية تقليل الحركة من خلال ضبط قيم translate على 0 بشكل تلقائي، ولكن مع تعديل قيمة الحركة إلى طول في طلب بحث الوسائط المفضّلة للحركة . يظهر للجميع بعض الصور المتحركة، ولكن يظهر للبعض فقط مربّع الرسالة المنبثقة وهو يتحرك.

في ما يلي الإطارات الرئيسية المستخدَمة في الصورة المتحركة للشريط المنبثق. ستتحكم CSS في مدخل، والانتظار، والخروج من الخبز المحمص، كل ذلك في رسم متحرك واحد.

@keyframes fade-in {
  from { opacity: 0 }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes slide-in {
  from { transform: translateY(var(--_travel-distance, 10px)) }
}

بعد ذلك، يُعدّ عنصر مربّع الرسائل المنبثقة المتغيّرات وينظّم الإطارات الرئيسية.

.gui-toast {
  --_duration: 3s;
  --_travel-distance: 0;

  will-change: transform;
  animation: 
    fade-in .3s ease,
    slide-in .3s ease,
    fade-out .3s ease var(--_duration);
}

@media (prefers-reduced-motion: no-preference) {
  .gui-toast {
    --_travel-distance: 5vh;
  }
}

JavaScript

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

import Toast from './toast.js'

Toast('My first toast')

إنشاء مجموعة الإشعارات المنبثقة والإشعارات المنبثقة

عند تحميل وحدة Toast من JavaScript، يجب أن تنشئ حاوية toast وتضيفها إلى الصفحة. اخترت إضافة العنصر قبل body، ما سيقلّل من احتمال حدوث مشاكل في تجميع z-index لأنّ الحاوية فوق الحاوية التي تحتوي على جميع عناصر النصّ.

const init = () => {
  const node = document.createElement('section')
  node.classList.add('gui-toast-group')

  document.firstElementChild.insertBefore(node, document.body)
  return node
}

لقطة شاشة لمجموعة النوافذ المنبثقة بين علامتَي head وbody

يتم استدعاء الدالة init() داخليًا في الوحدة، مع تخزين العنصر كـ Toaster:

const Toaster = init()

يتم إنشاء عنصر HTML للشريط المنبثق باستخدام الدالة createToast(). تتطلّب الدالة بعض النصوص، وتنشئ عنصر <output> وتزيّنه ببعض الفئات والسمات، وتضبط النص، وتعرض العقدة.

const createToast = text => {
  const node = document.createElement('output')
  
  node.innerText = text
  node.classList.add('gui-toast')
  node.setAttribute('role', 'status')

  return node
}

إدارة إشعار منبثق واحد أو عدّة إشعارات

تضيف JavaScript الآن حاوية إلى المستند لاحتواء الإشعارات المنبثقة وتكون جاهزة لإضافة الإشعارات المنبثقة التي تم إنشاؤها. تنظِّم الدالة addToast() معالجة رسائل Toast واحدة أو عدّة رسائل. تحقق أولاً من عدد محمصات الخبز، وما إذا كانت الحركة لا بأس بها، ثم استخدم هذه المعلومات لإلحاق الخبز المحمص أو لإضافة بعض الرسوم المتحركة التخيلية بحيث تبدو الإعلانات الأخرى "لتوفير مساحة" للخبز المحمص الجديد.

const addToast = toast => {
  const { matches:motionOK } = window.matchMedia(
    '(prefers-reduced-motion: no-preference)'
  )

  Toaster.children.length && motionOK
    ? flipToast(toast)
    : Toaster.appendChild(toast)
}

عند إضافة أول رسالة مصغّرة، يضيف Toaster.appendChild(toast) رسالة مصغّرة إلى الصفحة التي تشغّل صور CSS المتحركة: عرض الصورة المتحركة، الانتظار 3s، إخفاء الصورة المتحركة. يتمّ استدعاء flipToast() عندما تكون هناك إشعارات Toast حالية، باستخدام أسلوب يُسمّى FLIP من قِبل Paul Lewis. والفكرة هي احتساب الفرق في مواضع الحاوية قبل إضافة النافذة المنبثقة الجديدة وبعدها. يمكنك اعتبار ذلك مثل وضع علامة على مكان "محمّصة الخبز" الآن، ومكانها القادم، ثم إضافة مؤثرات متحركة من مكانها السابق إلى مكانها الحالي.

const flipToast = toast => {
  // FIRST
  const first = Toaster.offsetHeight

  // add new child to change container size
  Toaster.appendChild(toast)

  // LAST
  const last = Toaster.offsetHeight

  // INVERT
  const invert = last - first

  // PLAY
  const animation = Toaster.animate([
    { transform: `translateY(${invert}px)` },
    { transform: 'translateY(0)' }
  ], {
    duration: 150,
    easing: 'ease-out',
  })
}

تُستخدم شبكة CSS لرفع التخطيط. عند إضافة إشعار عاجل جديد، تضعه الشبكة في البداية وتترك مسافة بينه وبين الإشعارات الأخرى. وفي الوقت نفسه، يتم استخدام تأثير متحرك على الويب لتحريك الحاوية من موضعها القديم.

تجميع كل رمز JavaScript معًا

عند استدعاء Toast('my first toast')، يتم إنشاء إشعار منبثق تتم إضافته إلى الصفحة (ربما تكون الحاوية متحركة لتلائم النموذج الجديد)، ويتم عرض وعد تتم مراقبة اكتمال الصور المتحركة لـ CSS (الرسوم المتحركة الثلاثة للإطارات الرئيسية) للحصول على حل للوعود.

const Toast = text => {
  let toast = createToast(text)
  addToast(toast)

  return new Promise(async (resolve, reject) => {
    await Promise.allSettled(
      toast.getAnimations().map(animation => 
        animation.finished
      )
    )
    Toaster.removeChild(toast)
    resolve() 
  })
}

أعتقد أنّ الجزء المربك في هذا الرمز هو وظيفة Promise.allSettled() وتعيين toast.getAnimations(). بما أنّني استخدمت عدّة صور متحرّكة للإطارات الرئيسية لشريط الإشعارات المنبثقة، لمعرفة ما إذا كانت قد اكتملت جميعها، يجب أن تتم أولاً طلب كل منها من JavaScript، ثم يجب مراقبة كل من finished الوعد الذي يُقدّمه كل منها لإكماله. allSettled يناسبنا ذلك، حيث يتم حلّ المشكلة تلقائيًا بعد تنفيذ كل الوعود التي تقدّمها . يعني استخدام await Promise.allSettled() أنّ السطر التالي من الرمز البرمجي يمكنه إزالة العنصر بثقة وافتراض أنّ النافذة المنبثقة قد أكملت دورة حياتها. وأخيرًا، يفي الاتصال بـ resolve() الوعد باستخدام Toast عالي المستوى بحيث يمكن للمطوّرين تنظيف أو القيام بأي عمل آخر بعد ظهور الإشعار المنبثق.

export default Toast

أخيرًا، يتم تصدير الدالة Toast من الوحدة لنصوص برمجية أخرى تتمكّن من استيرادها واستخدامها.

استخدام مكوّن Toast

يتم استخدام النموذج المنبثق أو تجربة مطوّر التطبيق من خلال استيراد دالة Toast وطلبها باستخدام سلسلة رسالة.

import Toast from './toast.js'

Toast('Wizard Rose added to cart')

إذا أراد المطوّر إجراء عملية تنظيف أو أيّ إجراء آخر، يمكنه استخدام async وawait بعد عرض مربّع التلميح.

import Toast from './toast.js'

async function example() {
  await Toast('Wizard Rose added to cart')
  console.log('toast finished')
}

الخاتمة

الآن بعد أن عرفت كيف فعلت ذلك، كيف ستفعل ذلك؟ 🙂

لننوّع أساليبنا ونتعرّف على جميع الطرق لإنشاء تطبيقات على الويب. أنشئ عرضًا توضيحيًا أو روابط تغريدةني وسأضيفه إلى قسم الريمكسات في المنتدى أدناه.

الريمكسات التي أنشأها المستخدمون