يمكننا الآن البدء في إنشاء تطبيقنا مثل تطبيق قائمة المهام بعد أن فهمنا الأمور الأساسية في إطار عمل Svelte في المقال السابق، إذ سنلقي في هذا المقال نظرةً على الوظائف المطلوبة لتطبيقنا أولًا ثم سننشئ المكوِّن Todos.svelte
وسنضع شيفرة HTML وشيفرة التنسيق الثابتة في مكانها، وبالتالي سيصبح كل شيء جاهزًا لبدء تطوير ميزات تطبيق قائمة المهام التي سننتقل إليها في المقالات اللاحقة.
نريد أن يتمكن المستخدِمون من تصفح المهام وإضافتها وحذفها ووضع علامة عليها بوصفها مكتملةً، كما سنلقي نظرةً على بعض المفاهيم الأكثر تقدمًا.
- المتطلبات الأساسية: يوصَى على الأقل بأن تكون على دراية بأساسيات لغات HTML و CSS وجافاسكربت JavaScript، ومعرفة باستخدام سطر الأوامر أو الطرفية، وستحتاج طرفية مثبَّت عليها node وnpm لتصريف وبناء تطبيقك.
- الهدف: معرفة كيفية إنشاء مكوِّن Svelte وتصييره في مكوِّن آخر وتمرير البيانات إليه باستخدام الخاصيات Props وحفظ حالته.
يمكن متابعة كتابة شيفرتك معنا، لذلك انسخ أولًا مستودع github -إذا لم تفعل ذلك مسبقًا- باستخدام الأمر التالي:
git clone https://github.com/opensas/mdn-svelte-tutorial.git
ثم يمكنك الوصول إلى حالة التطبيق الحالية من تشغيل الأمر التالي:
cd mdn-svelte-tutorial/02-starting-our-todo-app
أو يمكنك تنزيل محتوى المجلد مباشرةً كما يلي:
npx degit opensas/mdn-svelte-tutorial/02-starting-our-todo-app
تذكَّر تشغيل الأمر التالي لبدء تشغيل تطبيقك في وضع التطوير:
npm install && npm run dev
فإذا أردت متابعتنا فابدأ بكتابة الشيفرة باستخدام أداة REPL.
ميزات تطبيق قائمة المهام
سيبدو تطبيق قائمة المهام كما يلي بمجرد أن يصبح جاهزًا:
سيتمكّن المستخدِم من تطبيق الأمور التالية باستخدام واجهة المستخدِم:
- تصفح المهام.
- وضع علامة على المهام المكتملة أو تعليقها دون حذفها.
- إزالة المهام.
- إضافة مهام جديدة.
- ترشيح المهام حسب الحالة: جميع المهام أو المهام النشطة أو المهام المكتملة.
- تعديل المهام.
- وضع علامة على جميع المهام بوصفها نشطةً أو مكتملةً.
- إزالة جميع المهام المكتملة.
إنشاء المكون الأول
لننشئ المكوِّن Todos.svelte
الذي سيحتوي على قائمة المهام.
أولًا، أنشئ مجلدًا جديدًا بالاسم src/components.
ملاحظة: يمكنك وضع مكوناتك في أيّ مكان ضمن المجلد src، ولكن المجلد components هو اصطلاح معروف يجب اتباعه، مما يسمح لك بالعثور على مكوناتك بسهولة.
ثانيًا، أنشئ ملفًا بالاسم src/components/Todos.svelte بحيث يحوي ما يلي:
<h1>Svelte To-Do list</h1>
ثالثًا، عدّل العنصر title
في الملف public/index.html ليحتوي على النص "Svelte To-do list" كما يلي:
<title>Svelte To-Do list</title>
رابعًا، افتح الملف src/App.svelte واستبدل محتوياته بما يلي:
<script> import Todos from './components/Todos.svelte' </script> <Todos />
سيصدر إطار عمل Svelte في وضع التطوير تحذيرًا في طرفية المتصفح عند تحديد خاصية غير موجودة في المكوِّن مثل تحديد الخاصية name
عند إنشاء نسخة من المكون App
ضمن الملف src/main.js، إذ لا تُستخدَم هذه الخاصية ضمن المكوِّن App
، كما يجب أن تعطيك الطرفية حاليًا رسالة مثل الرسالة "name
من src/main.js ويجب أن يبدو الآن كما يلي:
import App from './App.svelte' const app = new App({ target: document.body }) export default app
إذا تحققت من عنوان URL لخادم الاختبار، فسترى تصيير المكوِّن Todos.svelte
كما يلي:
إضافة شيفرة HTML الثابتة
سنبدأ أولًا بتمثيل شيفرة HTML لتطبيقنا لتتمكّن من رؤية الشكل الذي سيبدو عليه، لذا انسخ والصق ما يلي في ملف المكوِّن Todos.svelte
ليحل محل المحتوى الموجود مسبقًا:
<!-- Todos.svelte --> <div class="todoapp stack-large"> <!-- NewTodo --> <form> <h2 class="label-wrapper"> <label for="todo-0" class="label__lg"> What needs to be done? </label> </h2> <input type="text" id="todo-0" autocomplete="off" class="input input__lg" /> <button type="submit" disabled="" class="btn btn__primary btn__lg"> Add </button> </form> <!-- Filter --> <div class="filters btn-group stack-exception"> <button class="btn toggle-btn" aria-pressed="true"> <span class="visually-hidden">Show</span> <span>All</span> <span class="visually-hidden">tasks</span> </button> <button class="btn toggle-btn" aria-pressed="false"> <span class="visually-hidden">Show</span> <span>Active</span> <span class="visually-hidden">tasks</span> </button> <button class="btn toggle-btn" aria-pressed="false"> <span class="visually-hidden">Show</span> <span>Completed</span> <span class="visually-hidden">tasks</span> </button> </div> <!-- TodosStatus --> <h2 id="list-heading">2 out of 3 items completed</h2> <!-- Todos --> <ul role="list" class="todo-list stack-large" aria-labelledby="list-heading"> <!-- todo-1 (editing mode) --> <li class="todo"> <div class="stack-small"> <form class="stack-small"> <div class="form-group"> <label for="todo-1" class="todo-label"> New name for 'Create a Svelte starter app' </label> <input type="text" id="todo-1" autocomplete="off" class="todo-text" /> </div> <div class="btn-group"> <button class="btn todo-cancel" type="button"> Cancel <span class="visually-hidden">renaming Create a Svelte starter app</span> </button> <button class="btn btn__primary todo-edit" type="submit"> Save <span class="visually-hidden">new name for Create a Svelte starter app</span> </button> </div> </form> </div> </li> <!-- todo-2 --> <li class="todo"> <div class="stack-small"> <div class="c-cb"> <input type="checkbox" id="todo-2" checked/> <label for="todo-2" class="todo-label"> Create your first component </label> </div> <div class="btn-group"> <button type="button" class="btn"> Edit <span class="visually-hidden">Create your first component</span> </button> <button type="button" class="btn btn__danger"> Delete <span class="visually-hidden">Create your first component</span> </button> </div> </div> </li> <!-- todo-3 --> <li class="todo"> <div class="stack-small"> <div class="c-cb"> <input type="checkbox" id="todo-3" /> <label for="todo-3" class="todo-label"> Complete the rest of the tutorial </label> </div> <div class="btn-group"> <button type="button" class="btn"> Edit <span class="visually-hidden">Complete the rest of the tutorial</span> </button> <button type="button" class="btn btn__danger"> Delete <span class="visually-hidden">Complete the rest of the tutorial</span> </button> </div> </div> </li> </ul> <hr /> <!-- MoreActions --> <div class="btn-group"> <button type="button" class="btn btn__primary">Check all</button> <button type="button" class="btn btn__primary">Remove completed</button> </div> </div>
تحقّق من الخرج المُصيَّر مرةً أخرى، وسترى شيئًا يشبه ما يلي:
يُعَدّ تنسيق شيفرة HTML السابق ليس جيدًا كما أنه غير مفيد وظيفيًا، ولكن لنلقِ نظرةً على الشيفرة ونرى مدى ارتباطها بالميزات التي نرغب بها:
- تسمية أو عنوان Label ومربع نص لإدخال مهام جديدة.
- ثلاثة أزرار لترشيح المهام حسب حالتها.
- تسمية label توضّح العدد الإجمالي للمهام والمهام المكتملة.
- قائمة غير مرتبة تحتوي على عنصر قائمة لكل مهمة.
- يحتوي عنصر القائمة عند تعديل المهمة على حقل إدخال وزرَين لإلغاء التعديلات أو حفظها.
- إذا لم تكن المهمة قيد التعديل، فهناك مربع اختيار لضبط حالة المهمة المكتملة وزرَين لتعديل المهمة أو حذفها.
- يوجد زران لتحديد أو إلغاء تحديد جميع المهام وإزالة المهام المكتملة.
سنعمل في المقالات اللاحقة على تشغيل جميع هذه الميزات.
ميزات سهولة الوصول Accessibility لقائمة المهام
لاحظ وجود بعض السمات غير المعتادة مثل:
<button class="btn toggle-btn" aria-pressed="true"> <span class="visually-hidden">Show</span> <span>All</span> <span class="visually-hidden">tasks</span> </button>
تخبر السمة aria-pressed
التقنيات المساعدة مثل قارئات الشاشة أنّ الزر يمكن أن يكون في إحدى الحالتين: pressed
أو unpressed
مثل القول بأن الزر في وضع التشغيل أو الإيقاف، ويعني ضبط القيمة true
أنّ الزر مضغوط افتراضيًا.
ليس للصنف visually-hidden
أيّ تأثير حتى الآن، لأننا لم نضمّن أيّ ملف CSS، وسيُخفَى أيّ عنصر موجود في هذا الصنف عن المستخدِمين المبصرين وسيظل متاحًا لمستخدِمي قارئات الشاشة بمجرد أن نضع التنسيق في مكانه، لأن هذه الكلمات لا يحتاجها المستخدِمون المبصرون، وإنما تُستخدَم لتقديم مزيد من المعلومات حول ما يفعله الزر لمستخدِمي قارئات الشاشة الذين ليس لديهم القدرة البصرية لمساعدتهم.
كما يمكنك العثور على عنصر <ul> التالي:
<ul role="list" className="todo-list stack-large" aria-labelledby="list-heading">
تساعد السمة role
التقنيات المساعدة في توضيح نوع القيمة الدلالية للعنصر أو ما هو الغرض منه، إذ يُعامَل العنصر <ul>
بوصفه قائمةً افتراضيًا، ولكن ستؤدي التنسيقات التي نريد إضافتها إلى تعطيل هذه الوظيفة، ولكن سيعيد هذا الدور معنى القائمة إلى العنصر <ul>
.
تخبر السمة aria-labelledby
التقنيات المساعدة بأننا نتعامل مع العنصر <h2> مع معرِّف id
عنوان القائمة list-heading
بوصفه التسمية التي تشرح الغرض من القائمة الموجودة تحتها، إذ يعطي هذا الارتباط القائمةَ سياقًا مفيدًا، مما يساعد مستخدِمي قارئات الشاشة على فهم الغرض منها بصورة أفضل.
دعم إطار عمل Svelte لسهولة الوصول
يركّز إطار Svelte على سهولة الوصول أو الشمولية Accessibility، والهدف هو تشجيع المطورين على كتابة المزيد من الشيفرة البرمجية الشاملة افتراضيًا، وبما أنّ إطار Svelte يُعَدّ مصرِّفًا، فيمكنه تحليل قوالب HTML بطريقة ساكنة لتوفير تحذيرات متعلقة بسهولة الوصول عند تصريف المكونات.
لا يُعَدّ عملية تطبيق مبادئ سهولة الوصول -التي تُختصَر إلى a11y- أمرًا سهلًا دائمًا، ولكن سيساعدك إطار Svelte من خلال تحذيرك إذا كتبت شيفرة HTML لا تراعي تلك المبادئ، فإذا أضفنا العنصر <img> مثلًا إلى المكوِّن todos.svelte
بدون الخاصية alt
المقابلة له كما يلي:
<h1>Svelte To-Do list</h1> <img height="32" width="88" src="https://www.w3.org/WAI/wcag2A" />
فسيعطي المصرِّف التحذير التالي:
(!) Plugin svelte: A11y: <img> element should have an alt attribute src/components/Todos.svelte 1: <h1>Svelte To-Do list</h1> 2: 3: <img height="32" width="88" src="https://www.w3.org/WAI/wcag2A"> ^ created public/build/bundle.js in 220ms [2020-07-15 04:07:43] waiting for changes…
كما يمكن لمحرر الشيفرة عرض هذا التحذير حتى قبل استدعاء المصرِّف كما يلي:
يمكنك إخبار إطار عمل Svelte بتجاهل هذا التحذير للكتلة التالية من شيفرة HTML بتعليق يبدأ بعبارة svelte-ignore
كما يلي:
<!-- svelte-ignore a11y-missing-attribute --> <img height="32" width="88" src="https://www.w3.org/WAI/wcag2A">
ملاحظة: يمكنك باستخدام المحرّر VSCode إضافة تعليق التجاهل هذا تلقائيًا بالنقر على الرابط "Quick fix…" أو بالضغط على الاختصار Ctrl + .
.
إذا أردت تعطيل هذا التحذير، فيمكنك إضافة المعالج onwarn
إلى الملف rollup.config.js ضمن إعداد الإضافة Svelte
كما يلي:
plugins: [ svelte({ dev: !production, css: css => { css.write('public/build/bundle.css'); }, // Warnings are normally passed straight to Rollup. You can // optionally handle them here, for example to squelch // warnings with a particular code onwarn: (warning, handler) => { // e.g. I don't care about screen readers -> please DON'T DO THIS!!! if (warning.code === 'a11y-missing-attribute') return; // let Rollup handle all other warnings normally handler(warning); } }), ... ]
تُنفَّذ هذه التحذيرات في المصرِّف نفسه حسب التصميم وليس على أساس إضافة يمكن أن تختار إضافتها إلى مشروعك، كما تكمن الفكرة في التحقق من وجود مشاكل سهولة الوصول a11y في الشيفرة افتراضيًا والسماح بإلغاء تحذيرات معينة.
ملاحظة: لا يجب تعطيل هذه التحذيرات إلا إذا كانت لديك أسباب وجيهة لذلك مثل تعطيلها أثناء إنشاء نموذج أولي prototype سريع، إذ يجب أن تجعل صفحاتك قابلةً للوصول إلى أوسع قاعدة ممكنة من المستخدِمين.
قواعد الشمولية التي تحقق منها إطار عمل Svelte مأخوذة من الإضافة eslint-plugin-jsx-a11y، وهي إضافة من ESLint توفِّر فحوصات ساكنة للعديد من قواعد سهولة الوصول على عناصر JSX، كما يهدف إطار Svelte إلى تنفيذ كل من هذه القواعد في مصرِّفه، وقد نُقِل معظمها إلى Svelte فعليًا، بالإضافة إلى أنه يمكنك على GitHub معرفة فحوصات الشمولية التي لا تزال مفقودة، ويمكنك التحقق من معنى كل قاعدة من خلال النقر على رابطها الخاص.
تنسيق التطبيق
لنجعل قائمة المهام تبدو أفضل قليلًا، لذا استبدل محتويات الملف public/global.css بما يلي:
/* RESETS */ *, *::before, *::after { box-sizing: border-box; } *:focus { outline: 3px dashed #228bec; outline-offset: 0; } html { font: 62.5% / 1.15 sans-serif; } h1, h2 { margin-bottom: 0; } ul { list-style: none; padding: 0; } button { border: none; margin: 0; padding: 0; width: auto; overflow: visible; background: transparent; color: inherit; font: inherit; line-height: normal; -webkit-font-smoothing: inherit; -moz-osx-font-smoothing: inherit; -webkit-appearance: none; } button::-moz-focus-inner { border: 0; } button, input, optgroup, select, textarea { font-family: inherit; font-size: 100%; line-height: 1.15; margin: 0; } button, input { overflow: visible; } input[type="text"] { border-radius: 0; } body { width: 100%; max-width: 68rem; margin: 0 auto; font: 1.6rem/1.25 Arial, sans-serif; background-color: #f5f5f5; color: #4d4d4d; } @media screen and (min-width: 620px) { body { font-size: 1.9rem; line-height: 1.31579; } } /*END RESETS*/ /* GLOBAL STYLES */ .form-group > input[type="text"] { display: inline-block; margin-top: 0.4rem; } .btn { padding: 0.8rem 1rem 0.7rem; border: 0.2rem solid #4d4d4d; cursor: pointer; text-transform: capitalize; } .btn.toggle-btn { border-width: 1px; border-color: #d3d3d3; } .btn.toggle-btn[aria-pressed="true"] { text-decoration: underline; border-color: #4d4d4d; } .btn__danger { color: #fff; background-color: #ca3c3c; border-color: #bd2130; } .btn__filter { border-color: lightgrey; } .btn__primary { color: #fff; background-color: #000; } .btn__primary:disabled { color: darkgrey; background-color:#565656; } .btn-group { display: flex; justify-content: space-between; } .btn-group > * { flex: 1 1 49%; } .btn-group > * + * { margin-left: 0.8rem; } .label-wrapper { margin: 0; flex: 0 0 100%; text-align: center; } .visually-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px 1px 1px 1px); clip: rect(1px, 1px, 1px, 1px); white-space: nowrap; } [class*="stack"] > * { margin-top: 0; margin-bottom: 0; } .stack-small > * + * { margin-top: 1.25rem; } .stack-large > * + * { margin-top: 2.5rem; } @media screen and (min-width: 550px) { .stack-small > * + * { margin-top: 1.4rem; } .stack-large > * + * { margin-top: 2.8rem; } } .stack-exception { margin-top: 1.2rem; } /* END GLOBAL STYLES */ .todoapp { background: #fff; margin: 2rem 0 4rem 0; padding: 1rem; position: relative; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2.5rem 5rem 0 rgba(0, 0, 0, 0.1); } @media screen and (min-width: 550px) { .todoapp { padding: 4rem; } } .todoapp > * { max-width: 50rem; margin-left: auto; margin-right: auto; } .todoapp > form { max-width: 100%; } .todoapp > h1 { display: block; max-width: 100%; text-align: center; margin: 0; margin-bottom: 1rem; } .label__lg { line-height: 1.01567; font-weight: 300; padding: 0.8rem; margin-bottom: 1rem; text-align: center; } .input__lg { padding: 2rem; border: 2px solid #000; } .input__lg:focus { border-color: #4d4d4d; box-shadow: inset 0 0 0 2px; } [class*="__lg"] { display: inline-block; width: 100%; font-size: 1.9rem; } [class*="__lg"]:not(:last-child) { margin-bottom: 1rem; } @media screen and (min-width: 620px) { [class*="__lg"] { font-size: 2.4rem; } } .filters { width: 100%; margin: unset auto; } /* Todo item styles */ .todo { display: flex; flex-direction: row; flex-wrap: wrap; } .todo > * { flex: 0 0 100%; } .todo-text { width: 100%; min-height: 4.4rem; padding: 0.4rem 0.8rem; border: 2px solid #565656; } .todo-text:focus { box-shadow: inset 0 0 0 2px; } /* CHECKBOX STYLES */ .c-cb { box-sizing: border-box; font-family: Arial, sans-serif; -webkit-font-smoothing: antialiased; font-weight: 400; font-size: 1.6rem; line-height: 1.25; display: block; position: relative; min-height: 44px; padding-left: 40px; clear: left; } .c-cb > label::before, .c-cb > input[type="checkbox"] { box-sizing: border-box; top: -2px; left: -2px; width: 44px; height: 44px; } .c-cb > input[type="checkbox"] { -webkit-font-smoothing: antialiased; cursor: pointer; position: absolute; z-index: 1; margin: 0; opacity: 0; } .c-cb > label { font-size: inherit; font-family: inherit; line-height: inherit; display: inline-block; margin-bottom: 0; padding: 8px 15px 5px; cursor: pointer; touch-action: manipulation; } .c-cb > label::before { content: ""; position: absolute; border: 2px solid currentColor; background: transparent; } .c-cb > input[type="checkbox"]:focus + label::before { border-width: 4px; outline: 3px dashed #228bec; } .c-cb > label::after { box-sizing: content-box; content: ""; position: absolute; top: 11px; left: 9px; width: 18px; height: 7px; transform: rotate(-45deg); border: solid; border-width: 0 0 5px 5px; border-top-color: transparent; opacity: 0; background: transparent; } .c-cb > input[type="checkbox"]:checked + label::after { opacity: 1; }
يبدو كل شيء الآن أفضل كما يلي:
يمكنك الوصول إلى نسختك من مستودعنا على النحو التالي لمعرفة حالة الشيفرة، كما يجب أن تكون في نهاية هذا المقال:
cd mdn-svelte-tutorial/03-adding-dynamic-behavior
أو يمكنك تنزيل محتوى المجلد مباشرةً باستخدام الأمر التالي:
npx degit opensas/mdn-svelte-tutorial/03-adding-dynamic-behavior
تذكَّر تشغيل الأمر npm install && npm run dev
لبدء تشغيل تطبيقك في وضع التطوير، فإذا أردت متابعتنا، فابدأ بكتابة الشيفرة باستخدام الأداة REPL.
الخلاصة
بدأ تطبيق قائمة المهام في التبلور مع تطبيق شيفرة HTML وشيفرة التنسيق CSS، وأصبح كل شيء جاهزًا لنتمكّن من التركيز على الميزات التي يجب تطبيقها.
ترجمة -وبتصرُّف- للمقال Starting our Svelte to-do list app.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.