الجدول
يعرض بيانات لها صفوف وأعمدة، بأعمدة مسمّاة، ومعه اختياريًّا ترويسات صفوف وشرح وصفوف تذييل. يُمرَّر داخل صندوقه حين لا يتسع له المكان. تستطيع ترويسة العمود أن تطلب الصفوف بترتيبه، ويمكن اختيار الصفوف؛ أما الجدول نفسه فلا يفرز أبدًا.
- الفئة
- الاحتواء
- الرموز
comp.table- المستوى
- أساسي
- Web
tanrei-tableschedule - SwiftUI
TanreiTableradio_button_unchecked - Jetpack Compose
TanreiTableradio_button_unchecked - Flutter
TanreiTableradio_button_unchecked - React Native
Tableradio_button_unchecked
مولَّد من docs/status/status.json, specs/table.spec.json
ترويسات صفوف، وأرقام في النهاية، ثم صف تذييل
| الحالة | Web | SwiftUI | Compose | Flutter |
|---|---|---|---|---|
| مخطَّط | 2 | 6 | 6 | 6 |
| Alpha | 31 | 30 | 30 | 29 |
| Beta | 4 | 1 | 1 | 2 |
| الإجمالي | 37 | 37 | 37 | 37 |
الجدول لبيانات لها بُعدان: قائمة أسعار، أو مقارنة بين خطط، أو قائمة إعدادات مع أنواعها وقيمها الافتراضية، أو حالة عدة أشياء على عدة منصات، أو السجلات التي يعمل عليها الشخص. تستطيع ترويسة العمود أن تطلب الصفوف بترتيبه، والجدول الذي بلا صفوف يعرض حالتك الفارغة، وسطر حالة تحت الصفوف يحمل جملتك ومكوّن التنقل بين الصفحات الخاص بك. ويمكن اختيار الصفوف، ولكل صف خانة اختيار، أو اختيار صف واحد بزر اختيار، والجدول الذي ينتظر صفوفه يقول إنه مشغول. أما الجدول نفسه فلا يفرز ولا يصفّي ولا يجلب ولا يقسّم إلى صفحات أبدًا: يرسم الصفوف التي تُعطى له، بالترتيب الذي تُعطى به. ولا يغيّر عرض الأعمدة.
على الويب هو <tanrei-table> حول عنصر <table> الأصلي الذي تكتبه أنت. ليس للعنصر جذر ظل (shadow root)، والجدول لا يُعطى قيمة display أخرى أبدًا، فالزاحف، والنسخ واللصق في جدول بيانات، وتنقّل قارئ الشاشة في الجدول، كلها تلقى جدول HTML عاديًّا. والمنصات الأخرى لم تنفّذه بعد.
<link rel="stylesheet" href="@tanrei/web/table.css" />
<tanrei-table row-headers>
<table>
<caption>الزيارات بحسب المنطقة</caption>
<thead>
<tr>
<th scope="col">المنطقة</th>
<th scope="col" data-align="end" data-wrap="false">يناير</th>
</tr>
</thead>
<tbody>
<tr><th scope="row">الجزر</th><td data-align="end">212</td></tr>
</tbody>
<tfoot>
<tr><th scope="row">كل المناطق</th><td data-align="end">6,846</td></tr>
</tfoot>
</table>
</tanrei-table>
<script type="module">
import '@tanrei/web/components/table';
</script>import { Table } from '@tanrei/react';
import '@tanrei/web/table.css';
<Table density="compact" striped label="رموز الجدول">
<table>
<thead>
<tr><th scope="col">الرمز</th><th scope="col">النوع</th></tr>
</thead>
<tbody>
{tokens.map((token) => (
<tr key={token.name}><td data-wrap="false">{token.name}</td><td>{token.type}</td></tr>
))}
</tbody>
</table>
</Table>الملف @tanrei/web/table.css يرسم الجدول كله قبل أن يعمل أي سكربت. والعنصر يضيف ورقة الأنماط نفسها إلى الصفحة من تلقاء نفسه، فالصفحة التي تحمّل السكربت وحده تحصل هي أيضًا على جدول مرسوم، بعد لحظة.
| الجزء | على الويب |
|---|---|
| عمود | عنصر <th scope="col"> في الصف الوحيد من <thead>. ونصه هو الترويسة. |
| محاذاة عمود | القيمة data-align="start" أو "center" أو "end" على خلية الترويسة. والقيمة end للأرقام التي تُقارَن. |
| عمود لا يلتفّ نصه | الخاصية data-wrap="false" على خلية الترويسة، للمعرّفات والتواريخ والأرقام. |
| صف | عنصر <tr> في <tbody>. أما صف التذييل فعنصر <tr> في <tfoot>. |
| خلية | عنصر <td>. تحمل نصًّا، أو مكوّنًا، أو لا شيء. |
| ترويسة صف | عنصر <th scope="row"> خليةً أولى، مع row-headers على العنصر. |
| الاسم | عنصر <caption>، أو label على العنصر حيث يسمّيه أصلًا عنوان فوق الجدول. |
| عمود قابل للفرز | القيمة aria-sort="none" أو "ascending" أو "descending" على خلية الترويسة، وعنصر <button type="button"> حول نصها. والخاصية data-value على الخلية هي الاسم الذي يحمله الحدث sort. |
| بلا صفوف | عنصر <tr data-part="empty"> فيه عنصر <td colspan> واحد داخل <tbody>، يحمل حالتك الفارغة. |
| سطر الحالة | عنصر <div data-part="status"> بعد الجدول، يحمل عنصر <p role="status"> ومكوّن التنقل بين الصفحات الخاص بك. والخاصية status على العنصر تكتب الفقرة حيث لا تكتب أنت فقرة. |
| صفوف يمكن اختيارها | القيمة selection="multiple" على العنصر، أو selection="single" حيث يُختار صف واحد على الأكثر، وخلية أولى <td data-part="selection"> في كل صف. |
| صف يمكن اختياره | الخاصية data-value على العنصر <tr>، وعنصر <tanrei-checkbox label-hidden> في خلية الاختيار الخاصة به، أو عنصر <tanrei-radio-button label-hidden> مع single. والخاصية data-selected على العنصر <tr> تقول إنه مختار. |
| خانة الاختيار لكل الصفوف | عنصر <tanrei-checkbox label-hidden> في خلية الاختيار من صف الترويسة. والخاصية select-all-label على العنصر تسمّيها. ومع single تبقى هذه الخلية فارغة. |
| جدول يُحمَّل | الخاصية loading على العنصر. |
| صف نائب، لصف لم يصل بعد | عنصر <tr data-placeholder aria-hidden="true">، خلاياه فارغة أو فيها عنصر نائب من هيكل التحميل. |
ينقل العنصر محاذاة العمود والتفافه من خلية ترويسته إلى كل خلايا العمود. ولتُرسما قبل أن يعمل السكربت، كرِّر data-align و data-wrap على كل خلية، كما تفعل العروض التوضيحية في هذه الصفحة.
اضغط ترويسة: الجدول يطلب، وهذه الصفحة ترتّب الصفوف
| ملاحظة | |||
|---|---|---|---|
| غلاية | 15 | 38.00 | للمواقد الحثّية والغازية |
| مطحنة يدوية | 8 | 64.00 | |
| ميزان | 0 | 29.50 | |
| ورق ترشيح | 120 | 4.50 | يُباع في عبوات من مئة ورقة |
العمود القابل للفرز خلية ترويسة تقول الترتيب الذي عليه الصفوف، وحول نصها زر من عندك:
<th scope="col" aria-sort="ascending" data-value="name"><button type="button">الاسم</button></th>
<th scope="col" aria-sort="none" data-value="price" data-align="end"><button type="button">السعر</button></th>ورقة الأنماط تجعل خلية الترويسة كلها هي الزر وترسم العلامة من aria-sort: سهمًا باتجاهين ما دامت الصفوف ليست بترتيب العمود، وسهمًا للأعلى ما دامت تتصاعد به، وسهمًا للأسفل ما دامت تتنازل به. لا تكتب أنت أيقونة. حمِّل @tanrei/web/icons.css وخط الأيقونات، لأن العلامات رسوم من مجموعة الأيقونات.
الجدول لا يفرز أبدًا. عند الضغط يصدر الحدث sort ولا يتغيّر شيء على الشاشة؛ أنت ترتّب صفوفك وتكتب قيمة aria-sort الجديدة:
table.addEventListener('sort', (event) => {
const { column, index, direction } = event.detail; // 'price', 2, 'ascending' | 'descending'
showRows(sortedBy(column, direction)); // بياناتك، ومقارنتك، أو خادمك
for (const th of headerCells) th.setAttribute('aria-sort', th.dataset.value === column ? direction : 'none');
});الخاصية direction هي ما طلبه الشخص: القيمة ascending، أو descending حيث تتصاعد الصفوف أصلًا بذلك العمود. ليست هناك ضغطة ثالثة تمحو الترتيب. ولست ملزمًا بالترتيب المطلوب: لك أن تضع الصفوف بترتيب آخر، أو أن تتركها بلا ترتيب.
لجدول يرتّبه الخادم، ضع رابطًا مكان الزر: <a href="?sort=price&order=asc">السعر</a>، إلى الجدول نفسه بالترتيب الآخر. يعمل من دون سكربت. وعندئذ يصدر الحدث sort قبل أن يتبع المتصفح الرابط، واستدعاء event.preventDefault() يُبقي الصفحة.
بلا صفوف: حالتك الفارغة تحت صف الترويسة
| الاسم | المخزون | السعر |
|---|---|---|
اكتب صفًّا واحدًا من خلية واحدة تمتد على الأعمدة، وضع فيه حالتك الفارغة:
<tbody>
<tr data-part="empty">
<td colspan="3"><tanrei-empty-state headline="لا توجد منتجات تطابق عوامل التصفية هذه">…</tanrei-empty-state></td>
</tr>
</tbody>يعطيها الجدول حيّزًا فوقها وتحتها وعرض صندوقه، ويُبقيها ظاهرة ما دامت الأعمدة تُمرَّر أفقيًّا. لا يضيف كلمة من عنده. ومن دون صف كهذا، يكون الجدول الذي بلا صفوف ترويسته وحدها.
جملتك ومكوّن التنقل بين الصفحات الخاص بك، تحت الصفوف
| الإصدار | تاريخ الإصدار | التغييرات |
|---|---|---|
| 2.4.0 | 12 مايو | الجداول تُبقي ترويستها ظاهرة. |
| 2.3.2 | 28 أبريل | إصلاح للتواريخ المكتوبة بسنة من رقمين. |
| 2.3.1 | 20 أبريل | إصلاح لقائمة خيارات كانت تُفتح خارج الشاشة. |
| 2.3.0 | 2 أبريل | اللوحات الجانبية، وحلقة تركيز أهدأ. |
| 2.2.1 | 18 مارس | إصلاح لتلميح كان يبقى مفتوحًا. |
| 2.2.0 | 4 مارس | التخطيطات من اليمين إلى اليسار. |
الإصدارات 1 إلى 6 من 42
بعد الجدول، داخل العنصر، اكتب سطر الحالة: جملتك في فقرة هي حالة (status)، ومكوّن التنقل بين الصفحات الخاص بك.
<tanrei-table>
<table>…</table>
<div data-part="status">
<p role="status">1 إلى 20 من 132</p>
<tanrei-pagination label="صفحات المنتجات" variant="compact" size="small" page="1" count="7" previous-label="الصفحة السابقة" next-label="الصفحة التالية" count-text="الصفحة {page} من {count}"></tanrei-pagination>
</div>
</tanrei-table>تقف الجملة عند حافة البداية والتنقل بين الصفحات عند حافة النهاية؛ وحيث لا يتسع المكان لهما جنبًا إلى جنب، ينزل التنقل بين الصفحات تحت الجملة. لا يُمرَّر سطر الحالة أفقيًّا مع الأعمدة، ويبقى في أسفل الجدول الذي تُمرَّر صفوفه داخل صندوقه. الجدول لا يعدّ شيئًا ولا يكتب كلمة: «1 إلى 20 من 132» لك أنت، بلغتك. وحين تغيّر نص الفقرة، تُخبَر التقنيات المساعدة بتهذيب. والخاصية status على العنصر تكتب تلك الفقرة عنك حيث لا فقرة في ترميزك. أما الفقرة التي كتبتها بنفسك فهي التي تُقرأ، ولا تغيّر الخاصية عندئذ شيئًا.
ما يقف عند حافة النهاية هو التنقل بين الصفحات بالحجم small: بشكله المختصر كما هنا، أو بأرقام الصفحات حيث ينتقل الناس إلى صفحة برقمها. وحيث يختار الناس عدد الصفوف في كل صفحة، ضع قبله عنصر الاختيار الخاص بذلك، والاثنين معًا في عنصر واحد ترتّبه أنت؛ ونطاق الصفوف المعروضة جملتك أنت، لا جزء من التنقل بين الصفحات. أما روابط السابق والتالي فلا مكان لها هنا: فهي تقود إلى صفحات لها عناوين، لا إلى صفوف قائمة واحدة. الجدول يضع ما تعطيه إياه فقط: حين تتغير الصفحة، غيّر أنت الصفوف والجملة.
اختر صفوفًا: الجدول يحتفظ بالاختيار، وهذه الصفحة تعدّه
| الاسم | المخزون | السعر | |
|---|---|---|---|
| ورق ترشيح | 120 | 4.50 | |
| مطحنة يدوية | 8 | 64.00 | |
| غلاية | 15 | 38.00 | |
| ميزان | 0 | 29.50 |
تم تحديد 2 من 4
عيِّن selection="multiple" على العنصر، وابدأ كل صف بخلية اختيار. في صف الترويسة تحمل هذه الخلية خانة الاختيار لكل الصفوف. وفي صف من المتن تحمل خانة اختيار ذلك الصف، ويحمل الصف في data-value الاسم الذي تعرف به السجل:
<tanrei-table selection="multiple" row-headers select-all-label="تحديد كل المنتجات">
<table>
<caption>منتجات يُعاد طلبها</caption>
<thead>
<tr>
<td data-part="selection"><tanrei-checkbox label-hidden></tanrei-checkbox></td>
<th scope="col">الاسم</th>
<th scope="col" data-align="end">السعر</th>
</tr>
</thead>
<tbody>
<tr data-value="grinder" data-selected>
<td data-part="selection"><tanrei-checkbox label-hidden checked></tanrei-checkbox></td>
<th scope="row">مطحنة يدوية</th>
<td data-align="end">64.00</td>
</tr>
</tbody>
</table>
<div data-part="status"><p role="status">تم تحديد 1 من 4</p></div>
</tanrei-table>الجدول هو من يحتفظ بالاختيار. حين يغيّر الشخص خانة اختيار، يكتب العنصر data-selected على ذلك الصف، ويُبقي خانة الاختيار في صف الترويسة مطابقة، ويصدر عنه الحدث selectionchange مرة واحدة ومعه قيم كل الصفوف المختارة، بترتيب الصفوف:
table.addEventListener('selectionchange', (event) => {
if (!(event instanceof CustomEvent)) return; // انظر الملاحظة أدناه
const { values } = event.detail; // ['grinder', 'scale']
// جملتك، بلغتك، في الفقرة التي كتبتها
table.querySelector('[role="status"]').textContent = `تم تحديد ${values.length} من ${total}`;
});- لتختار صفًّا بنفسك، اكتب
data-selectedعلى عنصر<tr>الخاص به؛ وأزِل الخاصية فلا يبقى الصف مختارًا. العنصر يحدّد خانة اختيار الصف أو يلغي تحديدها ولا يصدر عنه شيء. - كون الصف مختارًا أو غير مختار مكتوب على الصف نفسه (
data-selected). الصفوف التي تعطيها من جديد بترتيب آخر تبقى مختارة أو غير مختارة، والصف الذي يُعطى للجدول أول مرة لا يكون مختارًا إلا حيث يقول هو ذلك عن نفسه. - الصف الذي بلا
data-valueلا يمكن اختياره. اترك خلية الاختيار فيه فارغة، كما في صف تذييل. - خانة الاختيار في صف الترويسة محدَّدة ما دامت كل الصفوف مختارة، وفي حالة غير محسومة ما دام بعضها مختارًا، ومعطَّلة ما دام لا صف يمكن اختياره. وهي تمثّل الصفوف التي أُعطيت للجدول، لا صفوف الصفحات الأخرى أبدًا.
- عمود الاختيار ليس عمود بيانات. الخاصيتان
row-headersوsticky-first-columnتعنيان العمود الذي بعده، وقيمةindexفي الحدثsortلا تعدّه. - الضغط في أي موضع من صف يمكن اختياره يغيّر خانة الاختيار فيه، كما يفعل الضغط على الخانة نفسها. الرابط والزر وحقل الإدخال في خلاياه تحتفظ بضغطها، والسحب وتحديد النص والنقرة الثانية من النقر المزدوج والنقر مع مفتاح تعديل لا تغيّر شيئًا. يُرسم الصف كله في حالتي التحويم والضغط كوحدة واحدة، وتبقى لوحة المفاتيح على خانة الاختيار.
للمتصفح حدث خاص به اسمه selectionchange، لتغيّر تحديد النص، وحقل النص الذي في خلية يرسله عبر الجدول. حدث الجدول من النوع CustomEvent ويحمل detail.values؛ وحدث المتصفح ليس كذلك. افحص ذلك، كما يفعل المثال.
لصفحة يجب أن تعمل من دون سكربت، استخدم خانات اختيار أصلية. الصف الذي خانة اختياره checked ترسمه ورقة الأنماط وحدها مختارًا، وتُرسَل خانات الاختيار مع نموذجك:
<tr data-value="grinder">
<td data-part="selection"><input type="checkbox" name="product" value="grinder" checked aria-label="تحديد مطحنة يدوية" /></td>
<th scope="row">مطحنة يدوية</th>
</tr>اختيار صف واحد: زر اختيار في كل صف، ولا شيء في الترويسة
| الخطة | المقاعد | شهريًا | |
|---|---|---|---|
| المبتدئة | 3 | 0.00 | |
| الفريق | 20 | 48.00 | |
| الأعمال | 100 | 180.00 |
حيث يختار الشخص سجلًا واحدًا (الخطة التي ينتقل إليها، أو عنوان الشحن، أو البنية التي يقارن بها)، عيِّن selection="single". تحمل خلية الاختيار في كل صف من صفوف المتن زر اختيار بدل خانة الاختيار، وتبقى خلية الاختيار في صف الترويسة فارغة، لأنه لا عنصر تحكم ينوب عن كل الصفوف:
<tanrei-table selection="single" row-headers>
<table>
<caption>الخطة التي ستنتقل إليها</caption>
<thead>
<tr><td data-part="selection"></td><th scope="col">الخطة</th><th scope="col" data-align="end">المقاعد</th></tr>
</thead>
<tbody>
<tr data-value="team" data-selected>
<td data-part="selection"><tanrei-radio-button label-hidden name="plan" checked></tanrei-radio-button></td>
<th scope="row">الفريق</th>
<td data-align="end">20</td>
</tr>
</tbody>
</table>
</tanrei-table>- اختيار صف يُلغي اختيار الصف الذي كان مختارًا. ويحمل
selectionchangeقيمة واحدة:{ values: ['team'] }. - الضغط على الصف المختار، أو على زر اختياره، لا يغيّر شيئًا. فزر الاختيار لا يُلغى بالضغط عليه مرة أخرى. وحيث يمكن للشخص أن يعود إلى عدم اختيار أي صف، ضع لذلك عنصر تحكم من عندك، وأزِل
data-selectedمن الصف. - أزرار الاختيار في الجدول الواحد تتشارك اسمًا واحدًا، فهي مجموعة واحدة: موضع توقف واحد في ترتيب التركيز، ومفاتيح الأسهم تنقل الاختيار من صف إلى صف. أعطها اسمك أنت، وهو ما يرسله النموذج؛ والعنصر يعطي اسمًا لما ليس له اسم.
- إن علَّمت أكثر من صف بالخاصية
data-selected، فالمختار هو الصف الذي علَّمته أخيرًا، ومن الصفوف المعلَّمة معًا أولها. ويُبلَّغ في بنيات التنقيح عن الصفوف المعلَّمة معًا. - وكل ما سوى ذلك كما مع خانات الاختيار: الضغط في أي مكان من الصف يختاره، والخاصية
data-selectedتكتبها أنت، والعنصر الأصلي<input type="radio" name="…">يعمل بلا سكربت.
التحميل: صفوف نائبة، وسطر الحالة يقول ذلك
| العميل | تاريخ الطلب | الإجمالي |
|---|
جارٍ التحميل
عيِّن loading ما دمت تجلب الصفوف. يُعرَض الجدول للتقنيات المساعدة مشغولًا ويرسم ما تعطيه إياه في أثناء ذلك. لا يجلب شيئًا ولا ينتظر شيئًا: هو في حالة تحميل ما دمت أنت تقول ذلك.
<tanrei-table loading>
<table>
<thead>…</thead>
<tbody>
<tr data-placeholder aria-hidden="true">
<td><span class="tanrei-skeleton-placeholder"></span></td>
<td data-align="end"><span class="tanrei-skeleton-placeholder" style="inline-size: 3rem; margin-inline-start: auto"></span></td>
</tr>
</tbody>
</table>
<div data-part="status"><p role="status">جارٍ التحميل</p></div>
</tanrei-table>- للتحميل الأول، أعطِ صفوفًا نائبة:
<tr data-placeholder aria-hidden="true">. ارتفاع الصف النائب لا يقل عن سطر واحد من النص، وليس فيه خانة اختيار، وهو مخفي عن التقنيات المساعدة. - اترك خلايا الصف النائب فارغة، أو ضع في كل منها عنصرًا نائبًا من هيكل التحميل (
class="tanrei-skeleton-placeholder"، من@tanrei/web/skeleton.css). ارتفاع العنصر النائب ارتفاع سطر واحد من نص الخلية، فيكون الصف بارتفاع الصف الذي يحل محله. والصنف وحده يكفي هنا: الجدول يقول أصلًا إنه مشغول. - لصفحة أخرى أو ترتيب آخر، أبقِ الصفوف التي كانت في الجدول وعيِّن
loading. الصفوف التي كان الشخص يقرؤها تبقى قابلة للقراءة. - لا يُخفَّت شيء ولا يُعطَّل شيء. الترويسات وخانات الاختيار تعمل والجدول يُحمَّل.
- ما دامت
loadingمعيَّنة، لا يُرسم الصف الذي فيه الحالة الفارغة، حتى إن لم يكن هناك صف آخر.
محوِّل Markdown يكتب عنصر <table> مجرَّدًا. غلِّفه وقت البناء؛ لا ينقل Tanrei محتواك وقت التشغيل. ومع rehype هي خطوة واحدة صغيرة:
import { visit, SKIP } from 'unist-util-visit';
/** يغلّف كل جدول في المستند بالعنصر <tanrei-table>. */
export function rehypeTanreiTable() {
return (tree) =>
visit(tree, 'element', (node, index, parent) => {
if (node.tagName !== 'table' || parent?.tagName === 'tanrei-table') return;
parent.children[index] = { type: 'element', tagName: 'tanrei-table', properties: {}, children: [node] };
return SKIP;
});
}القيمة align="right" التي يكتبها المحوِّل نفسه على خلية تُقرأ end، والقيمة align="left" تُقرأ start، فتصير «الأرقام إلى اليمين» «الأرقام إلى النهاية» في صفحة من اليمين إلى اليسار.
مضغوط وبصفوف مظلَّلة بالتناوب، لجدول يمسحه الشخص بنظره
| الرمز | يشير إلى | النوع |
|---|---|---|
| comp.table.separation | sys.behavior.separation | number |
| comp.table.divider.color | sys.color.outline-variant | color |
| comp.table.divider.thickness | sys.border.hairline | dimension |
| comp.table.row.gap | sys.space.space50 | dimension |
| comp.table.stripe.container.color | sys.color.surface-container-low | color |
| comp.table.caption.gap | sys.space.space100 | dimension |
أعرض من صندوقه: يُمرَّر هناك، ويبقى العمود الأول
| المنطقة | يناير | فبراير | مارس | أبريل | مايو | يونيو | يوليو |
|---|---|---|---|---|---|---|---|
| المنطقة الشمالية | 1,204 | 1,310 | 1,288 | 1,402 | 1,396 | 1,450 | 1,512 |
| المنطقة الوسطى | 3,020 | 2,985 | 3,110 | 3,204 | 3,190 | 3,275 | 3,301 |
| المنطقة الجنوبية | 870 | 902 | 915 | 899 | 940 | 968 | 1,002 |
| الجزر | 212 | 220 | 198 | 240 | 251 | 263 | 270 |
ارتفاع يحدّده التخطيط: تُمرَّر الصفوف، وتبقى الترويسة
| الإصدار | تاريخ الإصدار | التغييرات |
|---|---|---|
| 2.4.0 | 12 مايو | الجداول تُبقي ترويستها ظاهرة. |
| 2.3.2 | 28 أبريل | إصلاح للتواريخ المكتوبة بسنة من رقمين. |
| 2.3.1 | 20 أبريل | إصلاح لقائمة خيارات كانت تُفتح خارج الشاشة. |
| 2.3.0 | 2 أبريل | اللوحات الجانبية، وحلقة تركيز أهدأ. |
| 2.2.1 | 18 مارس | إصلاح لتلميح كان يبقى مفتوحًا. |
| 2.2.0 | 4 مارس | التخطيطات من اليمين إلى اليسار. |
| 2.1.0 | 11 فبراير | منتقي التاريخ والوقت. |
البنية
| الجزء | الوصف |
|---|---|
1 container | The table's box: as wide as its layout gives it, as tall as its rows unless the layout limits it. It has no fill, edge or corner of its own. When the columns or the rows do not fit, it is the box they scroll in, and then the one part of the table that takes focus. |
2 caption · اختياري | The name of the table, drawn above its first row at the start edge. It does not scroll sideways with the columns. |
3 header-row · اختياري | The first row: one header cell for each column. It can stay in view while the rows scroll under it. |
4 header-cell · اختياري | The name of a column, in a label style and a quieter color than the data. |
5 sort-button · اختياري | The whole header cell of a column that can be sorted, as one button: the column's name and its mark. It carries hover, focus and press, and asks the product for the rows in the column's order. |
6 sort-mark · اختياري | The glyph beside the name of a column that can be sorted: a two-way arrow while the rows are not in its order, an arrow up while they ascend by it, an arrow down while they descend. |
7 row | One record: a cell for each column, as tall as its tallest cell. A row that can be chosen is, for a pointer, one target that presses its checkbox or radio button, with its own hover and press (TBL-58, TBL-59). |
8 row-header · اختياري | The first cell of a row where the first column names its rows: the cell style at its emphasized weight. The first column can stay in view while the others scroll under it. |
9 selection-cell · اختياري | The cell before the first column of a table whose rows can be chosen: in a body row the row's checkbox, or its radio button where one row at most is chosen; in the header row the checkbox for every row, or nothing where one row at most is chosen; in a footer row nothing. |
10 cell | One value: text that wraps inside its column, or a component of the product's (a tag, a link, inline code). It may be empty. |
11 divider · اختياري | The one hairline between two rows, under the header row and above the footer rows, where rows are told apart by a line; also the line along the inner edge of a first column that stays in view, and along an edge of the box beyond which there is more to scroll to. |
12 stripe · اختياري | A quiet fill on every second body row. |
13 selected-row · اختياري | The fill of a body row that is chosen, across its whole width, in place of a stripe. |
14 placeholder-row · اختياري | A body row that stands for a record that has not arrived: as tall as a row of one line, holding what the product puts in its cells (a Skeleton), and no checkbox or radio button. |
15 empty · اختياري | What stands in the place of the body rows of a table that has none: the product's own content (an Empty state), as wide as the table's box, under the header row. |
16 footer-row · اختياري | A row after every body row that sums the columns up or notes something about them: the cell style at its emphasized weight. |
17 status · اختياري | One line under the last row, outside the columns: the product's sentence about what the table shows now (the range of rows shown, how many are chosen) at the start edge, and its pagination at the end edge. It does not scroll sideways with the columns. |
الخصائص
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
caption | string | "" | The name of the table, drawn above it ("Response times by region"). It is also what assistive technology calls the table and its scroll region. Prefer it to label wherever the page does not already name the table in sight. |
label | string | "" | The table's accessible name where no caption is drawn, because a heading just above it already says what it is. Not drawn. Ignored where caption is given. |
density | regular | compact | "regular" | How close the rows are. Regular (a 46 row in the default system) is for a table a person reads: a few rows, cells that wrap. Compact (a 32 row, smaller text) is for a table a person scans: hundreds of rows, identifiers and values. A design system may add values. |
separation | auto | divider | gap | "auto" | How a row is told from the row next to it. divider: one hairline between them. gap: empty space and no line. Auto takes comp.table.separation, the design system's answer (divider in the default system). |
striped | boolean | false | Fills every second body row with a quiet color, for a wide table of many similar rows where the eye loses its row on the way across. It can be combined with either separation. |
rowHeaders | boolean | false | Says that the first column names its rows (a component, a region, a plan), so its cells are drawn and exposed as row headers. False for a table whose rows are named by no single column. |
stickyHeader | boolean | false | Keeps the header row at the top of the table's box while its rows scroll under it. It has an effect only where the layout limits the table's height, so that the rows scroll in the table's own box. |
stickyFirstColumn | boolean | false | Keeps the first column at the start edge while the other columns scroll sideways under it, for a wide table whose first column says what each row is. Released by itself where the column would take more than half of the box. |
selection | none | single | multiple | "none" | Whether rows can be chosen, and how many. multiple puts a checkbox before the first column of every body row that has a value, and one in the header row that stands for all of them. single puts a radio button there instead and nothing in the header row: one row at most is chosen, and a person moves the choice from row to row but does not take it back (TBL-60). none draws no such column and ignores a row's selected. |
selectAllLabel | string | "" | The accessible name of the checkbox in the header row that chooses every row ("Select all rows"). Not drawn; read only while selection is multiple. From the product's words where the instance gives none. |
sortedAscendingLabel | string | "" | What a platform that has no sort state of its own says after the name of the column the rows ascend by ("sorted ascending"). Not drawn. A platform that exposes the direction itself (the web) does not read it. |
sortedDescendingLabel | string | "" | What a platform that has no sort state of its own says after the name of the column the rows descend by ("sorted descending"). Not drawn. A platform that exposes the direction itself (the web) does not read it. |
loading | boolean | false | Says that the product is fetching the rows — the first ones, another page of them, or the same ones in another order. The table is exposed as busy and draws the rows it is given meanwhile: the ones it had, or placeholder rows. It fetches nothing itself. |
status | string | "" | The product's sentence about what the table shows now ("1–20 of 132", "3 of 132 selected", "Loading"), drawn under the last row and told politely to assistive technology when it changes. The table counts nothing and writes no word of its own. Empty draws no status text. |
المنافذ
| المنفذ | الوصف |
|---|---|
empty | What the table draws in the place of its body rows while it has none and is not loading: the product's Empty state, or its own sentence. The table gives it the box's width and room above and below, and does nothing else to it. A fixture's string is drawn as plain text in its place. |
pagination | What stands at the end edge of the status line: one element, the product's Pagination — at size small, and compact where the product does not page by number — or a group the product lays out of a Select for the rows per page and that Pagination, as the table paginations of Carbon and MUI are. The range of rows shown ("1–20 of 132") is the status text, not the slot's. It is never Previous and next links, which lead to titled pages and not to the rows of one list. The table places it and does nothing else to it. A fixture's string is drawn as plain text in its place. |
الأحداث
| الحدث | الوصف |
|---|---|
sort | The user pressed the header of a sortable column; carries column (the column's value), index (its place among the columns of data, from 0) and direction (ascending or descending: the one asked for, TBL-34). The table changes nothing: the product orders its rows and sets the column's sort. |
selectionChange | The user changed which rows are chosen — by a row's checkbox or radio button, by a press on the row, by the checkbox for every row, or by an arrow key on a radio button; carries values: the values of the chosen rows after the change, in row order. While selection is single it holds the one chosen row's value. |
العناصر
أنواع العناصر التي يكررها المكوّن. يسرد كل نموذج اختبار عناصره بالترتيب، وتسمّي كل منصة كل نوع بأسلوبها.
column
One column of the table: the text of its header cell, how its cells are aligned and wrapped, and whether its header asks for the rows in its order.
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
header | string | "" | The text of the column's header cell ("Price"). Empty only where the table has no header row at all, and then for every column. |
align | start | center | end | "start" | Where the content of the column's cells sits, header and footer cells included. start for text. end for numbers that are compared, so that their digits line up. center for a mark or a single word that is the same kind of answer in every row. |
wrap | boolean | true | Whether the text of the column's cells may wrap. False keeps every cell on one line, for identifiers, dates, numbers and short values; the column is then as wide as its widest cell. |
sortable | boolean | false | Says that the rows can be put in this column's order: its header cell is a button that asks for it. The table never orders rows itself. It needs a header: a sortable column without one is drawn as a plain column. |
sort | none | ascending | descending | "none" | The order the rows are in by this column, as the product has put them: the header draws it and exposes it. The product sets it after it has ordered its rows; one column at a time is not none. |
value | string | "" | The name the product knows the column by ("price"), carried by the sort event. Not drawn. Empty: the event carries the column's index alone. |
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| العنصر | th | TanreiTableColumn | TanreiTableColumn | TanreiTableColumn | TableColumn |
header · الخاصية | scope | header | header | header | header |
align · الخاصية | data-align | alignment | align | align | align |
wrap · الخاصية | data-wrap | wraps | wrap | wrap | wrap |
sortable · الخاصية | button | isSortable | sortable | sortable | sortable |
sort · الخاصية | aria-sort | sort | sort | sort | sort |
value · الخاصية | data-value | value | value | value | value |
| ملاحظات | A <th scope="col"> in the <thead> row; its text is the header. data-align="end" and data-wrap="false" on that <th> reach every cell of the column: the element writes the positions of such columns on the wrapper (data-columns-end, data-columns-center, data-columns-nowrap) and one rule per position places the cells, so no cell is written to; without script, and from a Markdown renderer, the same attributes (or the renderer's own align attribute, where left and right are read as start and end) sit on each cell. A sortable column: the <th> holds the product's own <button type="button"> (or, for a table ordered by the server, an <a href> to the same table in the other order) around the header's text, and carries aria-sort="none", "ascending" or "descending"; the stylesheet draws the mark from that attribute, so the product writes no glyph. data-value on the <th> is the column's value. | An entry of the columns prop. |
row · مجموعة — تحتوي عناصر
One row of the table: a cell for each column, in the order of the columns; a record that can be chosen where the table's selection says so.
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
footer | boolean | false | Says that the row is a footer row: drawn after every body row, in the emphasized weight, never striped. For sums, averages and notes about the columns. |
value | string | "" | The name the product knows the record by, carried by selectionChange. Not drawn. A body row without a value cannot be chosen: it has no checkbox or radio button. |
selected | boolean | false | Whether the row is chosen. Read only while the table's selection is single or multiple and the row has a value; while it is single, one row at most is chosen (TBL-60). |
selectLabel | string | "" | The accessible name of the row's checkbox or radio button ("Select Islands"): words about this one record, so the instance's. Not drawn. Empty: it is named by the row's header cell where rowHeaders is true. |
placeholder | boolean | false | Says that the row stands for a record that has not arrived. It is drawn as a body row at least one line tall, holds what the product puts in its cells (a Skeleton) or nothing, has no checkbox or radio button and is hidden from assistive technology. |
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| العنصر | tr | TanreiTableRow | row | TanreiTableRow | Table.Row |
footer · الخاصية | tfoot | isFooter | footer | footer | footer |
value · الخاصية | data-value | value | value | value | value |
selected · الخاصية | data-selected | isSelected | selected | selected | selected |
selectLabel · الخاصية | aria-label | selectLabel | selectLabel | selectLabel | selectLabel |
placeholder · الخاصية | data-placeholder | isPlaceholder | placeholder | placeholder | placeholder |
| ملاحظات | A <tr>; a footer row is a <tr> inside <tfoot>. A row that can be chosen has data-value and a first cell <td data-part="selection"> holding the product's checkbox — <tanrei-checkbox label-hidden label="…">, or a native <input type="checkbox" aria-label="…"> that also works in a form without script; selectLabel is that checkbox's own name. data-selected on the <tr> draws the row as chosen; the element keeps it in step with the checkbox, and the stylesheet also reads a native checkbox that is checked, so a row is drawn chosen before and without script. While selection="single" the selection cell holds a radio button instead — <tanrei-radio-button label-hidden name="…" label="…">, or a native <input type="radio" name="…" aria-label="…"> — the radio buttons of one table share one name (the element gives one to those that have none, so that they form one group), and the header row's selection cell is empty. A placeholder row is <tr data-placeholder aria-hidden="true">. | A call in the table's scope: row(footer = false) { cell("…") }. |
cell · مجموعة — تحتوي عناصر
One value of a row: plain text, a component of the product's, or nothing.
| المنفذ | الوصف |
|---|---|
content | What the cell holds in place of plain text: a tag, a link, inline code, a button, a picture, any view of the product's. The table places it and styles nothing inside it (TBL-15). |
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| العنصر | td | TanreiTableCell | cell | TanreiTableCell | Table.Cell |
| ملاحظات | A <td>, or a <th scope="row"> for a row header; its children are the content. | A cell takes a string or a view builder. | cell(text) or cell { content } in a row's scope. | TanreiTableCell.text("…") or TanreiTableCell(child: …). | Its children are a string or elements. |
tag
A tag placed in a cell: the Tag component, as it is drawn anywhere else.
| الخاصية | النوع | القيمة الافتراضية | الوصف |
|---|---|---|---|
label | string | "" | The tag's text. |
variant | tonal | outlined | filled | "tonal" | The tag's look. |
status | default | info | warning | error | success | "default" | What the tag reports. |
size | small | medium | "medium" | The tag's size; small in a table cell, so that it does not make its row taller. |
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| العنصر | tanrei-tag | TanreiTag | TanreiTag | TanreiTag | Tag |
label · الخاصية | label | title | label | label | label |
variant · الخاصية | variant | variant | variant | variant | variant |
status · الخاصية | status | status | status | status | status |
size · الخاصية | size | size | size | size | size |
الحالات
السلوك
متطلبات بصيغة EARS. يُختبر تنفيذ كل منصة مقابل هذه المعرّفات. يُعرض نص المتطلبات كما ورد في ملف المواصفة، بالإنجليزية.
| المعرّف | المتطلب |
|---|---|
TBL-1 | THE table SHALL draw, from top to bottom, its caption where it has one, one header row holding one header cell for each column, its body rows in the order given, and its footer rows after every body row, each row holding one cell for each column in the order of the columns; THE table SHALL be at least as wide as the layout gives it, SHALL draw no fill, no edge, no corner and no elevation of its own around its rows, and SHALL bring no outer space of its own, so that it is spaced by what holds it. |
TBL-2 | THE table SHALL size every column from its content: a column SHALL never be narrower than the widest thing in it that cannot break — a word, a number, a cell of a column whose wrap is false, a component — with the cell's inline paddings; WHILE the columns at those least widths fit the table's box THE table SHALL share the box's width among them in proportion to their content, so that the table fills its box and a column of long text takes what the short columns leave; THE table SHALL NOT give a column a width in a unit of its own, and a width the product gives a column SHALL replace what the content would give it. |
TBL-3 | THE table SHALL wrap the text of a cell inside its column, breaking lines by the rules of the text's language, and SHALL never cut it or end it with an ellipsis; a run with no break opportunity (an identifier, an address) SHALL NOT be broken inside and widens its column instead (TBL-2, TBL-17); WHERE a column's wrap is false THE table SHALL keep every cell of that column, its header cell included, on one line. |
TBL-4 | THE table SHALL pad every cell at the top and bottom by comp.table.cell.padding-block.regular and at the start and end by comp.table.cell.padding-inline.regular while density is regular, and by comp.table.cell.padding-block.compact and comp.table.cell.padding-inline.compact while density is compact, the first and the last cell of a row included; SHALL set a cell's text in comp.table.cell.text.typescale.regular or comp.table.cell.text.typescale.compact by density, in comp.table.cell.text.color, with digits in figures of equal width so that the numbers of a column line up; SHALL make a row as tall as its tallest cell and no taller, with no height of its own; and SHALL align the cells of a row to the top, so that the first lines of a row share a baseline. |
TBL-5 | THE table SHALL set a column's header cell in comp.table.header.text.typescale.regular or comp.table.header.text.typescale.compact by density, in comp.table.header.text.color, with the paddings of a cell (TBL-4), and SHALL fill the header row with comp.table.header.container.color; the header cells of a row SHALL align to the bottom, so that the last line of each sits next to the data it names. IF every column's header is empty THE table SHALL draw no header row and no line for it, and the table then relies on its row headers (TBL-6). |
TBL-6 | WHILE rowHeaders is true THE table SHALL treat the first cell of every body row and of every footer row as the header of its row: it SHALL draw that cell in the cell's text style at its emphasized weight — the style's weight plus sys.typescale.emphasis.body — in comp.table.cell.text.color, with a cell's paddings and no fill of its own, and SHALL expose it as a row header (TBL-24); WHILE rowHeaders is false THE table SHALL draw and expose the first column as cells, except that a first cell the product's own content already marks as the header of its row (on the web a th whose scope is row) SHALL be drawn and exposed as one whatever rowHeaders says: rowHeaders adds row headers and takes none away. WHILE selection is multiple or single the first column is the first column of data, after the selection column (TBL-42). |
TBL-7 | THE table SHALL align the content of every cell of a column, its header cell and its footer cells included, as the column's align names — start: at the start edge of the cell; end: at the end edge, for numbers that are compared digit by digit; center: in the middle, for a mark or a single word that is the same kind of answer in every row — and SHALL align every line of a wrapped cell the same way; a value of align the table does not define SHALL be treated as start. |
TBL-8 | WHILE the table's separation is divider (TBL-9) THE table SHALL draw one line of comp.table.divider.thickness in comp.table.divider.color across its whole width under the header row, between every two rows that follow each other, and between the last body row and the first footer row, without adding to a row's height or moving its content; it SHALL draw no line above the first row or below the last, and no line between columns. |
TBL-9 | THE table SHALL tell a row from the row next to it as its separation names: WHILE separation is auto, as comp.table.separation names, which follows sys.behavior.separation unless a design system sets it apart; WHILE separation is divider or gap, as that value names, whatever the tokens say; divider draws the lines of TBL-8 and gap leaves the space of TBL-10. WHEN separation, or the token it follows, changes THE table SHALL draw the other form at once, without animation, keeping its scroll position; WHEN separation names a value the table does not define THE table SHALL treat it as auto and in debug builds report the unknown value. A platform that cannot draw a choice SHALL fall back to the first value, divider, and state that in its README. |
TBL-10 | WHILE the table's separation is gap (TBL-9) THE table SHALL leave comp.table.row.gap of empty space under the header row, between every two rows that follow each other, and between the last body row and the first footer row, SHALL draw none of the lines of TBL-8, and SHALL leave no such space above the first row or below the last; the rows SHALL stay exposed as rows (TBL-24). |
TBL-11 | WHILE striped is true THE table SHALL fill every second body row — the second, the fourth and so on, counted among the body rows as they are shown — with comp.table.stripe.container.color across the row's whole width, paddings included, and SHALL fill neither the header row nor a footer row with it; the fill SHALL change nothing else about the row, and the row separation of TBL-9 SHALL be drawn as without it. |
TBL-12 | WHERE caption is not empty THE table SHALL draw it above the header row, at the start edge of the table's box, in comp.table.caption.text.typescale and comp.table.caption.text.color, wrapping inside the box's width, comp.table.caption.gap above the first row; the caption SHALL stay where it is while the columns scroll sideways (TBL-17) and SHALL be the table's accessible name (TBL-23). |
TBL-13 | WHERE a row's footer is true THE table SHALL draw it after every body row, in the order given, as a footer row: its cells in the cell's text style at its emphasized weight — the style's weight plus sys.typescale.emphasis.body — in comp.table.cell.text.color, aligned as their columns are, never striped, and exposed as rows of the table that follow its body; IF a row whose footer is false is given after a footer row THE table SHALL still draw every body row before every footer row and in debug builds report the order. |
TBL-14 | WHERE a cell has no content THE table SHALL draw nothing in it, keep its place and its paddings so that the cells after it stay in their columns, and expose it as an empty cell; THE table SHALL NOT put a dash, a word or any other mark of its own in it. IF a row holds fewer cells than the table has columns THE table SHALL keep the row's place and its height and leave the places of the missing cells empty at the row's end: a platform that builds its rows SHALL draw them as empty cells, and a platform that draws the product's own rows (the web) SHALL write no cell into them, so that there the row's line and a stripe end at its last cell, and in debug builds SHALL report the row; IF it holds more THE table SHALL draw only as many as it has columns and in debug builds report the row. |
TBL-15 | WHERE a cell holds a component or any content of the product's that is not plain text — a tag, a link, inline code, a button, a picture — THE table SHALL place it by the column's alignment (TBL-7) on the cell's first line, SHALL add nothing to it and style nothing inside it, and SHALL let pointer, touch and keyboard input reach it as it would outside the table, in the reading order of the cells. |
TBL-16 | THE table and its cells SHALL NOT take pointer, touch or keyboard input of their own and SHALL NOT change anything they draw under a pointer, a press or focus, and a row SHALL NOT either except as TBL-58 and TBL-59 say: no row has a focused state, is a stop in the focus order or takes a key; a row that cannot be chosen — every row of a table whose selection is none, the header row, a footer row, a placeholder row and a body row without a value — has no hovered or pressed state and is not pressed as a whole; and a cell is not a stop in the focus order. Four parts of the table take input, each by its own sentences: the header cell of a sortable column, which is a button (TBL-32 to TBL-41); the checkboxes of a table whose selection is multiple, which are the Checkbox component, and the radio buttons of a table whose selection is single, which are the Radio button component (TBL-42 to TBL-47, TBL-60, TBL-61); a body row that can be chosen, which a pointer presses as its checkbox or radio button (TBL-58, TBL-59); and the scroll region while the table scrolls (TBL-22). WHILE no column is sortable and selection is none THE table SHALL emit no event. |
TBL-17 | WHEN the columns at their least widths (TBL-2) are wider than the table's box THE table SHALL keep its box at the width the layout gives it and scroll its columns sideways inside that box, header row, body rows and footer rows together, beginning at the start edge; it SHALL never be wider than its layout and SHALL never make what holds it scroll sideways; a wheel, a trackpad, a drag on a touch screen and the keys of TBL-22 SHALL scroll it. |
TBL-18 | WHERE the layout gives the table less height than its rows need THE table SHALL scroll its rows up and down inside its own box, the caption above them as the first thing in the box, where it MAY scroll away with the rows on a platform whose caption is part of what scrolls (the web's native caption), and the status line staying at the bottom of the box (TBL-51); WHERE the layout sets no such limit THE table SHALL be exactly as tall as its caption and its rows, and SHALL set no height and no number of rows of its own. |
TBL-19 | WHILE stickyHeader is true and the table scrolls its rows in its own box (TBL-18) THE table SHALL keep the header row at the top of that box while the rows pass under it, filled with comp.table.sticky.container.color under comp.table.header.container.color so that nothing shows through it, and SHALL draw a line of comp.table.divider.thickness in comp.table.divider.color under it whatever the separation is while a row is under it; WHILE stickyHeader is false, or the table does not scroll its rows in its own box, THE header row SHALL scroll with the rows. |
TBL-20 | WHILE stickyFirstColumn is true and the table scrolls sideways (TBL-17) THE table SHALL keep the first column — its header cell, its cells and its footer cells — at the start edge of the box while the other columns pass under it, each of its cells filled with comp.table.sticky.container.color under the fill its row gives it (comp.table.header.container.color in the header row, comp.table.stripe.container.color in a striped row), and SHALL draw a line of comp.table.divider.thickness in comp.table.divider.color along its inner edge for the table's whole height while a column is under it; WHILE both stickyHeader and stickyFirstColumn hold THE first header cell SHALL stay above both. IF the first column is wider than half of the table's box THE table SHALL let it scroll with the other columns, so that a narrow window is never filled by the column that stays. |
TBL-21 | WHILE the table scrolls sideways and columns lie beyond an inline edge of its box THE table SHALL draw a line of comp.table.divider.thickness in comp.table.divider.color along that edge of the box for the table's whole height, and SHALL remove it when the scroll reaches that edge; at the start edge of a table whose first column is kept in view the line of TBL-20 is that line. THE table SHALL show that there is more with these lines alone: no shadow, no fade and no color that is not the divider's. |
TBL-22 | WHILE the table scrolls in its own box, sideways or up and down, THE table SHALL make that box one stop in the focus order, before anything in its cells that takes focus, exposed as a region that carries the table's name (TBL-23); WHILE it has keyboard focus the box SHALL draw the system's focus indicator inside its edge — sys.focus.outer-width wide in the color role focus-ring, set in from the edge by sys.focus.offset so that nothing clips it, and never animated — and the arrow keys, Page Up, Page Down, Home and End SHALL scroll it as the platform scrolls a region, the left and right arrow keys moving the view left and right in either layout direction; WHILE the table does not scroll THE table SHALL NOT be a stop in the focus order and SHALL expose no region. |
TBL-23 | THE table SHALL take its accessible name from caption where caption is not empty, and from label otherwise, and SHALL NOT draw label; IF both are empty THE table SHALL be exposed without a name and in debug builds report that a table has no name, and WHILE such a table scrolls in its own box that box SHALL still be a stop in the focus order (TBL-22), exposed as a region without a name, because the keyboard must be able to scroll it; IF both are given THE caption SHALL be the name and label SHALL be ignored. |
TBL-24 | THE table SHALL be exposed to assistive technology as a table with its number of rows and columns, its header cells as column headers, its first cells as row headers while rowHeaders is true, and every cell associated with the header of its column and, where there is one, of its row, so that a person moving cell by cell hears what each value belongs to; a platform that has no table semantics SHALL expose each row as one group in reading order and each cell with its column header before its own content. Keeping a header row or a first column in view, striping, the density and scrolling SHALL change nothing that is exposed. |
TBL-25 | WHEN the table's rows, columns or cells change THE table SHALL draw the new content at once, without a motion, keeping its scroll position where the content it showed still exists, and SHALL NOT announce the change by itself. The one place where the table tells a change is its status line, and what that says is the product's (TBL-52); WHEN rows are replaced because the product ordered or fetched them THE table SHALL keep keyboard focus on the sort button, the checkbox or the radio button that had it, where that control still exists. |
TBL-26 | THE table SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the table is shown THE table SHALL draw itself for the new direction at once, without a motion of its own, keeping its content and the column a person was looking at in view. |
TBL-27 | WHERE the layout direction is right-to-left THE table SHALL lay its columns out from the right, the first column at the right edge; SHALL apply paddings and the alignments start and end by the layout direction; SHALL begin its sideways scroll at the right edge; SHALL keep a first column that stays in view (TBL-20) at the right edge with its line on its left; and SHALL draw the lines of TBL-21 at the edges beyond which columns lie. Top and bottom SHALL NOT change: the header row is the first row and the footer rows the last in either direction. |
TBL-28 | THE table SHALL lay out the text of each cell, of each header cell and of the caption as text of its own direction, isolated from the cells beside it: it takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right name, an identifier, a number with its sign, unit or currency, a version and a date keep their own order in a right-to-left layout; THE table SHALL place that text by the column's alignment in the layout direction, not by the text's own, and SHALL NOT reverse the digits of a number: a column of numbers whose align is end is at the left edge of its cells in a right-to-left layout, with its digits written left to right. |
TBL-29 | WHILE the text size is enlarged THE table SHALL grow every text style with it, give up none of its paddings and none of its lines (they MAY grow with the text where the design system's units follow the text size), size its columns again from the larger content (TBL-2), wrap what can wrap and scroll in its own box where the columns no longer fit (TBL-17); it SHALL cut no content, SHALL set no fixed height on a row or a cell, SHALL lose no content when the reader raises line height, paragraph spacing, letter spacing or word spacing, and SHALL need no sideways scrolling of what holds it at a width of 320 points. |
TBL-30 | WHERE the platform draws with a person's own forced colors (the web's forced-colors mode, and a platform's equivalent where it has one) THE table SHALL draw every line of TBL-8, TBL-19, TBL-20 and TBL-21 in the platform's text color and its text in that color, SHALL keep header cells, row headers and footer cells told apart by their text styles, and, because the mode removes fills, SHALL draw the lines of TBL-8 in a striped table whatever its separation is, so that its rows are still told apart. |
TBL-31 | WHEN density names a value that the active design system does not have THE table SHALL render with the default value's tokens, and in debug builds report the unknown value. |
TBL-32 | WHERE a column's sortable is true and its header is not empty THE table SHALL draw that column's header cell as one button, the sort button: its container is the whole box of the header cell, paddings included, with square corners; it holds the column's header as its label, in the header's text style (TBL-5), and the sort mark — one glyph of the system's icon set in a box of comp.table.header.icon.size, drawn through the icon helper with sys.icon.family.unselected, sys.icon.fill.unselected, the weight comp.table.header.icon.weight and an optical size clamped between sys.icon.optical-size.min and sys.icon.optical-size.max — comp.table.header.gap after the label's box, at the height of the label's last line, so that a header that wraps keeps its mark at the edge of its label rather than after the last word; in a column whose align is end the mark SHALL stand before the label, so that the end of the label stays on the edge its numbers end at. The mark's box SHALL be kept whatever the column's sort is, so that a change of order moves no label and changes no column's width. IF sortable is true and header is empty THE table SHALL draw the column as one that is not sortable and in debug builds report it. |
TBL-33 | THE table SHALL draw the order the product sets and never make one: WHILE a sortable column's sort is none its sort button is the part sort — the label in comp.table.sort.label.color, the glyph swap_vert in comp.table.sort.icon.color, the container in comp.table.sort.container.color; WHILE its sort is ascending or descending it is the part sorted — the label in comp.table.sorted.label.color, the glyph arrow_upward for ascending and arrow_downward for descending in comp.table.sorted.icon.color, the container in comp.table.sorted.container.color. THE table SHALL NOT reorder, compare, filter or hide a row, SHALL NOT change a column's sort itself, and SHALL draw its rows in the order it is given whatever any sort says. WHEN a column's sort changes THE table SHALL draw the other glyph and colors at once, with no motion but that of a state change (TBL-35). IF more than one column's sort is not none THE table SHALL draw each as it is given and in debug builds report it: the rows have one order. A sort on a column whose sortable is false SHALL be ignored. |
TBL-34 | WHEN the user activates a sort button by pointer, touch, Enter or Space THE table SHALL emit sort exactly once, carrying the column's value, its index among the columns of data and the direction asked for — ascending WHILE the column's sort is none or descending, descending WHILE it is ascending — and SHALL change nothing it draws but the answer to the press: the mark, the colors and the rows stay as they are until the product sets sort and gives the rows in their new order. The direction is what the person asked; the product MAY set another, or none. THE table SHALL keep keyboard focus on the sort button through the press and through the change of rows that follows it. WHERE the product gave the control a destination in place of an action (on the web an anchor in the header cell, whose address is the same table in the other order) THE table SHALL emit sort once and then let the platform follow the destination unless the product cancelled the event, and Space SHALL NOT activate it, as it activates no link. |
TBL-35 | WHILE a sort button is hovered, keyboard-focused or pressed THE table SHALL render its container, its label, its mark and its opacity from the component tokens of its part (sort or sorted) in the highest-precedence active state, with precedence pressed over focused over hovered; focus gained by pointer or touch SHALL NOT select the focused state. A change between states SHALL take comp.table.motion.state-duration with comp.table.motion.state-easing and SHALL be immediate where motion is reduced. The state layer SHALL cover the header cell and nothing beside it, and WHILE the header row or the column is kept in view (TBL-19, TBL-20) SHALL be drawn over comp.table.sticky.container.color, so that nothing shows through it. |
TBL-36 | WHEN a sort button receives keyboard focus THE table SHALL show the system's focus indicator inside the edge of its header cell without animation — sys.focus.outer-width wide in the color role focus-ring, set in from the edge by sys.focus.offset so that neither the next cell, the box's edge nor a header kept in view clips it, with the inner stroke of sys.focus.inner-width where the system has one; focus gained by pointer or touch SHALL NOT show it. WHEN keyboard focus moves to a sort button, a checkbox or a radio button that is under a header row, a first column or a status line kept in view, or outside the box, THE table SHALL scroll its box so that the whole control is in view beside them. |
TBL-37 | WHEN a sort button is pressed by pointer, touch, Enter or Space THE table SHALL show the pressed state without delay and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner. |
TBL-38 | WHILE a sort button is pressed THE table SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; label, mark and opacity still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.table.motion.feedback-duration with comp.table.motion.state-easing, fades over comp.table.motion.state-duration with comp.table.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. |
TBL-39 | THE table SHALL show the keyboard focus of a sort button as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README. A sort button's container has square corners, the header cell's, so sys.shape.corner-smoothing has nothing to ease on it. |
TBL-40 | THE table SHALL make a sort button's hit area its whole header cell, with no gap to the hit area of the header cell beside it. WHERE input is touch, or the platform cannot tell the input type, a header row that holds a sort button SHALL be at least sys.target.touch high, drawn so, at both densities, because a header cell cannot extend a hit area over the row under it; for a fine pointer it SHALL be at least sys.target.pointer high WHILE density is regular, and WHILE density is compact as high as its density gives. A header cell narrower than the target SHALL extend its hit area along the row no further than halfway into the paddings between it and the cell beside it. |
TBL-41 | THE table SHALL expose a sortable column's header cell as the column header it is (TBL-24) with the order its sort names — ascending, descending, or none for a column that can be sorted and is not — and the sort button inside it as a button named by the column's header; the sort mark SHALL be hidden from assistive technology. On the web these are aria-sort on the header cell and the product's own button in it. WHERE a platform has no sort state THE table SHALL say the order after the button's name with sortedAscendingLabel or sortedDescendingLabel, which come from the instance or from the product's words under the keys of the same names and from nowhere else; IF such a platform is given neither, THE table SHALL expose the button by its name alone and in debug builds report the missing word once. THE table SHALL NOT announce an order itself when it changes: the header cell's state says it, and a product that wants it spoken writes it in status (TBL-52). |
TBL-42 | WHILE selection is multiple or single THE table SHALL draw a selection column before its first column: in the header row a cell holding the checkbox for every row WHILE selection is multiple (TBL-45) and nothing WHILE it is single (TBL-60), in each body row that has a value a cell holding that row's control — a checkbox WHILE selection is multiple, a radio button WHILE it is single — and an empty cell in a body row without a value, in a placeholder row and in a footer row. Each checkbox is the Checkbox component and each radio button the Radio button component, as their own specs draw them, with no label drawn, placed on the cell's first line (TBL-15) with the edge of its box or ring, not of its round container, at the cell's padding, so that the boxes and rings stand on the line the text of the other cells starts at: the selection cell's paddings are a cell's paddings less half of what the control's container exceeds its box by — comp.checkbox.container.size over comp.checkbox.box.size, comp.radio-button.container.size over comp.radio-button.ring.size — (never less than 0), the round container reaching into them, and the column is as wide as the control's container and those paddings. The selection column is not a column of data: it has no header text, the first column of rowHeaders and of stickyFirstColumn is the one after it, and a sort event's index does not count it. WHILE stickyFirstColumn is true the selection column SHALL stay in view with the first column (TBL-20), and the two together are what is released when they would take more than half of the box. WHILE selection is none THE table SHALL draw no such column and no checkbox or radio button, SHALL ignore every row's selected, and SHALL emit no selectionChange. |
TBL-43 | WHILE selection is multiple or single and a body row's selected is true THE table SHALL draw that row as chosen: comp.table.row.selected.container.color across its whole width, paddings and the selection cell included, in place of the stripe of TBL-11 where the row would have one; the text of its cells and of its row header in comp.table.row.selected.text.color; and its checkbox or radio button checked. What a cell holds that is not plain text SHALL keep its own colors (TBL-15). In a column kept in view the fill SHALL lie over comp.table.sticky.container.color. The lines and the gap between rows (TBL-8, TBL-10) SHALL be drawn as without it. The fill SHALL change at once, without a motion of the table's own. A footer row and a placeholder row are never chosen. |
TBL-44 | WHEN the user changes a row's checkbox or radio button — by a press, by Space while it has focus, or, for a radio button, by an arrow key (TBL-61) — THE table SHALL set that row's selected to the control's new state, WHILE selection is single let the row that was chosen go (TBL-60), and emit selectionChange exactly once, carrying the values of every chosen row after the change in row order. A press on the row outside its selection cell presses its control where TBL-58 says so, and this sentence holds for that change as for any other. WHEN the product sets a row's selected THE table SHALL draw it and SHALL NOT emit. THE table SHALL keep which rows are chosen by their values: WHEN rows are given again in another order (TBL-33) each row SHALL be drawn chosen or not by its own selected, and the table SHALL carry no selection over to a row it is not given. |
TBL-45 | WHILE selection is multiple THE table SHALL keep the checkbox for every row in step with the body rows it holds that have a value: checked WHILE each of them is chosen, in its mixed (indeterminate) state WHILE some are and some are not, unchecked WHILE none is, and disabled WHILE there is no such row. WHEN the user activates it while it is unchecked or mixed THE table SHALL choose every such row, and while it is checked SHALL clear every one, and SHALL emit selectionChange exactly once with the values after the change. "Every row" is every row the table has been given: it SHALL NOT claim to choose records the product has not given it (the rows of other pages), and a product that offers that says so with a control of its own. WHILE selection is single there is no such checkbox (TBL-60). |
TBL-46 | THE table SHALL name each row's checkbox or radio button by that row's selectLabel; WHERE selectLabel is empty and rowHeaders is true the control SHALL be named by the text of the row's header cell; IF it has neither THE table SHALL expose the control without a name and in debug builds report the row once per table. WHILE selection is multiple THE table SHALL name the checkbox for every row by selectAllLabel, from the instance or from the product's words under the key selectAllLabel and from nowhere else; IF neither gives it THE table SHALL still draw the checkbox, expose it without a name and in debug builds report the missing word. A table whose selection is multiple or single SHALL still be exposed as a table (TBL-24), its rows as rows and its selection cells as cells: which rows are chosen is said by the checked state of their checkboxes or radio buttons, the header checkbox says checked, mixed or not checked, and the table SHALL add no selected state to a row and no role of a grid. |
TBL-47 | THE table SHALL make each sort button and each checkbox its own stop in the focus order, and the radio buttons of a table whose selection is single one stop together (TBL-61), in reading order — the scroll region first while the table scrolls (TBL-22), then the header row's controls from the start, then each row's checkbox, or the one radio button that is the stop, followed by whatever in that row's cells takes focus — and SHALL handle no key that moves focus from cell to cell: the arrow keys scroll the region or do what the focused control does with them, which for a radio button is TBL-61. A checkbox's or radio button's hit area SHALL be its whole selection cell, in width and in height; for a pointer the rest of its row answers too (TBL-58). WHERE input is touch, or the platform cannot tell the input type, a row that holds a checkbox or a radio button SHALL be at least sys.target.touch high, drawn so, at both densities, and its selection cell at least sys.target.touch wide; for a fine pointer the row SHALL be at least sys.target.pointer high WHILE density is regular, and WHILE density is compact as high as its density gives. |
TBL-48 | WHILE the table has no body row, loading is false and the empty slot has content THE table SHALL draw that content in the place of the body rows: under the header row and its line, before any footer row, as wide as the table's box and staying in view while the columns scroll sideways, with comp.table.empty.padding-block above and below it and a cell's inline padding at its sides (TBL-4), with no line, no stripe and no fill of the table's through it. THE table SHALL expose it as one row holding one cell that spans every column, SHALL add no word, glyph or role of its own to it, and SHALL NOT announce it (TBL-25): where a search or a filter emptied the table, the product's Empty state is the live one. WHILE the table has no body row and the empty slot has no content THE table SHALL draw its caption, its header row and its footer rows and nothing between them. WHILE the table has a body row THE table SHALL NOT draw the empty slot's content. |
TBL-49 | WHILE loading is true THE table SHALL be exposed as busy; SHALL draw the rows it is given as they are — the rows it had, or placeholder rows (TBL-50) — and SHALL NOT draw the empty slot's content, even with no body row; SHALL keep its header row, its sort buttons, its checkboxes, its radio buttons and its status line working as without it; SHALL change no color, no opacity and no size of its own because of it; and SHALL announce nothing itself when loading begins or ends: the product says it in status (TBL-52). THE table SHALL fetch nothing, wait for nothing and time nothing: loading is true for as long as the product says so. |
TBL-50 | WHERE a row's placeholder is true THE table SHALL draw it as a body row — counted for stripes, parted from its neighbours as TBL-9 says, with a cell's paddings — at least as tall as a body row holding one line of text at the table's density, however little its cells hold; SHALL place in each cell what the product gives it there (a Skeleton, by TBL-15) or nothing; SHALL draw no checkbox or radio button in its selection cell and count it neither as a body row for the header checkbox (TBL-45) nor as a row for the empty slot (TBL-48); and SHALL hide the row from assistive technology, so that a person hears that the table is busy and not a row of nothing. IF a row's placeholder is true while loading is false THE table SHALL draw it all the same and in debug builds report it. |
TBL-51 | WHERE status is not empty or the pagination slot has content THE table SHALL draw a status line under its last row: the status text at the start edge in comp.table.status.text.typescale and comp.table.status.text.color, the pagination slot's content at the end edge, both centered on the line's height, with comp.table.status.padding-block above and below, a cell's inline padding at its sides (TBL-4) and at least comp.table.status.gap between the two; WHERE the two do not fit on one line THE table SHALL put the status text above the pagination, comp.table.status.gap apart, each at the start edge, and the text SHALL wrap and never be cut. The status line SHALL be as wide as the table's box and SHALL stay where it is while the columns scroll sideways (TBL-17). WHILE separation is divider THE table SHALL draw one line of comp.table.divider.thickness in comp.table.divider.color above it. WHILE the table scrolls its rows in its own box (TBL-18) the status line SHALL stay at the bottom of that box while the rows pass under it, filled with comp.table.sticky.container.color, with that line above it whatever the separation is while a row is under it. WHERE status is empty and the slot has no content THE table SHALL draw no status line and leave no room for one. |
TBL-52 | THE table SHALL expose the status text as a status that assistive technology is told of politely WHEN it changes, without moving focus, and SHALL tell it nothing when the text is first drawn with the table. THE table SHALL write no word into it and SHALL count nothing for it: how many rows there are, which of them are shown and how many are chosen are the product's to say, in its own language. THE table SHALL place the content of the pagination slot and SHALL NOT style it, give it keys or listen to it: the table shows the rows it is given, emits nothing about pages, and holds no page number and no page size. |
TBL-53 | WHERE the layout direction is right-to-left THE table SHALL draw the selection column at the right edge, before the first column; SHALL place a sort mark after its label on the left, and before it — on the right — in a column whose align is end; SHALL draw the status text at the right edge of the status line and the pagination at the left; and SHALL keep the selection column and a first column that stay in view at the right (TBL-27). The glyphs swap_vert, arrow_upward and arrow_downward show an order and not a direction of reading, and SHALL NOT be mirrored. The status text SHALL be laid out as text of its own direction (TBL-28). |
TBL-54 | WHERE the platform draws with a person's own forced colors THE table SHALL draw a sort button's label and mark in the platform's button-text color and its focus indicator in the platform's highlight color, SHALL keep the sorted column told from the others by its glyph alone, and, because the mode removes the fill of a chosen row, SHALL rely on the row's control — its checkbox, which the Checkbox draws checked in that mode, or its radio button, which the Radio button draws selected there — to say that the row is chosen; it SHALL draw the line above a status line kept in view in the platform's text color (TBL-30). |
TBL-55 | WHILE the text size is enlarged THE table SHALL grow a sort button's label and its mark's box with it, wrap the label inside its column where the column's wrap allows it and keep the mark on the label's last line, grow the status text and wrap it, keep the selection column as wide as its checkbox or radio button and paddings, and lay the status line out in two lines where one no longer fits (TBL-51); nothing of these SHALL be cut, and TBL-29 holds for a table with every one of them at a width of 320 points. |
TBL-56 | WHILE the system's icon set cannot yet draw a sort mark (its font is still loading) THE table SHALL draw nothing in the mark's box, never the glyph's name as text, SHALL keep the box, and SHALL draw the glyph once the set can; the sort button SHALL work all the same. |
TBL-57 | WHERE the platform draws the product's own elements and script does not run (the web) THE table SHALL still draw, from the product's markup alone, everything of TBL-32, TBL-33, TBL-42, TBL-43, TBL-48, TBL-50 and TBL-51 that is drawing: a sort button and the mark of the order the header cell names, the hover, focus and press of the button, the selection column, a chosen row, the empty content, placeholder rows and the status line; a sort button that is an anchor, and checkboxes and radio buttons that are the platform's own inside the product's form, SHALL work as the platform makes them work — radio buttons of one name exclude each other and share one stop without script (TBL-60, TBL-61). What needs script and is then absent: the events, a press on a row outside its checkbox or radio button and the row's hover and press (TBL-58, TBL-59), which are then not drawn, the header checkbox kept in step and choosing every row (TBL-45), the busy state where the product did not write it, keeping focus through a change of rows, and what TBL-22 already loses. Nothing SHALL be hidden that script would have to reveal. |
TBL-58 | WHILE selection is multiple or single, WHEN a body row that can be chosen (it has a value and a checkbox or radio button, TBL-42) is pressed and released outside its selection cell with the primary mouse button, a touch or a pen, without the pointer moving further than the platform's touch slop between the two, THE table SHALL press that row's control exactly as a press on the control does: a checkbox's row is chosen or let go, a radio button's row is chosen where it was not and stays chosen where it was (TBL-60), and selectionChange is emitted once where the choice changed (TBL-44). For a pointer the whole row is the control's hit area, the way a whole card answers a press while the controls inside it keep their own. THE table SHALL NOT press the control IF the press lands on or inside something in a cell that answers a press itself — a link, a button, a field, a select, a label, a summary, an editable element, an element with a tab index, an element with a widget role, a custom element that takes focus, or an element to which the product gave the pointer cursor where the row's own is not (the pointer the row shows for itself does not count) — which keeps its own behaviour; IF the release ends a text selection, that is, the selection is not empty and lies in the row once the press is released; IF the pointer moved further than the slop (a drag, a scroll); IF a modifier key (Control, Command, Shift, Alt or Option) is held or a button other than the primary is used; IF the press is the second or a later press of a multiple click; IF the product cancelled the click; or IF the press lands inside a table nested in a cell, whose rows are its own. A press in the selection cell is the control's (TBL-47). The header row, whose checkbox stands for every row, a footer row, a placeholder row and a body row without a value SHALL take no such press. The row SHALL get no role, no name, no focus stop, no selected state and no announcement of its own: the checkbox or radio button stays the one control of the row, which the keyboard and assistive technology reach as TBL-46, TBL-47 and TBL-61 say, and a press on the row is a larger target for the same action. WHERE the platform's row answers no press (on the web, without the element's script) the row SHALL show neither the pointer cursor nor a state, and a press there SHALL choose nothing. |
TBL-59 | WHILE selection is multiple or single and a fine pointer is over a body row that can be chosen where a press would be its control's — its selection cell, or its cells outside anything that answers a press itself (TBL-58) — THE table SHALL draw that row hovered, as one target — the chosen row of a table whose selection is single too, whose press is its radio button's even though it changes nothing, as the radio button itself answers a hover and a press while it is selected — across its whole width, paddings and the selection cell included: its fill in the hovered container color of the row's condition — selected WHILE the row is chosen, unselected otherwise; the default system mixes comp.table.row.selected.state-layer.color or comp.table.row.unselected.state-layer.color into that condition's resting fill — laid over what the row is filled with at rest (its stripe, the chosen fill, comp.table.sticky.container.color in a column kept in view) and short of the gap of TBL-10; the text of its cells in the hovered text color of its condition; and the pointer cursor. WHILE the pointer is over something in a cell that answers a press itself, that thing SHALL keep its own cursor, hover and press, and the row SHALL be drawn at rest. WHEN such a row is pressed by pointer, touch or pen where TBL-58 or TBL-47 makes the press its control's THE table SHALL draw the row pressed without delay and keep it so for at least sys.motion.duration.press-hold, even when the press ends sooner, answering as sys.behavior.pressed-feedback names: overlay draws the pressed container and text colors of the row's condition across the row; dim applies the table's pressed opacity to the whole row while its fill and text stay with the state under the press; none draws no answer; and ripple, which has no one box to grow in across the cells of a row, answers as overlay. The press SHALL end when the pointer leaves the row, moves further than the slop or is cancelled. The fills SHALL change at once, without a motion of the table's own, as the chosen fill does (TBL-43). The checkbox or radio button SHALL keep its own hover, focus and press; a key that presses it or moves to it SHALL NOT draw the row pressed, and its keyboard focus is shown by the control alone. Under forced colors the row's state fills are gone with the other fills (TBL-54). |
TBL-60 | WHILE selection is single THE table SHALL let one row at most be chosen: each body row that can be chosen holds a radio button in its selection cell (TBL-42), and the header row's selection cell SHALL hold nothing, because no control stands for every row; IF the product puts a control there THE table SHALL NOT draw it and in debug builds report it. The radio buttons of one table SHALL be one group whose options exclude each other (RAD-3): on the web the radio buttons of one name, the name the product gave them or, for those it gave none, one the table gives them. WHEN the user chooses a row that is not chosen — by a press on its radio button or on the row (TBL-58), by Space, or by an arrow key (TBL-61) — THE table SHALL set that row's selected, let the row that was chosen go, and emit selectionChange exactly once, carrying the value of the chosen row alone. WHEN the user activates the radio button of the chosen row, or presses that row, THE table SHALL keep the row chosen and SHALL NOT emit: a radio button is never cleared by its own activation (RAD-2), and a product that lets a person go back to no row chosen offers a control of its own for that, which clears selected. WHEN the product sets selected THE table SHALL draw it and SHALL NOT emit (TBL-44); IF more than one row says it is chosen THE table SHALL draw chosen the one whose selected the product set last — of several set at once, the first in row order — and let the others go without emitting, and in debug builds report several set at once. IF a body row's selection cell holds a checkbox WHILE selection is single, or a radio button WHILE it is multiple, THE table SHALL treat the row as one that cannot be chosen, SHALL NOT draw that control, and in debug builds report it. |
TBL-61 | WHILE selection is single THE table SHALL expose each row's radio button as a radio, checked or not, named as TBL-46 says, with its position among the table's radio buttons and their number, and SHALL wrap them in no radio group, give a row no selected state and add no role of a grid: a radio group cannot hold the rows of a table, and the table's name and the column the radio buttons stand in say what they choose. THE radio buttons SHALL be one stop in the focus order — the chosen row's, or, while no row is chosen, the first row's that can be chosen — at the place of that row's control in the reading order of TBL-47, and Tab SHALL leave them rather than move between them (RAD-5). WHILE one of them has keyboard focus, Down Arrow and Right Arrow SHALL move focus to the radio button of the next row that can be chosen and Up Arrow and Left Arrow to the previous one's, wrapping from the last to the first, and choose that row as TBL-60 says, with one selectionChange for each key; in a right-to-left layout Left Arrow and Right Arrow SHALL exchange what they do. THE table SHALL scroll its box so that a radio button that receives keyboard focus is in view beside a header row or a status line kept in view (TBL-36), and WHEN rows are given again focus SHALL stay with the radio button of the row of the same value (TBL-25). |
مطابقة الأسماء بين المنصات
التكافؤ لا يعني واجهات برمجية متطابقة. تعبّر كل منصة عن العقد نفسه بأسلوبها.
| المواصفة | Web | SwiftUI | Jetpack Compose | Flutter | React Native |
|---|---|---|---|---|---|
| المكوّن | tanrei-table | TanreiTable | TanreiTable | TanreiTable | Table |
caption · الخاصية | caption | caption | caption | caption | caption |
label · الخاصية | label | accessibilityLabel | contentDescription | semanticLabel | accessibilityLabel |
density · الخاصية | density | density | density | density | density |
separation · الخاصية | separation | separation | separation | separation | separation |
striped · الخاصية | striped | striped | striped | striped | striped |
rowHeaders · الخاصية | rowHeaders | rowHeaders | rowHeaders | rowHeaders | rowHeaders |
stickyHeader · الخاصية | stickyHeader | stickyHeader | stickyHeader | stickyHeader | stickyHeader |
stickyFirstColumn · الخاصية | stickyFirstColumn | stickyFirstColumn | stickyFirstColumn | stickyFirstColumn | stickyFirstColumn |
selection · الخاصية | selection | selection | selection | selection | selection |
selectAllLabel · الخاصية | select-all-label | selectAllLabel | selectAllLabel | selectAllLabel | selectAllLabel |
sortedAscendingLabel · الخاصية | sorted-ascending-label | sortedAscendingLabel | sortedAscendingLabel | sortedAscendingLabel | sortedAscendingLabel |
sortedDescendingLabel · الخاصية | sorted-descending-label | sortedDescendingLabel | sortedDescendingLabel | sortedDescendingLabel | sortedDescendingLabel |
loading · الخاصية | loading | isLoading | loading | loading | loading |
status · الخاصية | status | status | status | status | status |
sort · الحدث | sort | onSort | onSort | onSort | onSort |
selectionChange · الحدث | selectionchange | selection | onSelectionChange | onSelectionChanged | onSelectionChange |
| ملاحظات | Form: <tanrei-table> is a custom element with **no shadow root**, wrapped around a native <table> that stays in the light DOM — <tanrei-table><table><caption>…</caption><thead>…</thead><tbody>…</tbody><tfoot>…</tfoot></table></tanrei-table>. The native table is never given another display, so its semantics, a crawler's reading, copy and paste as a table and a screen reader's table navigation are the browser's own. The wrapper is the scroll box (display: block; overflow: auto; max-inline-size: 100%; the product limits its height with max-block-size). Everything drawn comes from one stylesheet, @tanrei/web/table.css, whose selectors start at tanrei-table and are wrapped in :where(); it styles the page without script (an element that is not yet defined is still an element), and the element adopts the same sheet into the root it is connected to, so it also works inside another component's shadow root. Props are attributes of the wrapper: density, separation, striped, row-headers, sticky-header, sticky-first-column, label. caption is the native <caption>; the caption attribute is a convenience that writes one. label becomes aria-label on the table. Script adds only what CSS cannot: while the wrapper overflows it sets tabindex="0", role="region" and aria-labelledby (the caption) or aria-label (TBL-22), and removes them when it does not; it sets data-more-start and data-more-end for the lines of TBL-21, writes the positions of the columns whose header names data-align or data-wrap on the wrapper (data-columns-end, -center, -nowrap; one rule per position places the cells and no cell is written to), gives first cells role="rowheader" where row-headers is set on a table whose body cells are <td> (Markdown), and releases a first column wider than half of the box (TBL-20). Without script the table is fully drawn, scrolls in its box, and keeps its sticky parts; it lacks the two edge lines and, in a browser that does not focus scrollers by itself, the focus stop. Sticky parts are position: sticky on the cells (inset-block-start: 0 on thead th, inset-inline-start: 0 on the first cell of each row) with border-collapse: separate and the lines drawn as cell borders, because collapsed borders do not travel with a sticky cell. The caption stays in place sideways with position: sticky; inset-inline-start: 0 and a width of the wrapper (100cqi). font-variant-numeric: tabular-nums on cells (TBL-4); unicode-bidi: plaintext per cell (TBL-28). Markdown: a renderer emits a bare <table>; the product wraps it at build time (a rehype step of a few lines, shown in the README) — Tanrei does not transform content. Inside .tanrei-prose the element is an island by its tag name (PRS-16). A cell that spans (colspan, rowspan) and a second header row keep working as HTML and are drawn with the same cell styles, but no fixture covers them. As built: the wrapper is a grid of one cell, not display: block, so that the two "there is more" lines are as tall as the table; the attributes the element writes are data-more-start, data-more-end, data-more-above, data-first-column-released, data-columns and the three positions above, and data-separation on the table; the region's aria-labelledby or aria-label is copied from the table's own name where the product named it natively. **Sorting, selection, empty, loading and status: proposed form, for the platform's agent to settle.** Everything new is the product's own markup inside the same <table>, drawn by table.css without script (TBL-57). Sorting: the stylesheet makes the control in a th[aria-sort] fill the cell (the cell's paddings move onto the control) and draws the mark as a generated glyph from the attribute; the element listens for the control's activation and emits sort (cancelable where the control is an anchor) with detail: { column, index, direction }; it never writes aria-sort. Selection: selection="multiple" on the wrapper; the header row's first cell is <td data-part="selection"> (a cell and not a header cell: a header without text is announced as an empty one) holding the checkbox for every row, which the element keeps checked, indeterminate or unchecked and disables with no row to choose, and names from select-all-label or the product's words (setWords({ selectAllLabel })); the element listens to change from the row checkboxes, writes data-selected, and emits selectionchange with detail: { values }. Neither checkbox is moved, wrapped or replaced. sorted-ascending-label and sorted-descending-label are accepted and not read: aria-sort is the browser's. Empty: the product writes <tbody><tr data-part="empty"><td colspan="…">…</td></tr></tbody> with its <tanrei-empty-state> inside; the stylesheet keeps the cell's content as wide as the box (100cqi) and in view sideways, as it does the caption, and the element corrects a colspan that is not the number of columns. Loading: the loading attribute; the element writes aria-busy on the table (a server may write it itself). Status: after the <table>, inside the wrapper, <div data-part="status"><p role="status">…</p> <nav class="tanrei-pagination">…</nav></div>; it is sticky at the inline start with the box's width, and at the block end, with the sticky fill; the status attribute is a convenience that writes the paragraph, as caption writes a caption. Coarse pointers get the taller header row and rows by @media (pointer: coarse). | Proposed spelling, for the platform's agent to settle. TanreiTable(caption:, columns: [TanreiTableColumn], density:, …) { TanreiTableRow { TanreiTableCell("…"); TanreiTableCell { TanreiTag("Beta", size: .small) } } }. Built on Grid and GridRow (columns sized from content, which is TBL-2) inside a ScrollView whose axes are the ones that overflow; a table of several hundred rows uses a LazyVStack of rows with column widths measured once from the header and a sample of rows. The platform's own Table is not used: it is a data table of its own look, with selection and sorting, and it collapses to its first column in a compact width. Sticky parts are pinned section headers (pinnedViews) for the header row and an overlaid first column whose offset follows the scroll. Accessibility: SwiftUI has no table container for custom views, so TBL-24's fallback applies — each row is an accessibility container in reading order, each cell's label is its column header followed by its content, header cells carry the header trait, and the scroll view is named by the caption. Sorting, selection, empty, loading and status: TanreiTableColumn(header:, value:, isSortable:, sort:), TanreiTable(selection: $values, onSort: { column, index, direction in … }) where selection is a binding to the set of chosen values, and rows take value:, selectLabel:, isPlaceholder:. A sortable header is a Button whose accessibility value is sortedAscendingLabel or sortedDescendingLabel (SwiftUI has no sort trait); the row's checkbox is TanreiCheckbox. The empty slot and the pagination slot are view builders (empty:, pagination:); the status text is posted as an announcement when it changes; isLoading sets nothing SwiftUI has a trait for, so placeholder rows are hidden and the status says it. Single selection (selection single, TBL-60, TBL-61): the row's control is the platform's Tanrei radio button in place of the checkbox, the header row holds none, and the product's selection holds one value at most. | Proposed spelling, for the platform's agent to settle. TanreiTable(columns = listOf(TanreiTableColumn("Price", align = End)), caption = …) { row { cell("…"); cell { TanreiTag("Beta", size = Small) } } }. Compose has no table layout: a custom Layout measures each column's least and preferred widths (intrinsics) and shares the width as TBL-2 says, inside horizontalScroll; a long table is a LazyColumn of rows with the widths measured from the header and the rows composed so far, and stickyHeader for the header row. Semantics: the container sets collectionInfo(rowCount, columnCount) and each cell collectionItemInfo(rowIndex, columnIndex), header cells are heading(), and each cell's content description is preceded by its column header, so TalkBack announces position and header. The scroll container is focusable and named by the caption. Sorting, selection, empty, loading and status: TanreiTableColumn(header, value = "price", sortable = true, sort = TanreiTableSort.Ascending), onSort = { column, index, direction -> }, selection = TanreiTableSelection.Multiple, rows as row(value = "a1", selected = …, selectLabel = …) with onSelectionChange = { values -> }; the product holds both states. A sortable header is Modifier.clickable(role = Role.Button) with stateDescription from sortedAscendingLabel or sortedDescendingLabel; the checkboxes are TanreiCheckbox (ToggleableState.Indeterminate for the header's); empty and pagination are composable slots; the status text is a polite liveRegion. Single selection (selection single, TBL-60, TBL-61): the row's control is the platform's Tanrei radio button in place of the checkbox, the header row holds none, and the product's selection holds one value at most. | Proposed spelling, for the platform's agent to settle. TanreiTable(caption: …, columns: [TanreiTableColumn(header: "Price", align: TanreiTableAlign.end)], rows: [TanreiTableRow(cells: [...])]). Built on the framework's Table widget with IntrinsicColumnWidth (flex for a column that wraps), inside a horizontal SingleChildScrollView in a Scrollbar; a long table uses a two-dimensional scroll view with pinned rows and columns for the sticky parts. The Material library's DataTable is not used: it brings its own look, sorting and selection, and reads a Material theme. Semantics: the table, row, cell and column-header roles where the framework version has them, and otherwise one Semantics per cell whose label is the column header followed by the content; the scroll view is a focusable region named by the caption. Sorting and selection: TanreiTableColumn(header:, value:, sortable:, sort:), onSort: (column, index, direction) {}, selection: TanreiTableSelection.multiple, TanreiTableRow(value:, selected:, selectLabel:, placeholder:) and onSelectionChanged: (values) {}; the product holds both states. A sortable header is an InkWell in Semantics(button: true, value: sortedAscendingLabel); the checkboxes are TanreiCheckbox with tristate for the header's; empty and pagination are widgets; the status text is Semantics(liveRegion: true); placeholder rows are in ExcludeSemantics. Single selection (selection single, TBL-60, TBL-61): the row's control is the platform's Tanrei radio button in place of the checkbox, the header row holds none, and the product's selection holds one value at most. | Proposed spelling, for the platform's agent to settle. <Table caption="…" columns={[{ header: "Price", align: "end" }]}><Table.Row><Table.Cell>…</Table.Cell></Table.Row></Table>. React Native has no table layout: rows are Views in a horizontal ScrollView (inside a vertical one, or a FlatList with stickyHeaderIndices for a long table), and column widths are measured in a first pass with onLayout and applied in a second, so the first frame of a large table is drawn after measuring. A first column that stays in view is a second column of cells drawn over the scroll view and kept in step with its vertical offset. There is no table role on either native platform: TBL-24's fallback applies — a row is one accessible group and each cell's accessibilityLabel is its column header followed by its content; header cells have accessibilityRole="header". Follows the app's direction where an ancestor's is not known. Sorting and selection: columns { header, value, sortable, sort }, onSort={({ column, index, direction }) => …}, selection="multiple", <Table.Row value selected selectLabel placeholder> and onSelectionChange={(values) => …}; the product holds both states. A sortable header is a Pressable with accessibilityRole="button" and accessibilityValue={{ text: sortedAscendingLabel }}; the checkboxes are Checkbox (accessibilityState={{ checked: "mixed" }} for the header's); empty and pagination are elements; the status text has accessibilityLiveRegion="polite" on Android and is posted with AccessibilityInfo.announceForAccessibility on iOS; accessibilityState={{ busy: true }} while loading. Single selection (selection single, TBL-60, TBL-61): the row's control is the platform's Tanrei radio button in place of the checkbox, the header row holds none, and the product's selection holds one value at most. |
الرموز
| الرمز | يشير إلى | القيمة المحلولة (فاتح) |
|---|---|---|
--tanrei-comp-table-caption-gap | sys.space.space100 | 8px |
--tanrei-comp-table-caption-text-color | sys.color.on-surface-variant | |
--tanrei-comp-table-cell-padding-block-compact | — | 6px |
--tanrei-comp-table-cell-padding-block-regular | — | 12px |
--tanrei-comp-table-cell-padding-inline-compact | sys.space.space150 | 12px |
--tanrei-comp-table-cell-padding-inline-regular | sys.space.space200 | 16px |
--tanrei-comp-table-cell-text-color | sys.color.on-surface | |
--tanrei-comp-table-divider-color | sys.color.outline-variant | |
--tanrei-comp-table-divider-thickness | sys.border.hairline | 1px |
--tanrei-comp-table-empty-padding-block | sys.space.space400 | 32px |
--tanrei-comp-table-focused-opacity | — | 1 |
--tanrei-comp-table-header-container-color | — | |
--tanrei-comp-table-header-gap | sys.space.space50 | 4px |
--tanrei-comp-table-header-icon-size | sys.icon.extra-small | 16px |
--tanrei-comp-table-header-icon-weight | sys.icon.mark.weight | 500 |
--tanrei-comp-table-header-text-color | sys.color.on-surface-variant | |
--tanrei-comp-table-hovered-opacity | — | 1 |
--tanrei-comp-table-motion-feedback-duration | sys.motion.role.feedback | 450ms |
--tanrei-comp-table-motion-state-duration | sys.motion.role.state.large | 250ms |
--tanrei-comp-table-motion-state-easing | sys.motion.easing.state | cubic-bezier(0.2, 0, 0, 1) |
--tanrei-comp-table-pressed-opacity | — | 1 |
--tanrei-comp-table-row-gap | sys.space.space50 | 4px |
--tanrei-comp-table-row-selected-container-color | sys.color.secondary-container | |
--tanrei-comp-table-row-selected-hovered-container-color | — | |
--tanrei-comp-table-row-selected-hovered-text-color | — | |
--tanrei-comp-table-row-selected-pressed-container-color | — | |
--tanrei-comp-table-row-selected-pressed-text-color | — | |
--tanrei-comp-table-row-selected-state-layer-color | comp.table.row.selected.text.color | |
--tanrei-comp-table-row-selected-text-color | sys.color.on-secondary-container | |
--tanrei-comp-table-row-unselected-container-color | — | |
--tanrei-comp-table-row-unselected-hovered-container-color | — | |
--tanrei-comp-table-row-unselected-hovered-text-color | — | |
--tanrei-comp-table-row-unselected-pressed-container-color | — | |
--tanrei-comp-table-row-unselected-pressed-text-color | — | |
--tanrei-comp-table-row-unselected-state-layer-color | comp.table.cell.text.color | |
--tanrei-comp-table-row-unselected-text-color | comp.table.cell.text.color | |
--tanrei-comp-table-separation | sys.behavior.separation | 0 |
--tanrei-comp-table-sort-container-color | — | |
--tanrei-comp-table-sort-focused-container-color | — | |
--tanrei-comp-table-sort-focused-icon-color | — | |
--tanrei-comp-table-sort-focused-label-color | — | |
--tanrei-comp-table-sort-focused-state-layer-color | — | |
--tanrei-comp-table-sort-hovered-container-color | — | |
--tanrei-comp-table-sort-hovered-icon-color | — | |
--tanrei-comp-table-sort-hovered-label-color | — | |
--tanrei-comp-table-sort-hovered-state-layer-color | — | |
--tanrei-comp-table-sort-icon-color | sys.color.on-surface-variant | |
--tanrei-comp-table-sort-label-color | comp.table.header.text.color | |
--tanrei-comp-table-sort-pressed-container-color | — | |
--tanrei-comp-table-sort-pressed-icon-color | — | |
--tanrei-comp-table-sort-pressed-label-color | — | |
--tanrei-comp-table-sort-pressed-state-layer-color | — | |
--tanrei-comp-table-sort-state-layer-color | comp.table.sort.label.color | |
--tanrei-comp-table-sorted-container-color | — | |
--tanrei-comp-table-sorted-focused-container-color | — | |
--tanrei-comp-table-sorted-focused-icon-color | — | |
--tanrei-comp-table-sorted-focused-label-color | — | |
--tanrei-comp-table-sorted-focused-state-layer-color | — | |
--tanrei-comp-table-sorted-hovered-container-color | — | |
--tanrei-comp-table-sorted-hovered-icon-color | — | |
--tanrei-comp-table-sorted-hovered-label-color | — | |
--tanrei-comp-table-sorted-hovered-state-layer-color | — | |
--tanrei-comp-table-sorted-icon-color | sys.color.on-surface | |
--tanrei-comp-table-sorted-label-color | sys.color.on-surface | |
--tanrei-comp-table-sorted-pressed-container-color | — | |
--tanrei-comp-table-sorted-pressed-icon-color | — | |
--tanrei-comp-table-sorted-pressed-label-color | — | |
--tanrei-comp-table-sorted-pressed-state-layer-color | — | |
--tanrei-comp-table-sorted-state-layer-color | comp.table.sorted.label.color | |
--tanrei-comp-table-status-gap | sys.space.space200 | 16px |
--tanrei-comp-table-status-padding-block | sys.space.space100 | 8px |
--tanrei-comp-table-status-text-color | sys.color.on-surface-variant | |
--tanrei-comp-table-sticky-container-color | sys.color.surface | |
--tanrei-comp-table-stripe-container-color | sys.color.surface-container-low | |
--tanrei-comp-table-caption-text-typescale | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-table-cell-text-typescale-compact | sys.typescale.body-small | 400 13px/1.5385 |
--tanrei-comp-table-cell-text-typescale-regular | sys.typescale.body | 400 14px/1.5714 |
--tanrei-comp-table-header-text-typescale-compact | sys.typescale.label-small | 500 12px/1.3333 |
--tanrei-comp-table-header-text-typescale-regular | sys.typescale.label | 500 14px/1.4286 |
--tanrei-comp-table-status-text-typescale | sys.typescale.body-small | 400 13px/1.5385 |
نماذج الاختبار
عروض مرجعية. تلتقط كل المنصات لقطة لكل نموذج بالمعرّف نفسه حتى يمكن مقارنة النتائج.
density: regular (42)
basic
caption: Button sizes items: 6 label-only
label: Button sizes items: 6 long-cells
label: Properties of the button items: 7 requirements
label: Requirements of the checkout items: 5 totals
caption: Components by status and platform rowHeaders: true items: 11 no-header
label: Accessibility of the button rowHeaders: true items: 5 empty-cells
caption: Orders this week items: 8 tags
caption: Status by platform rowHeaders: true items: 10 centered
caption: What each plan includes rowHeaders: true items: 7 mapping
label: Names on each platform rowHeaders: true items: 10 striped
caption: Visits by region striped: true rowHeaders: true items: 10 separation-divider
caption: Button sizes separation: divider items: 6 separation-gap
caption: Button sizes separation: gap items: 6 separation-gap-striped
caption: Orders this week separation: gap striped: true items: 8 wide
caption: Visits by region items: 15 wide-sticky-column
caption: Visits by region rowHeaders: true stickyFirstColumn: true items: 15 sticky-header
caption: Releases stickyHeader: true items: 15 japanese
caption: 表の設定 items: 6 rtl
caption: الطلب direction: rtl items: 8 rtl-wide-sticky-column
caption: الزيارات حسب المنطقة rowHeaders: true stickyFirstColumn: true direction: rtl items: 13 ltr-cells-rtl
label: Names on each platform rowHeaders: true direction: rtl items: 10 sortable
caption: Products sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending items: 7 sorted-end
caption: Products by price sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending items: 7 sortable-mixed
caption: Products sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending items: 6 sortable-long-headers
caption: Response times sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending items: 6 selection
caption: Products rowHeaders: true selection: multiple selectAllLabel: Select all products items: 8 selection-all
caption: Products rowHeaders: true selection: multiple selectAllLabel: Select all products items: 7 selection-striped
caption: Products separation: gap striped: true selection: multiple selectAllLabel: Select all products items: 7 selection-sticky-column
caption: Visits by region rowHeaders: true stickyFirstColumn: true selection: multiple selectAllLabel: Select all regions items: 13 selection-single
caption: Featured product rowHeaders: true selection: single items: 8 sortable-selection
caption: Products rowHeaders: true selection: multiple selectAllLabel: Select all products sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending status: 2 of 4 selected items: 7 empty
caption: Products items: 3 slots: empty empty-selection
caption: Products selection: multiple selectAllLabel: Select all products sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending status: 0 of 0 items: 3 slots: empty header-alone
label: Keyboard items: 2 loading
caption: Products rowHeaders: true selection: multiple selectAllLabel: Select all products loading: true status: Loading items: 7 loading-placeholders
caption: Products selection: multiple selectAllLabel: Select all products loading: true status: Loading items: 8 status
caption: Products status: 4 products items: 7 status-pagination
caption: Products status: 1–4 of 132 items: 7 slots: pagination status-long
label: Products status: Showing products 1 to 4 of the 132 that… items: 7 slots: pagination japanese-data
caption: 商品 rowHeaders: true selection: multiple selectAllLabel: すべての商品を選択 sortedAscendingLabel: 昇順で並べ替え済み sortedDescendingLabel: 降順で並べ替え済み status: 4 件中 1 件を選択中 items: 7 rtl-sortable-selection
caption: المنتجات rowHeaders: true selection: multiple selectAllLabel: تحديد كل المنتجات sortedAscendingLabel: مرتب تصاعديًا sortedDescendingLabel: مرتب تنازليًا status: تم تحديد 1 من 3 direction: rtl items: 6 slots: pagination rtl-empty
caption: المنتجات status: 0 منتج direction: rtl items: 3 slots: empty density: compact (7)
compact
caption: Button sizes density: compact items: 6 compact-tokens
label: Tokens of the table density: compact items: 16 striped-compact-tokens
label: Tokens of the table density: compact striped: true items: 16 wide-sticky-column-striped
caption: Visits by region density: compact striped: true rowHeaders: true stickyFirstColumn: true items: 14 sortable-compact
label: Products density: compact sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending items: 7 selection-compact
label: Products density: compact selection: multiple selectAllLabel: Select all products items: 7 data
caption: Products density: compact rowHeaders: true stickyHeader: true selection: multiple selectAllLabel: Select all products sortedAscendingLabel: sorted ascending sortedDescendingLabel: sorted descending status: 1–4 of 132 · 1 selected items: 7 slots: pagination مولَّد من specs/table.spec.json, tokens/comp/table.tokens.json, design/tokens/
استخدم الجدول لبيانات لها صفوف وأعمدة. ولا تستخدمه لوضع أشياء جنبًا إلى جنب: التقنيات المساعدة تُعلن الصفوف والأعمدة والترويسات لكل خلية، وهذا ضجيج حول محتوى ليس جدوليًّا. استخدم التخطيط لذلك.
وقائمة المفاتيح والقيم التي ليس لها بُعد ثانٍ أقرب إلى قائمة الوصف (description list) منها إلى جدول.
density |
ارتفاع الصف ذي السطر الواحد من النص | يُستخدم لـ |
|---|---|---|
regular (الافتراضي) |
46 | جدول يقرؤه الشخص: صفوف قليلة، وخلايا يلتفّ نصها. |
compact |
32 | جدول يمسحه الشخص بنظره: مئات الصفوف، ومعرّفات وقيم. |
ليس للصف ارتفاع خاص به. ارتفاعه ارتفاع أطول خلاياه.
separation |
ما يرسمه |
|---|---|
auto (الافتراضي) |
ما اختاره نظام التصميم: خط رفيع في النظام الافتراضي. |
divider |
خط رفيع واحد بين الصفوف، وتحت صف الترويسة، وفوق صفوف التذييل. |
gap |
فراغ بلا خط. |
الخاصية striped تظلّل الصفوف بالتناوب: تملأ كل صف ثانٍ من صفوف المتن بلون هادئ. استخدمها لجدول عريض فيه صفوف كثيرة متشابهة، حيث تضيّع العين صفها وهي تعبره. ويمكن جمعها مع أي من طريقتي الفصل.
- تأخذ الأعمدة عرضها من محتواها. لا يكون العمود أضيق أبدًا مما لا ينقسم فيه، وعمود النص الطويل يأخذ ما تتركه الأعمدة القصيرة.
- النص يلتفّ ولا يُقصّ أبدًا. والمعرّف لا ينقسم من داخله؛ بل يوسّع عموده.
- حاذِ عمود الأرقام إلى النهاية، لتصطفّ منازلها. واترك النص في البداية. ولا توسّط إلا علامة أو كلمة واحدة هي النوع نفسه من الجواب في كل صف.
- لا تعطِ العمود عرضًا بنفسك إلا حيث لا بدّ (
style="width: …"على خلية ترويسته). هو يحلّ محل ما كان المحتوى سيعطيه.
- يُمرَّر الجدول داخل صندوقه، ولا تُمرَّر الصفحة أبدًا. وخط رفيع على حافة من الصندوق يقول إن وراءها أعمدة. ولا يحتاج إلى منطقة تمرير حوله: فهو يعلّم حوافه بنفسه.
- الخاصية
sticky-first-columnتُبقي العمود الأول ظاهرًا ما دامت الأعمدة الأخرى تُمرَّر تحته. ويفلت من تلقاء نفسه حيث كان سيأخذ أكثر من نصف الصندوق، فلا يملأ العمودُ الباقي شاشةَ هاتف أبدًا. - الخاصية
sticky-headerتُبقي صف الترويسة ظاهرًا ما دامت الصفوف تُمرَّر. وتعمل حيث يحدّ تخطيطك ارتفاع الجدول (max-block-sizeعلى العنصر)؛ ولا تتبع الصفحة. - الجدول الذي على سطح غير سطح الصفحة (بطاقة، أو مربع حوار) يضبط
--tanrei-comp-table-sticky-container-colorعلى لون ذلك السطح حيث يُبقي صف ترويسته أو عموده الأول ظاهرًا، أو حيث يكون له سطر حالة. تُملأ هذه الأجزاء بذلك اللون، فلا تظهر من خلالها الصفوف والأعمدة التي تمرّ تحتها.
- اجعل العمود قابلًا للفرز حيث كان الشخص سيبحث عن صف به: اسم، أو تاريخ، أو مبلغ، أو فئة. وعمود الملاحظات ليس منها.
- تُرسم العلامة على كل عمود قابل للفرز، وفي السكون أيضًا، لتبيّن شاشة اللمس أي الترويسات يمكن ضغطها.
- يُفرز بعمود واحد في المرة. ومفتاح فرز ثانٍ عنصر تحكم من عندك.
- قل الترتيب الجديد في سطر الحالة حيث تريده أن يُنطق («مرتَّبة بحسب السعر، الأعلى أولًا»). الجدول لا يُعلن شيئًا بنفسه.
- ارتفاع صف الترويسة في جدول فيه عمود قابل للفرز لا يقل عن 44 في الكثافة regular، وعن 48 للمس في الكثافتين.
- أتِح للأشخاص اختيار الصفوف حيث يعملون على عدة سجلات دفعة واحدة: حذف، أو تصدير، أو نقل. ولسجل واحد، ضع الإجراء في صفه.
- استخدم اختيار صف واحد (
single) حيث يكون الجواب سجلًا واحدًا: خطة، أو عنوانًا، أو إصدارًا للمقارنة به. ولا تستخدمه لفتح سجل. - الضغط في أي موضع من الصف يختاره. وإن كان الصف يفتح سجله أيضًا، فضع لذلك رابطًا في ترويسة الصف، لا ضغطًا على الصف.
- قل في سطر الحالة كم صفًّا اختير («تم تحديد 3 من 132»). الجدول لا يعدّ شيئًا.
- ضع الأزرار التي تعمل على الصفوف المختارة فوق الجدول. هي لك أنت؛ والجدول يخبرك بما اختير.
- قدِّم «الكل (132)» بعنصر تحكم من عندك. خانة الاختيار في صف الترويسة تختار الصفوف التي أُعطيت للجدول، لا صفوف الصفحات الأخرى أبدًا.
- كل خانة اختيار موضع توقف في ترتيب التركيز. اجعل صفوف الصفحة الواحدة عددًا يستطيع الشخص عبوره بالمفتاح Tab، ووزِّع الباقي على صفحات. وأزرار الاختيار في الجدول الواحد كلها موضع توقف واحد.
- ارتفاع الصف الذي فيه خانة اختيار أو زر اختيار لا يقل عن 44 في الكثافة regular، وعن 48 للمس في الكثافتين. والصف المضغوط يبقى مضغوطًا لمؤشر الإدخال.
- الوسم والشيفرة المضمَّنة يكونان بالطراز tonal ما لم تختر طرازًا آخر، وتعبئة الطراز tonal تكاد تكون في النظام الافتراضي تعبئة الصف المختار، فيضيع شكلهما على الصف المختار. ويبقى نصهما مقروءًا. استخدم الوسم بالطراز outlined في جدول تُختار صفوفه.
- استخدم صفوفًا نائبة للتحميل الأول، بعدد يقارب الصفوف التي تتوقعها.
- أبقِ الصفوف عند تغيير الترتيب أو الصفحة. لا تضع صفوفًا نائبة أو مؤشر تحميل مكان صفوف يقرؤها الشخص.
- قل «جارٍ التحميل» في سطر الحالة، وقل ما وصل حين ينتهي التحميل («1 إلى 20 من 132»).
- أزِل الصفوف النائبة حين تزيل
loading. الصف الذي يُترك يبقى مرسومًا، ويُبلَّغ عنه في بنيات التنقيح.
- استخدم الحالة الفارغة لجدول بلا صفوف، وقل لماذا لا صفوف فيه وما العمل بعد ذلك. والحالة الفارغة التي تجيب عن بحث أو تصفية تأخذ
live، لتُعلَن النتيجة. - أبقِ سطر الحالة لما يعرضه الجدول الآن: كم صفًّا فيه وأيها معروض. والأزرار التي تعمل على الصفوف مكانها فوق الجدول.
- أعطِ كل جدول اسمًا: شرحًا (caption) يراه الجميع، أو
labelحيث يقول العنوان الذي فوقه ما هو أصلًا. - اكتب العمود الأول ترويسات صفوف (
<th scope="row">وrow-headers) كلما كان يقول ما هو كل صف. - اترك الخلية فارغة حيث لا شيء يقال. وحيث يكون الغياب نفسه معلومة («غير متاح») فاكتبه.
- لا تستخدم جدولًا لتخطيط شاشة.
- لا تعتمد على تظليل الصفوف ولا على اللون لحمل المعنى. الحالة في خلية كلمة، أو وسم تقولها تسميته.
- لا تضع شرطة في خلية فارغة لتملأها.
- لا تجعل صفًّا قابلًا للضغط، ولا تكتب في ترويسة علامة فرز أو ترتيبًا من عندك. تصير الترويسة قابلة للضغط بالترميز المذكور أعلاه ولا بشيء غيره. يُختار الصف بخانة اختياره أو بزر اختياره، والصفوف التي تُفتح ليست جزءًا من الجدول.
- لا تخفِّت الجدول ولا تعطِّله وهو يُحمَّل.
- الدور
table- التسمية
- The table is named by its caption, or by label where no caption is drawn (TBL-23). Header cells name their columns and, while rowHeaders is true, first cells name their rows; every cell is associated with both (TBL-24). While the table scrolls in its own box, that box is a region with the same name, so that a person who reaches it by keyboard hears what scrolls. A sortable column's button is named by the column's header, and the header cell carries the order (TBL-41). A row's checkbox or radio button is named by selectLabel, or by the row's header; the header row's checkbox by selectAllLabel (TBL-46). The radio buttons of a table are not wrapped in a radio group: each is exposed with its position among them (TBL-61). The status line is the product's sentence, told politely when it changes (TBL-52).
- الحد الأدنى للهدف
- تستوفي مساحة اللمس الحد الأدنى لحجم الهدف حتى لو كان الحجم المرئي أصغر. minimum 32px · pointer 44px · spacing 8px · touch 48px
لوحة المفاتيح
| المفاتيح | الإجراء |
|---|---|
| Tab | Moves focus to the table's box while the table scrolls in it; then to each sortable column's header button, in reading order; then, row by row, to the row's checkbox where rows can be chosen — where one row at most is chosen, once to the chosen row's radio button, or the first row's while none is — and to whatever in its cells takes focus. A table that does not scroll, sorts nothing and selects nothing is not a stop. |
| Enter / Space | On a sortable column's header: asks for the rows in that column's order — ascending, or descending where they already ascend by it. Where the header is a link, Enter alone. |
| Space | On a row's checkbox: chooses the row or lets it go. On the header row's checkbox: chooses every row, or lets every row go where all are chosen. On a row's radio button: chooses the row where it is not chosen; on the chosen row's, nothing. |
| Arrow keys | On a row's radio button: move to the next row's radio button (Down, and Right in a left-to-right layout) or the previous one's, wrapping, and choose that row. While the box has focus: scroll it. Left and right move the view left and right in either layout direction. |
| Page Up / Page Down, Home / End | While the box has focus: scroll it by a page, or to its beginning or end, as the platform scrolls a region. |
ملاحظات
- The table, its cells and every row that cannot be chosen take no input (TBL-16). What does — a sortable column's header, the checkboxes, a row that can be chosen (for a pointer), the scroll box — is a control, a larger target for one, or a region: a header's hit area is its whole cell and a checkbox's its whole selection cell, each at least the touch target high for touch at both densities (TBL-40, TBL-47); the compact density is lower than the pointer target, as a compact tree row is. What is pressable inside a cell (a link, a button) meets the minimum target by its own spec.
- A table is for data that has rows and columns. Do not use it to place things side by side: assistive technology announces rows, columns and headers for every cell, which is noise around content that is not tabular. Use the layout.
- Give every table a name (WCAG 1.3.1, 4.1.2): a caption that everyone sees is best; label is for a table whose heading already names it. A table that scrolls and has no name is a focus stop a person cannot tell from any other, so debug builds report it (TBL-23).
- Header cells are structure, not style (WCAG 1.3.1). Every column has a header cell unless the table has no header row at all; rowHeaders is true whenever the first column says what each row is. A table with two levels of column headers, or cells that span columns or rows, is outside this spec: split it.
- A scrolling box must be reachable and operable by keyboard (WCAG 2.1.1) and its focus must be visible (2.4.7): TBL-22. It is a stop only while it scrolls, so short tables add nothing to the focus order.
- Reflow (WCAG 1.4.10) exempts data tables from two-dimensional scrolling, and the table uses that exemption carefully: only its own box scrolls sideways, never the page (TBL-17, TBL-29), and a first column that stays in view is released on a narrow window (TBL-20).
- Meaning never rests on color: stripes are an aid to the eye and say nothing; a status in a cell is a word, or a tag whose label says it. An empty cell is exposed as empty (TBL-14); where the absence itself is information ("not available", "none"), write it in the cell.
- Contrast in the default system: cell text is on-surface and header text on-surface-variant, each held to 4.5:1 on every surface and container step by the token invariants, the stripe included. The lines are decorative: rows, headers and the region are exposed without them.
- Alignment end for numbers is for sighted comparison only; it changes nothing that is exposed. Text is never justified and never centred except in a column the product sets to center.
- A table with checkboxes stays a table (TBL-46). The grid pattern — one stop for the whole table, arrow keys from cell to cell — is for a sheet whose cells are edited or acted on; it takes over the keys a screen reader uses to read a table, it would replace the roles of the product's own table on the web, and it cannot work without script. The price of staying a table is one Tab stop for each row's checkbox, which pagination keeps to a page of rows.
- Where a person chooses one record — the plan to change to, the address to ship to, the build to compare against — selection is single, and the selection column holds a radio button, as the single selection of Carbon's data table and of Ant Design's table does: a radio says that the choices exclude each other, and that one is answered once it is (TBL-60). A radio is never cleared by its own activation (HTML, WAI-ARIA APG radio group), so the table offers no way back to none: a product that needs it gives a control of its own. The radio buttons share one name and so behave as one radio group — one Tab stop, the arrow keys move the choice (TBL-61) — which also keeps a page of rows to one stop where checkboxes would be one each. They are not wrapped in an element of the radio group role, which cannot hold the rows of a table; each radio is heard with its row's name, its state and its position ("Select Kettle, radio button, checked, 3 of 4"), inside the table's name. The grid alternative, rows that carry aria-selected (React Aria's table, single selection mode), is declined for the reasons above.
- A press anywhere on a row that can be chosen presses its checkbox or radio button (TBL-58), as a press on a card or on a list item that can be chosen does: a larger target for a pointer (WCAG 2.5.8), not a second control. The row gets no role, no tab index and no selected state — ARIA allows aria-selected on a row only inside a grid or a treegrid — so that the keyboard and assistive technology meet the one control the row has, its named checkbox or radio button, and lose nothing. What a cell holds that answers a press (a link, a button, a field) keeps it, and a drag that selects text chooses nothing, so the text of a table can still be selected and copied.
- Sorting is asked for and shown, never done, by the table (TBL-33, TBL-34): the header cell says the order (WCAG 1.3.1, 4.1.2) and the mark repeats it to the eye without relying on color (1.4.1). One column is sorted at a time.
- A chosen row is said by its checkbox or radio button, checked; the fill is the same fact for the eye and is not relied on (WCAG 1.4.1), which is also what is left in forced colors (TBL-54). In the default system the text of a chosen row is on-secondary-container on a layer of that color over the surface, the pair a selected list item and a selected tree row are drawn in, held to 4.5:1 by the token invariants, and the fill stands 1.1:1 at least from a hovered row; the contrast of what a cell holds beside plain text on that fill is its own component's.
- Status messages (WCAG 4.1.3): the table announces nothing itself (TBL-25) and gives the product one polite place to say what changed — rows found, rows chosen, loading and loaded (TBL-52). An Empty state in the empty slot that answers a search or a filter is live by its own spec.
- While loading, the table is busy and placeholder rows are hidden (TBL-49, TBL-50), so that a person does not walk through rows of nothing. Do not take the rows a person was reading away for a spinner where they can stay.
- Focus is never lost to a change of rows (TBL-25) and a focused control is never left under a part kept in view (TBL-36; WCAG 2.4.11).
مولَّد من specs/table.spec.json, tokens/sys/measure.tokens.json
- الجدول الأصلي يبقى جدولًا: الصفوف وترويسات الأعمدة وترويسات الصفوف والخلايا هي ما يعرّفه المتصفح نفسه، بتظليل الصفوف أو من دونه، بأي من الكثافتين، وهو يُمرَّر، وحين يُبقى عمود أو تُبقى الترويسة ظاهرة.
- الشرح يسمّي الجدول. ومن دونه تصير
labelقيمةaria-labelللجدول ولا تُرسم. ومع الاثنين يكون الشرح هو الاسم. - ما دام الجدول يُمرَّر داخل صندوقه، فذلك الصندوق موضع توقف واحد في ترتيب التركيز، قبل أي شيء في خلاياه، ومنطقة (region) باسم الجدول. تمرّره مفاتيح الأسهم والمفاتيح Page Up و Page Down و Home و End. والجدول الذي لا يُمرَّر لا يضيف موضع توقف.
- حلقة التركيز تُرسم داخل حافة الصندوق، فلا يقصّها شيء.
- حيث تكون الخلايا الأولى في الجدول عناصر
<td>عادية (كما في ما يُولَّد من Markdown) وrow-headersمعيَّنة، يعطيها العنصر دور ترويسة الصف. - في التخطيط من اليمين إلى اليسار تجري الأعمدة من اليمين، ويبدأ التمرير من اليمين، والعمود الأول الباقي يكون في اليمين، ويحتفظ نص كل خلية باتجاهه: الرقم لا يُعكس أبدًا.
- عند تكبير النص إلى 200% يُحسب عرض الأعمدة من جديد، ويُمرَّر الجدول داخل صندوقه حيث لم تعد تتسع.
- في الألوان المفروضة (forced colors) كل خط بلون النص، والجدول ذو الصفوف المظلَّلة يرسم خطوط صفوفه أيًّا كانت طريقة فصله، لأن هذا الوضع يزيل التعبئات.
- خلية ترويسة العمود القابل للفرز ترويسة عمود تحمل الترتيب (
aria-sort)، وزرك الذي فيها اسمه نص الترويسة. والعلامة مخفيّة عن التقنيات المساعدة. - زر الفرز موضع توقف واحد في ترتيب التركيز. يضغطه المفتاحان Enter و Space؛ وحيث يكون رابطًا فالمفتاح Enter وحده. ويظل التركيز عليه بعد الضغط. وحيث تضع صف ترويسة جديدًا مع الصفوف، ينتقل التركيز إلى الزر الجديد للعمود نفسه.
- مؤشر التركيز لزر الفرز يُرسم داخل خلية ترويسته، والزر الذي يأخذ التركيز بلوحة المفاتيح يُخرَج من تحت عمود أول أو سطر حالة يبقى ظاهرًا.
- الصف الذي فيه الحالة الفارغة صف واحد من خلية واحدة. لا يضيف إليه الجدول دورًا ولا كلمة.
- فقرة سطر الحالة حالة (status): تغيّر نصها يُعلَن بتهذيب، من دون نقل التركيز، ولا يُعلَن شيء حين يُرسم أول مرة.
- في الألوان المفروضة تأخذ تسمية زر الفرز وعلامته لون نص الأزرار، والعمود المفروز به يدل عليه سهمه وحده.
- من دون سكربت تُرسم أزرار الفرز وعلاماتها، والصف الذي فيه الحالة الفارغة، وسطر الحالة كما هي، ويعمل رابط الفرز. أما الحدث
sortفيحتاج إلى سكربت. - الجدول الذي يمكن اختيار صفوفه يبقى جدولًا. لا يأخذ دور الشبكة (grid)، ولا يأخذ الصف حالة التحديد (selected)، ولا مفتاح ينقل التركيز من خلية إلى خلية. وكون خانة اختيار الصف أو زر اختياره محدَّدًا (checked) هو ما يقول إن الصف مختار.
- خانة الاختيار في صف الترويسة تقول إنها محدَّدة، أو غير محدَّدة، أو مختلطة ما دامت غير محسومة، وتكون معطَّلة ما دام لا صف يمكن اختياره.
- كل خانة اختيار موضع توقف خاص بها في ترتيب التركيز: خانة الاختيار وأزرار الفرز في صف الترويسة أولًا، ثم خانة اختيار كل صف. والمفتاح Space يحدّدها أو يلغي تحديدها.
- خانة اختيار الصف أو زر اختياره، إن لم يكن له اسم خاص به، تسمّيه خلية ترويسة الصف.
- مع
singleتكون أزرار الاختيار كلها موضع توقف واحدًا: يصل المفتاح Tab إلى زر اختيار الصف المختار (وإلى زر الصف الأول ما دام لا صف مختارًا)، والضغطة التالية تخرج من الجدول. وتنتقل مفاتيح الأسهم إلى زر اختيار الصف التالي أو السابق وتختار ذلك الصف، وتعود من الطرف الآخر عند النهاية. ويُسمع كل زر اختيار مع اسم صفه وحالته وموضعه بينها، مثل «تحديد الفريق، زر اختيار، محدَّد، 2 من 3». ولا تُلَفّ في مجموعة أزرار اختيار، لأن المجموعة لا تستطيع أن تحمل صفوف جدول. - الضغط في أي موضع من خلية اختيار يضغط خانة الاختيار التي فيها. وحين تضع صفوفًا جديدة أو تعيد ترتيب الصفوف والتركيز على خانة اختيار، ينتقل التركيز إلى خانة اختيار الصف الذي له قيمة
data-valueنفسها. - بالنسبة إلى المؤشر، الصف الذي يمكن اختياره هدف أكبر لخانة الاختيار فيه. لا يأخذ الصف دورًا ولا موضع تركيز ولا حالة اختيار، فتصل لوحة المفاتيح وقارئ الشاشة إلى خانة الاختيار باسمها كما من قبل، ويبقى نص الصف قابلًا للتحديد والنسخ.
- في الألوان المفروضة تزول تعبئة الصف المختار، وتبقى خانة الاختيار أو زر الاختيار دالّةً على أن الصف مختار.
- ما دامت
loadingمعيَّنة، يُعرَض الجدول للتقنيات المساعدة مشغولًا (aria-busy). والصفوف النائبة مخفيّة عن التقنيات المساعدة، فيسمع الشخص أن الجدول مشغول ولا يسمع صفوفًا من لا شيء. الجدول لا يُعلن شيئًا بنفسه حين يبدأ التحميل أو ينتهي. - من دون سكربت يُرسم عمود الاختيار، والصف المختار (بالخاصية
data-selected، أو بخانة اختيار أصلية محدَّدة)، والصفوف النائبة كما هي، وتعمل خانات الاختيار الأصلية في نموذجك. أما مطابقة خانة الاختيار في صف الترويسة والحدثselectionchangeفتحتاجان إلى سكربت. ومن دون سكربت لا يُظهر الصف حالة التحويم ولا مؤشر اليد، والضغط بجانب خانة الاختيار لا يغيّر شيئًا. - يُبلَّغ في بنيات التنقيح عن جدول بلا اسم، وعن كثافة أو طريقة فصل أو قيمة
selectionغير معروفة، وعن صف تذييل معطى قبل المتن، وعن صف فيه خلايا أكثر أو أقل من الأعمدة، وعن عمود قابل للفرز بلا ترويسة، وعن عمودين مفروز بهما في آن واحد، وعن خانة اختيار صف بلا اسم، وعن عدة صفوف مُعلَّمة بأنها مختارة في آن واحد، وعن عنصر تحكم من نوع خاطئ معsingle، وعن صف نائب في جدول لا يُحمَّل.
- سمِّ الجدول. الجدول الذي يُمرَّر وليس له اسم موضع توقف للتركيز لا يستطيع الشخص تمييزه من أي موضع آخر.
- أعطِ كل عمود خلية ترويسة، وعلِّم ترويسات الصفوف بأنها ترويسات صفوف. الترويسات بنية، لا تنسيق.
- أبقِ الجدول الذي يحتاج إلى مستويين من ترويسات الأعمدة، أو إلى خلايا ممتدة، أبسط ما تستطيع. هي تعمل كما تعمل في HTML، لكن لا جزء من هذا المكوّن مصمَّم حولها؛ وتقسيم الجدول أوضح في العادة.
- ما يُضغط داخل خلية (رابط، أو زر) يحتفظ باسمه وتركيزه وحجم هدفه. أعطِه حيّزًا.
- بعد أن ترتّب الصفوف، اكتب قيمة
aria-sortالجديدة على خلايا الترويسة: القيمةascendingأوdescendingعلى واحدة، وnoneعلى سائر الخلايا القابلة للفرز. ولا تترك الخاصية على ترويسة ليس فيها زر. - أبقِ فقرة سطر الحالة في الصفحة ما دامت بلا نص، ليُعلَن النص التالي الذي تعطيها إياه.
- سمِّ خانة الاختيار في صف الترويسة بالخاصية
select-all-label، وسمِّ خانة اختيار كل صف بالخاصيةlabelالخاصة بها حيث ليس للجدول ترويسات صفوف. - قل في سطر الحالة متى يبدأ التحميل ومتى ينتهي. هو الموضع الوحيد الذي يُعلَن فيه التغيّر.
لا يأتي Tanrei بكلمات من عنده. الجدول يطلب منك كلمة واحدة، هي اسم خانة الاختيار في صف الترويسة: الخاصية select-all-label على العنصر، أو selectAllLabel بالاستدعاء setWords(). والكلمتان الأخريان تكتبهما في ترميزك.
| الكلمة | أين تعطيها |
|---|---|
| اسم خانة الاختيار في صف الترويسة («تحديد كل المنتجات») | الخاصية select-all-label على العنصر، أو مرة واحدة لكل الجداول بالاستدعاء setWords({ selectAllLabel: 'تحديد كل الصفوف' }). |
| اسم خانة اختيار الصف أو زر اختياره («تحديد مطحنة يدوية») | الخاصية label على عنصر <tanrei-checkbox> أو <tanrei-radio-button> في الصف، أو aria-label على عنصر أصلي. ومع ترويسات الصفوف لك أن تتركه: ترويسة الصف تسمّي خانة الاختيار. |
| جملة سطر الحالة («تم تحديد 2 من 4»، «جارٍ التحميل») | فقرة سطر الحالة، أو status على العنصر حيث لا تكتب فقرة. |
من دون اسم لخانة الاختيار في صف الترويسة، تُرسم خانة الاختيار مع ذلك وتعمل، وليس لها اسم يُتاح للتقنيات المساعدة، وبنية التنقيح تبلّغ عن الكلمة الناقصة.