टोस्ट कॉम्पोनेंट बनाना

ज़रूरत के हिसाब से काम करने वाला और सुलभ टॉस्ट कॉम्पोनेंट बनाने का तरीका.

इस पोस्ट में, मैं टॉस्ट कॉम्पोनेंट बनाने का तरीका शेयर करना चाहता हूं. डेमो आज़माएं.

डेमो

अगर आपको वीडियो देखना पसंद है, तो यहां इस पोस्ट का YouTube वर्शन दिया गया है:

खास जानकारी

टॉस्ट, उपयोगकर्ताओं के लिए इंटरैक्टिव नहीं होते. साथ ही, ये ऐसिंक्रोनस और छोटे मैसेज होते हैं. आम तौर पर, इनका इस्तेमाल इंटरफ़ेस फ़ीडबैक पैटर्न के तौर पर किया जाता है, ताकि उपयोगकर्ता को किसी कार्रवाई के नतीजों के बारे में जानकारी दी जा सके.

इंटरैक्शन

सूचनाओं, सूचनाओं, और प्रॉम्प्ट के मुकाबले, टॉस्ट इंटरैक्टिव नहीं होते. इन्हें खारिज नहीं किया जा सकता और न ही इन्हें हमेशा के लिए दिखने के लिए सेट नहीं किया जा सकता. सूचनाएं, ज़्यादा अहम जानकारी, सिंक किए गए मैसेज के लिए होती हैं. इन मैसेज के लिए इंटरैक्शन की ज़रूरत होती है. इसके अलावा, ये पेज लेवल के बजाय सिस्टम लेवल के मैसेज होते हैं. सूचना देने की अन्य रणनीतियों के मुकाबले, टॉस्ट ज़्यादा पैसिव होते हैं.

मार्कअप

<output> एलिमेंट, टॉस्ट के लिए एक अच्छा विकल्प है, क्योंकि स्क्रीन रीडर को इसकी सूचना दी जाती है. सही एचटीएमएल, हमें JavaScript और सीएसएस की मदद से बेहतर बनाने के लिए एक सुरक्षित आधार देता है. साथ ही, इसमें बहुत सारा 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 का इस्तेमाल करता है कि किन किन किनारों पर पिन करना है. साथ ही, यह उसी block-end किनारे से थोड़ा padding भी इस्तेमाल करता है.

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

.gui-toast-container एलिमेंट पर ओवरले किए गए DevTools बॉक्स साइज़ और पैडिंग वाला स्क्रीनशॉट.

टॉस्ट कंटेनर, व्यूपोर्ट में अपनी जगह तय करने के साथ-साथ एक ग्रिड कंटेनर भी होता है. इसमें टॉस्ट अलाइन और डिस्ट्रिब्यूट किए जा सकते हैं. आइटम को justify-content का इस्तेमाल करके एक ग्रुप के तौर पर और justify-items का इस्तेमाल करके अलग-अलग सेंटर किया जाता है. gap का इस्तेमाल करें, ताकि टोस्ट एक-दूसरे से न छुएं.

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

टॉस्ट ग्रुप पर सीएसएस ग्रिड ओवरले वाला स्क्रीनशॉट. इस बार, टॉस्ट के चाइल्ड एलिमेंट के बीच के स्पेस और गैप को हाइलाइट किया गया है.

जीयूआई टोस्ट

किसी एक टॉस्ट में कुछ padding, border-radius के साथ कुछ नरम कोने, और मोबाइल और डेस्कटॉप के साइज़ में मदद करने के लिए min() फ़ंक्शन होता है. यहां दी गई सीएसएस में रिस्पॉन्सिव साइज़, टॉस्ट को व्यूपोर्ट के 90% या 25ch से ज़्यादा चौड़ा होने से रोकता है.

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

एक .gui-toast एलिमेंट का स्क्रीनशॉट, जिसमें पैडिंग और बॉर्डर रेडियस दिखाया गया है.

स्टाइल

लेआउट और पोज़िशन सेट करने के बाद, ऐसी सीएसएस जोड़ें जो उपयोगकर्ता की सेटिंग और इंटरैक्शन के हिसाब से बदलाव करने में मदद करती है.

टोस्ट कंटेनर

टॉस्ट इंटरैक्टिव नहीं होते. उन पर टैप करने या स्वाइप करने से कुछ नहीं होता. हालांकि, फ़िलहाल वे पॉइंटर इवेंट का इस्तेमाल करते हैं. नीचे दी गई सीएसएस का इस्तेमाल करके, टॉस्ट को क्लिक चुराने से रोकें.

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

जीयूआई टोस्ट

कस्टम प्रॉपर्टी, एचएसएल, और प्राथमिकता वाली मीडिया क्वेरी की मदद से, टॉस्ट को हल्के या गहरे रंग वाली अडैप्टिव थीम दें.

.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%;
  }
}

ऐनिमेशन

स्क्रीन पर दिखने के दौरान, नया टॉस्ट ऐनिमेशन के साथ दिखना चाहिए. कम मोशन का इस्तेमाल करने के लिए, translate वैल्यू को डिफ़ॉल्ट रूप से 0 पर सेट किया जाता है. हालांकि, मोशन की प्राथमिकता वाली मीडिया क्वेरी में मोशन की वैल्यू को लंबाई पर अपडेट किया जाता है. सभी को कुछ ऐनिमेशन मिलता है, लेकिन सिर्फ़ कुछ उपयोगकर्ताओं को टॉस्ट की दूरी दिखती है.

यहां टॉस्ट ऐनिमेशन के लिए इस्तेमाल किए गए मुख्य फ़्रेम दिए गए हैं. सीएसएस, एक ही ऐनिमेशन में टॉस्ट के दिखने, रुकने, और हटने की प्रोसेस को कंट्रोल करेगी.

@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

स्टाइल और स्क्रीन रीडर के लिए उपलब्ध एचटीएमएल तैयार होने के बाद, उपयोगकर्ता के इवेंट के आधार पर, टॉस्ट बनाने, जोड़ने, और मिटाने के लिए JavaScript की ज़रूरत होती है. डेवलपर के लिए, टॉस्ट कॉम्पोनेंट का इस्तेमाल करना आसान होना चाहिए. साथ ही, इसमें कम से कम बदलाव करने पड़ें. जैसे:

import Toast from './toast.js'

Toast('My first toast')

टॉस्ट ग्रुप और टॉस्ट बनाना

जब JavaScript से टॉस्ट मॉड्यूल लोड होता है, तो उसे एक टॉस्ट कंटेनर बनाना होगा और उसे पेज पर जोड़ना होगा. मैंने body से पहले एलिमेंट जोड़ने का विकल्प चुना है. इससे z-index को स्टैक करने में आने वाली समस्याओं की संभावना कम हो जाएगी, क्योंकि कंटेनर, बॉडी के सभी एलिमेंट के लिए कंटेनर के ऊपर है.

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

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

हेड और बॉडी टैग के बीच मौजूद टॉस्ट ग्रुप का स्क्रीनशॉट.

init() फ़ंक्शन को मॉड्यूल में अंदरूनी तौर पर कॉल किया जाता है. इसमें एलिमेंट को Toaster के तौर पर स्टैश किया जाता है:

const Toaster = init()

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() फ़ंक्शन, एक या कई टॉस्ट को हैंडल करता है. सबसे पहले, टॉस्ट की संख्या और यह देखना कि मोशन ठीक है या नहीं. इसके बाद, इस जानकारी का इस्तेमाल करके टॉस्ट जोड़ें या कोई शानदार ऐनिमेशन बनाएं, ताकि अन्य टॉस्ट नए टॉस्ट के लिए "जगह बना सकें".

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) पेज पर एक टॉस्ट जोड़ता है, जिससे सीएसएस ऐनिमेशन ट्रिगर होते हैं: ऐनिमेट इन, वेट 3s, ऐनिमेट आउट. flipToast() को तब कॉल किया जाता है, जब पहले से मौजूद टॉस्ट हों. इसके लिए, पॉल लुईस की FLIP तकनीक का इस्तेमाल किया जाता है. इसका मकसद, नए टॉस्ट को जोड़ने से पहले और बाद में, कंटेनर की पोज़िशन में हुए अंतर का हिसाब लगाना है. इसे इस तरह समझें कि टोस्टर अभी कहां है और कहां जाएगा. इसके बाद, जहां से वह था वहां से लेकर जहां वह है वहां तक ऐनिमेशन दिखाएं.

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',
  })
}

सीएसएस ग्रिड, लेआउट को ऊपर उठाता है. जब कोई नया टॉस्ट जोड़ा जाता है, तो ग्रिड उसे सबसे पहले रखता है और अन्य टॉस्ट के साथ स्पेस देता है. इस दौरान, कंटेनर को पुरानी जगह से ऐनिमेट करने के लिए, वेब ऐनिमेशन का इस्तेमाल किया जाता है.

सभी JavaScript को एक साथ जोड़ना

Toast('my first toast') को कॉल करने पर, एक टॉस्ट बनाया जाता है और उसे पेज पर जोड़ा जाता है. हो सकता है कि नए टॉस्ट को शामिल करने के लिए, कंटेनर को ऐनिमेशन दिया गया हो. इसके बाद, एक वाद दिया जाता है और बनाए गए टॉस्ट को देखा जाता है, ताकि वाद पूरा करने के लिए सीएसएस ऐनिमेशन पूरा हो जाए. इसमें तीन मुख्य फ़्रेम वाले ऐनिमेशन शामिल होते हैं.

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 finishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedfinishedallSettled हमारे लिए यह काम करता है. इसके सभी वादे पूरे हो जाने पर, यह अपने-आप पूरा हो जाता है. await Promise.allSettled() का इस्तेमाल करने का मतलब है कि कोड की अगली लाइन, एलिमेंट को भरोसे के साथ हटा सकती है और यह मान सकती है कि टॉस्ट का लाइफ़साइकल पूरा हो गया है. आखिर में, resolve() को कॉल करने से, हाई लेवल का टॉस्ट प्रॉमिस पूरा हो जाता है, ताकि टॉस्ट दिखने के बाद डेवलपर, साफ़-सफ़ाई कर सकें या कोई दूसरा काम कर सकें.

export default 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')
}

नतीजा

अब आपको पता है कि मैंने यह कैसे किया, तो आप कैसे करेंगे‽ 🙂

आइए, अलग-अलग तरीकों का इस्तेमाल करके, वेब पर कॉन्टेंट बनाने के सभी तरीके जानें. डेमो बनाएं और मुझे ट्वीट करें लिंक भेजें. हम इसे कम्यूनिटी रीमिक्स सेक्शन में जोड़ देंगे!

कम्यूनिटी रीमिक्स