menu قائمة —— تنقل
القائمة هي الهيكل العظمي لتنقّل المواقع: قائمة عليا أفقية، وقائمة جانبية عمودية، وقائمة
أيقونات قابلة للطي. وفرقها عن dropdown هو الثبات والتسلسل الهرمي——فالقائمة دائمة في
الصفحة، وتعشّش المستويات الفرعية بلا حدّ؛ أما القائمة المنسدلة فقائمة عمليات منبثقة مؤقتة.
وما يديره core/menu هو حساب حالات التوسيع ودلالات إمكانية الوصول، أما التخطيط وتحديد
الموضع فكله في SCSS.
متى تُستخدم
Section titled “متى تُستخدم”- لتطبيق
<LhMenu>/<LhSubMenu>/<LhMenuItem>: التنقّل الرئيسي للموقع، والشريط الجانبي للوحة الإدارة، ودليل موقع التوثيق. - التنقّل الذي يحتاج أكثر من مستويين.
- لا ينبغي استخدامها: صف من أزرار العمليات المتراصّة (استخدم
space+button)؛ قائمة العمليات المنبثقة المؤقتة (استخدمdropdown)؛ تبديل المحتوى على شكل علامات تبويب (استخدمtabs).
طريقة الاستيراد
Section titled “طريقة الاستيراد”import { getMenuClassNames, getMenuItemClassNames, getSubMenuClassNames, getMenuItemGroupClassNames, toggleMenuOpen, isMenuOpen, moveMenuFocus, getMenuAria, getMenuItemAria, getSubMenuAria,} from '@lh-ui/share/core/menu'import type { LhMenuMode } from '@lh-ui/share/core/menu'نية التصميم
Section titled “نية التصميم”الوضع الأكورديون يسمح بصفر مفتوح: عند uniqueOpened: true يُرجع toggleMenuOpen عند
النقر على عنصر مفتوح مصفوفة فارغة [] (طي الكل)، وعند النقر على عنصر غير مفتوح يُرجع [key]
(يحتفظ به وحده). ولاحظ أنها ليست «الإبقاء على عنصر مفتوح واحد على الأقل»——صفر مفتوح مسموح
به، مما يجعل «طي الكل» حالة قابلة للتفاعل.
الوضع العادي يحذف ويضيف لا يستبدل: تُرجع مصفوفة جديدة (تحذف عبر filter / تضيف)، مع
إبقاء بقية العناصر المفتوحة كما هي——ففي القوائم متعددة المستويات فتح عدة قوائم فرعية في
الوقت نفسه حاجة شائعة.
moveMenuFocus غلاف @deprecated: انتقلت خوارزمية التنقّل الدوري إلى
getNextListIndex في core/shared/keyboard، واحتفظت هذه الدالة بالتوقيع القديم (يستقبل مصفوفة
عناصر DOM) للاستدعاءات القائمة في الأسفل. وما تفعله: تصفية tabIndex === -1 (تكافئ العناصر
المعطّلة في النواة)، والرجوع إلى العنصر الأول عندما لا يكون العنصر الحالي في القائمة. استخدم
getNextListIndex مباشرة في الأكواد الجديدة.
ثلاثية aria: الحاوية role="menu"، والعنصر الورقي role="menuitem"، وعنوان القائمة الفرعية
role="menuitem" + aria-expanded + aria-controls. ولاحظ أن عنوان القائمة الفرعية ليس
دورًا فرعيًا من menuitem——ففي بنية قائمة APG، يبقى العنصر القابل للتوسيع menuitem، ويُعبَّر
عن حالة التوسيع بـ aria-expanded.
فصل أسماء الأصناف عن الحالات: --active / --disabled / --opened كلها أصناف منطقية
مستقلة، تجمع SCSS منها الأنماط. أما --collapse (حالة الطي) فاسم صنف على مستوى الحاوية، يؤثر
في طريقة تصيير كل العناصر (قائمة الأيقونات تعرض الأيقونات فقط، ويكمّل tooltip النصوص).
جدول الدوال
Section titled “جدول الدوال”| الدالة | التوقيع | الناتج | الدور |
|---|---|---|---|
getMenuClassNames |
(state: { mode, collapse }) => string[] |
أسماء الأصناف | نمط التخطيط + حالة الطي |
getMenuItemClassNames |
(state: { active, disabled }) => string[] |
أسماء الأصناف | العنصر الورقي |
getSubMenuClassNames |
(state: { active, disabled, opened }) => string[] |
أسماء الأصناف | القائمة الفرعية |
getMenuItemGroupClassNames |
() => string[] |
أسماء الأصناف | حاوية المجموعة |
toggleMenuOpen |
(openeds, key, uniqueOpened) => string[] |
مصفوفة جديدة | توسيع/طي (غير مُتغيِّر) |
isMenuOpen |
(openeds, key) => boolean |
هل هو مفتوح | استعلام includes |
moveMenuFocus ⚠️ |
(items, currentEl, direction) => HTMLElement | null |
العنصر الهدف | نقل التركيز ↑/↓ |
getMenuAria |
(id?) => LhAriaAttrs |
حزمة aria | role=menu |
getMenuItemAria |
(state: { disabled? }) => LhAriaAttrs |
حزمة aria | menuitem |
getSubMenuAria |
(options: { opened, disabled?, menuId? }) => LhAriaAttrs |
حزمة aria | menuitem + expanded |
⚠️ غلاف توافق @deprecated، والأكواد الجديدة تستخدم getNextListIndex من shared/keyboard.
تعريفات الأنواع
Section titled “تعريفات الأنواع”export type LhMenuMode = 'horizontal' | 'vertical'إمكانية الوصول
Section titled “إمكانية الوصول”getMenuAria('menu-1') // { role: 'menu', id: 'menu-1' }getMenuItemAria({ disabled: true }) // { role: 'menuitem', 'aria-disabled': true }getSubMenuAria({ opened: true, menuId: 'menu-1' })// { role: 'menuitem', 'aria-expanded': true, 'aria-controls': 'menu-1' }getSubMenuAria({ opened: false })// { role: 'menuitem', 'aria-expanded': false }- القيمة الكاذبة لـ aria-expanded تُخرج أيضًا:
{ 'aria-expanded': false }ذات معنى——فهي تخبر قارئ الشاشة صراحةً «هذه القائمة الفرعية مطوية». وهذا يتبع اصطلاحshared/a11y: لا يُحذف إلا undefined (غير مُمرَّر)، أما false فقيمة حالة مشروعة. - اختلاف نمط التنقّل بلوحة المفاتيح: القوائم الأفقية تستخدم ←/→، والقوائم العمودية ↑/↓،
وهذا اصطلاح نمط القوائم في APG. وينتظر مستخدم قارئ الشاشة السلوك المقابل عند الضغط على
مفاتيح الأسهم——
moveMenuFocusيوفّر الاتجاه العمودي 1/-1 فقط، وتُعدَّل ←/→ للقوائم الأفقية في طبقة واجهة المستخدم وتُستدعى الدالة نفسها. - aria-current لتمييز الصفحة الحالية: يُستحسن للعنصر النشط، إضافةً إلى صنف
--active، أن تضيف طبقة واجهة المستخدمaria-current="page"(دلالة موافقة للعنصر الأخير فيbreadcrumb) لينطق قارئ الشاشة «الصفحة الحالية». - تعويض النصوص في القوائم المطوية: في وضع
collapseلا تُعرض إلا الأيقونات، فيجب وضع نص عنصر القائمة فيaria-labelأو tooltip عند التحويم——وإلا فسيكون زر الأيقونة بلا اسم أمام قارئ الشاشة. - تخطّي العناصر المعطّلة: العناصر ذات
--disabledلا تستجيب للنقر (يعترضها المستخدم طبقة)، لكنها تبقى في DOM. ويجب على التنقّل بلوحة المفاتيح تخطّيها (تصفية tabIndex فيmoveMenuFocus).
أمثلة الاستخدام
Section titled “أمثلة الاستخدام”import { toggleMenuOpen, isMenuOpen } from '@lh-ui/share/core/menu'
toggleMenuOpen(['a', 'b'], 'b', false) // ['a'] (الوضع العادي يطوي b)toggleMenuOpen(['a'], 'b', false) // ['a', 'b'] (يفتح b)toggleMenuOpen(['a'], 'a', true) // [] (أكورديون: النقر ثانية يطوي، صفر مفتوح مسموح)toggleMenuOpen(['a'], 'b', true) // ['b'] (أكورديون: يُحتفظ بـ b وحده)
isMenuOpen(['a', 'b'], 'a') // trueisMenuOpen(['a'], 'c') // false// حالة التوسيع المُتحكَّم بها (طبقة واجهة المستخدم): openeds مصفوفة لا Set،// لتسهيل التسلسل وv-modelconst openeds = ref<string[]>([])function onSubMenuClick(key: string) { openeds.value = toggleMenuOpen(openeds.value, key, props.uniqueOpened)}<!-- Vue3: قائمة جانبية عمودية --><template> <LhMenu mode="vertical" :collapse="collapsed" :unique-opened="true"> <LhSubMenu index="1"> <template #title><LhIcon name="settings" /><span>إعدادات النظام</span></template> <LhMenuItem index="1-1" @click="go('/users')">إدارة المستخدمين</LhMenuItem> <LhMenuItem index="1-2" disabled>إدارة الصلاحيات</LhMenuItem> </LhSubMenu> <LhMenuItem index="2" @click="go('/profile')">الملف الشخصي</LhMenuItem> </LhMenu></template>// React: openeds عبر useState<string[]>؛ ونقر عناصر القائمة ينفّذ navigate؛// وحالة الطي تُمرَّر إلى القوائم الفرعية عبر context (بدلاً من تمرير الخصائص في كل مستوى)// uniapp: <view> + catchtap؛ ويستخدم وضع الطي في جانب التطبيقات المصغّرة// رسم موضع للعرض بعرض px ثابتالأنماط والرموز
Section titled “الأنماط والرموز”| اسم الصنف | الدلالة |
|---|---|
.lh-menu |
الحاوية |
.lh-menu--{horizontal|vertical} |
نمط التخطيط |
.lh-menu--collapse |
حالة الطي (أيقونات فقط) |
.lh-menu-item |
العنصر الورقي |
.lh-menu-item--active / --disabled |
تنشيط / تعطيل |
.lh-sub-menu / --active / --disabled / --opened |
عنوان القائمة الفرعية |
.lh-sub-menu__popup |
القائمة الفرعية المنبثقة (القوائم الأفقية) |
.lh-menu-item-group |
مجموعة (بعنوان مجموعة) |
خلفية العنصر النشط --lh-menu-item-active-background، ولون النص --lh-menu-item-color (يجب
قلب الرموز في –lh-menu-item-color صراحةً في الوضع الداكن، انظر فخ الوضع الداكن في
AGENTS.md الجذر)؛ ورمز الارتفاع --lh-menu-item-height.
ملاحظات
Section titled “ملاحظات”- openeds مصفوفة لا كائن: الترتيب غير مهم لكن يجب إزالة التكرار؛ ويضمن
toggleMenuOpenالوحاد. والمصفوفة أبسط حدسيًا عند تسلسلها في query التوجيه أو localStorage. - الأكورديون يتحكم في المستوى نفسه فقط: يجعل
uniqueOpenedالقوائم الفرعية المباشرة ضمن القائمة نفسها حصرية متبادلة؛ أما القوائم الفرعية من المستوى الثالث فلا تتأثر (يحتفظ كل منها بـ openeds الخاص به). - أبناء القائمة الأفقية منبثقون: المستوى الأول يُصفّ أفقيًا، والمستوى الثاني ينبثق نحو
الأسفل (الصنف
lh-sub-menu__popup، وتحديد الموضع عبرshared/popper)؛ أما أبناء القائمة العمودية فيتمددون داخليًا (يزاحون المحتوى الأسفل). وتختلف بنية DOM بين النمطين، ويحركها mode. --activeيحتاج إلى مزامنة التوجيه: تتبع حالة التنشيط في القائمة عادةً التوجيه الحالي، فتستمع طبقة واجهة المستخدم لتغيّر التوجيه وتحدّث active index——ولا تمس core التوجيه.- tooltip في حالة الطي: عند
collapse: trueيتبقّى من عنصر القائمة الأيقونة فقط، ويجب أن يُكمَّل النص عبرtooltip(يُعرض عند التحويم). وهذه نقطة تعاون بين مكوّنين. - اعتماد
moveMenuFocusعلى DOM: يستقبلHTMLElement[]، فلا يُستدعى إلا في المتصفح (يُتجاوز في SSR)؛ ويصفّي tabIndex لا يقرأ صنف disabled——فعلى طبقة واجهة المستخدم ضمان أن tabIndex للعناصر المعطّلة هو -1. - menu وdropdown الدور نفسه لكن الاستعمال مختلف: كلاهما يستخدم
role="menu". menu تنقّل دائم، وdropdown قائمة عمليات مؤقتة——لا تنتحل التنقّل بالموقع عبر dropdown (يختفي عند التحديث، ولا يدعم الروابط العميقة).
@lh-ui/share/core/menu → toggleMenuOpen( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue