در این مدتی که با فریمورک svelte کار می کرده ام موقعیت هایی پیش اومده که باید به کاربر notification نشون میدادم (با استفاده از المان های html). راه های مختلفی رو امتحان کردم و در نهایت راهی رو که برای پیاده سازی یک notification system ترجیح میدم پیدا کردم.
در این پست می خوام روشی رو که هربار استفاده می کنم رو با شما هم به اشتراک بزارم.
قبل از اینکه یه راست بریم سراغ کد، اول باید یه پروژه svelte رو راه بندازیم. برای اینکار من ترجیح میدم از template رسمی خود svelte استفاده کنم. پس اول بریم و با استفاده از این template یه repo ایجاد کنیم. لینک repo رو در انتهای پست بهتون میدم (مورد توجه کسانی که صبرشون به آخر رسیده). پس اگه کد ها رو اشتباه جایگذاری کردید نگران نباشید.

اول از همه در گیتهاب به svletejs/template مراجعه می کنیم و گزینه Use this template رو میزنیم. سپس می تونیم یه repo ایجاد کنیم

بعد از ساخت repo. باید روی کامپیوتر خودمون پروژه رو clone کنیم:
git clone https://github.com/Amohammadi2/Svelte-Notification-System-Tutorial.git
و بعد از وارد شدن به فولدر repo دستور زیر رو جهت نصب وابستگی (dependency) های پروژه وارد می کنیم:
npm install
بعد از اینکه وابستگی های مورد نیاز نصب شدند. app رو تست می کنیم تا ببینیم همه چی OK هست یا نه.
npm run dev
با زدن دستور بالا اول کل پروژه compile میشه و بعد سرور توسعه rollup راه می افته. اگه تا به این مرحله همه چیز طبق انتظار پیش رفته باشه، می تونید مرورگرتون رو باز کنید و به آدرس localhost:5000 برید.
بعد با این صفحه مواجه میشید:

قبل از اینکه بریم سراغ نوشتن کد ها. بزارید با ویژگی های notification systemـی که می خواهیم بسازیم آشناتون کنم:
برای پیاده سازی هم به دو component نیاز خواهیم داشت:
اولی جا و مکان Notification ها رو مشخص می کنه، دومی هم خود notification ها هستند که شامل یه متن پیام یه رنگ پس زمینه و دکمه بستن و همچنین یک تایمر هستند.
همچنین به یک ماژول مستقل javascript برای برای افزودن Notification نیاز خواهیم داشت که با استفاده از utility های اون ماژول از هر جای کد که خواستیم به سادگی یک Notification به نمایش بزاریم.
برای اینکه یه دید اجمالی از پروژه دستتون بیاد به gif زیر نگاهی بندازید:

خوب دیگه حالا که یه دل سیر portfolio من رو دیدید. می تونیم بریم سراغ پیاده سازی.
اولین کاری که باید بکنید اینه که وارد فایل App.svelte در پوشه src بشید و هرچی کد هست رو پاک کنید. شما رو نمی دونم ولی انجام دادن این کار با کد developer های دیگه به من احساس دوباره متولد شدن میده.
حالا دیگه باید پنجره مرورگر شما تبدیل به یک صفحه نقاشی سفید و دست نخورده شده باشه. صفحه ای که باید هنرتون رو در اون به نمایش بزارید!
در پوشه src پوشه دیگه ای به نام components بسازید. باز داخل components پوشه دیگه ای به نام notifications بسازید. اینجا همونجایی هست که قراره component هامون رو قرار بدیم.
در نهایت ساختار پوشه های شما باید اینطور باشه
└───src
└───components
└───notifications
حالا در پوشه notifications دو فایل به نام های زیر بسازید:
اول از همه میریم سراغ NotificationBox.svelte که جا و مکان Notification ها رو در صفحه مشخص می کنه و با وجود اون می تونیم Notification ها رو هر جای صفحه جا بدیم.

همونطور که می بینید زیاد چیز خاصی نیست. z-index هم فقط محض احتیاط به کار رفته که .notif-box به هیچ قیمتی زیر المان های دیگه نره. در component های svelte، کد های css رو بین تگ های style می نویسید و هر جای دیگه از کد (طبق قرار داد بالای style ها) می تونید کد های html تون رو بنویسید. کد های javascript هم بین تگ های script نوشته میشن که به صورت قراردادی در ابتدای فایل قرار میگیرند.
تمام کد های css ای که در NotificationBox.svelte می نویسید فقط مربوط به اون قسمت میشن و قسمت های دیگه رو تحت تاثیر قرار نمیدن. این نامگذاری رو آسون می کنه و باعث میشه که دیگه اشکال های مربوط به selector های همنام و ترکیبات عجیب و غریبشون پیش نیاد.
حالا فعلا کارمون با NotificationBox تمام شده. فایل Notification.svelte رو باز کنید. کد های زیر رو در اون بنویسید:

در این component ما دو property میگیریم به نام msg و type. از type برای تعیین نوع notification (مثلا success, alert, warning) و اعمال style های css بر اساس اون استفاده می کنیم. msg هم که به نمایش گذاشته میشه.
نکته: not-progress همون progress-barـی هست که روند پیشرفت timer رو نشون میده. بعدا با یک key frame animation اون رو هم راه می اندازیم.
حالا بیاید کمی css به این فایل اضافه کنیم. میان تگ های style این کد ها رو وارد کنید.

اول از همه عرض notification رو 100% عرض المان والد (parent) در نظر میگیریم و با قرار دادن box-sizing در خط 3 مانع تاثیر padding روی اندازه اصلی میشیم. position اش رو هم به خاطر اینکه بعدا قراره یک progress-bar با position: absolute بهش اضافه کنیم، relative در نظر میگیریم. بقیه (box-shadow) فقط برای خوشگلی notification مونه.
حالا وقتشه که حالت های مختلف notification رو هم با رنگ های پس زمینه مختلف بهش اضافه کنیم:

حالا اگه فرضا مقدار property که اسمش type هست "success" باشه، رنگ پس زمینه سبز برای notification در نظر گرفته خواهد شد (و همچنین درمورد بقیه حالت های alert و warning)
اگه یادتون باشه، notification ما یک دکمه برای بستن داشت با کلاسی به نام dismiss. کمی هم به اون style بدیم بد نیست:

در کد های بالا تنها نکته ای که می خوام بهش اشاره کنم display: inline-block هستش که برای این استفاده میشه که المان های inline مانند block ها قابلیت افزودن margin هم داشته باشند. (نمی دونم چرا وقتی فقط از inline استفاده می کنم دیگه margin ها هیچ اثری ندارند).
الان می تونیم به progress-bar مون یک animation هم اضافه کنیم که روند پیشرفت timer رو مشخص کنه:

توضیحات: .not-progress عرض 100% داره (به واسطه right: 0 و left: 0) ولی در واقع المان after:: چیزیه که قراره با animation عرضش رو به مرور زمان افزایش بدیم.
حالا تقریبا کار های مربوط به طراحی و افزودن html و css تموم شده. برای اینکه یه تست بکنیم. فایل App.svelte رو باز کنید و کد های زیر رو داخلش بنویسید:

تا به الان اگر کد ها رو به درستی وارد کرده باشید همچین نتیجه ای رو خواهید دید:

حالا وقتشه که با استفاده از javascript سیستمی برای افزودن، مدیریت و بستن notification ها اضافه بسازیم.
در پوشه src پروژه خود پوشه ای به نام stores بسازید و در اون فایلی به نام notifications.js بسازید. داخل فایل این کد ها رو وارد کنید:

حالا ما یه سری notification به صورت hard-code شده داریم. نکته ای که درباره notification هامون وجود داره اینه که من دارم از یک Symbol برای ساخت یک unique id استفاده می کنم. بعدا با استفاده از Symbol موجود در pk می توان تشخیص داد کدوم notification باید حذف بشه.
اگه قبلا با دیتابیس های SQL کار کرده باشید. pk دقیقا کار یک primary key رو انجام میده. اگه درمورد Symbol ها چیزی نمی دونید می تونید به این صفحه مراجعه کنید.
برای اینکه از لیست notification هامون component های Notification بسازیم. باید از each-block استفاده کنیم (البته این مخصوص svelte js هستش و مثل template ها هستش). کد زیر رو در App.svelte در همونجایی که NotificationBox رو قرار دادید وارد کنید.

در همین 5 خط کد نکات زیادی وجود داره که شاید براتون سوال باشه. اولا syntax کلی `each` در svelte اینطوریه:
{#each list as item (optional_key)} <!-- now a variable called item is accessible in this scope --> <DoStuff /> ... {/each}
اجازه بدید قبل از اینکه ادامه بدیم یکم حاشیه برم. اگه با keyed each block ها آشنا هستید که این پرانتز رو رد کنید وگرنه پیشنهاد می کنم بخونید
پرانتز باز (
اگه قرار باشه که روی list تون عملیاتی انجام بدید که باعث تغییر index آیتم ها میشه (مثل حذف یا مرتب سازی (sort)) باید از یک key استفاده کنید. svelte میاد این key رو با هر آیتم در نظر میگیره و با استفاده از اون تشخیص میده کدوم آیتم ها حذف شده اند یا جای کدوم آیتم ها تغییر کرده.
اگه key نباشه چه اتفاقی می افته؟
در حالت عادی svelte JS در each block میاد به ازای هر آیتم در لیست component هایی که بهش میگیم رو می سازه. اگر key وجود نداشته باشه و ما مثلا آیتمی رو حذف کنیم، component هایی که به ازای اون آیتم قبلا ایجاد شده اند پاک نمیشن بلکه فقط مقدار property هاشون عوض میشه.
مثلا در این مورد ما اگر key وجود نداشته باشه، ما هم 5 notification داشته باشیم و دومی رو حذف کنیم. component مربوط به دومی حذف نمیشه بلکه property های msg و type و pk نوتیفیکیشن سوم رو میگیره و همچنین notification سوم هم مقدار property های notification چهارم رو میگیره و به همین ترتیب تا آخر میره و در نهایت آخرین Notification حذف میشه.
بزارید با یک عکس دقیق تر توضیح بدم:

این نوع مکانیزم در شرایطی که ما مقادیر وابسته به property ها داریم مشکل ایجاد می کنه. مثلا فرض کنید که ما یک componentـی داریم که propertyـی داره به نام number. ما مقدار number رو دو برابر می کنیم و در متغیری به نام double ذخیره می کنیم. حالا اگه ترتیب لیست عوض بشه، مقدار number تغییر پیدا می کنه اما مقدار double ثابت می مونه.
البته میشه با روش هایی این رو حل کرد اما چرا راه سخت رو بریم وقتی راه آسون تری هست؟ یه key به svelte بدی همه این مشکلات رو حریفه و می تونه تصمیم بگیره چیکار کنه تا این مشکلات پیش نیاد.
) پرانتز بسته
حالا وقتشه که یک کلاس ایجاد کنیم که با استفاده ازش Notification اضافه و حذف کنیم. برای این کار در فولدر src فولدری به نام utils بسازید و داخلش فایلی به نام notification_api.js اضافه کنید. کد ها رو در فایلی که ایجاد کرده اید بنویسید:

اگه توجه کنید، در هر دو متد داریم متد update رو که در notifications_list قرار داره رو فراخوانی می کنیم. این متد مقدار ذخیره شده در notifications_list رو در یک callback function بهمون میده. حالا ما باید مقدار جدید رو در این callback برگردونیم.
در متد delete داریم از filter برای حذف کردن notification ای که pk اش برابر با pk دریافت شده از طریق پارامتر تابع است استفاده می کنیم. _(فقط یکی از notification ها pk اش می تونه برابر با pk دریافت شده باشه پس بقیه از filter رد میشن)
حالا وقتشه که wrapper ها رو هم در ادامه کلاس اضافه کنیم تا دیگه کارمون با NotificationApi تموم بشه.

به طور کلی در دو حالت زیر باید notification ها بسته بشن:
حالا ما تمام چیز های مورد نیازمون رو به صورت یکپارچه در NotificationAPI داریم، فقط کافیه با component مون integrate اش کنیم. برای این کار فایل Notification.svelte رو باز کنید و کد هاشو تغییر بدید:

این قسمت جنبه تست داره می تونید کلا ردش کنید چون اصلا مهم نیست. اگه هم تصمیم گرفتید ادامه بدید هم که دمتون گرم واقعا.

هرجور عشقتون کشید بهشون style بدید. جنبه مهم قضیه اضافه کردن notification ها از طریق NotificationAPI هست.
به هر حال. تا الان نتیجه کارمون اینه:

اما آخه چرا اینقدر خشک و خالی! باید کمی animation بهش بدیم.
این رو به راحتی به کمک svelte حلش می کنیم. فقط کافیه کمی کد App.svelte رو تغییر بدید:


همونطور که قول داده بودم، این هم لینک repository پروژه
خوب دیگه به آخر پست رسیدیم، امیدوارم مفید واقع شده باشه. اگه خوشتون اومد لایک کنید اگه سوالی داشتید کامنت بزارید، در اسرع وقت جواب میدم.
ممکنه از این نوشته ها هم خوشتون بیاد: