قائمة منسدلة Dropdown
قائمة أوامر تنبثق للأسفل. يمكن أن يكون المُشغِّل أي عنصر، وتُعرَّف عناصر القائمة عبر command، وعند النقر يُمرَّر عبر حدث command. مطبَّقة وفق نمط WAI-ARIA للقوائم: تدوير بمفاتيح الأسهم، والانتقال إلى الأول والأخير عبر Home/End، والتشغيل عبر Enter/Space، والإغلاق مع إعادة التركيز عبر Esc.
الاستخدام الأساسي
آخر أمر: —
<template>
<LhDropdown @command="onCommand">
<LhButton>{{ selectedLabel || 'المزيد من العمليات' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="new">جديد</LhDropdownItem>
<LhDropdownItem command="edit">تحرير</LhDropdownItem>
<LhDropdownItem command="delete">حذف</LhDropdownItem>
</template>
</LhDropdown>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const lastCommand = ref('')
// نص المُشغِّل يُدار من قبل المستخدم: لا يطرح حدث command سوى قيمة الأمر،
// ويترك طريقة عرض التصنيف للخارج
const labelMap: Record<string, string> = { new: 'جديد', edit: 'تحرير', delete: 'حذف' }
const selectedLabel = ref('')
function onCommand(command: string | number | undefined) {
lastCommand.value = String(command)
selectedLabel.value = labelMap[String(command)] ?? String(command)
}
</script><script>
export default {
data() {
return {
lastCommand: '',
// نص المُشغِّل يُدار من قبل المستخدم: لا يطرح حدث command سوى قيمة الأمر،
// ويترك طريقة عرض التصنيف للخارج
labelMap: { new: 'جديد', edit: 'تحرير', delete: 'حذف' },
selectedLabel: '',
}
},
methods: {
onCommand(command) {
this.lastCommand = String(command)
this.selectedLabel = this.labelMap[String(command)] ?? String(command)
},
},
}
</script>
<template>
<LhDropdown @command="onCommand">
<LhButton>{{ selectedLabel || 'المزيد من العمليات' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="new">جديد</LhDropdownItem>
<LhDropdownItem command="edit">تحرير</LhDropdownItem>
<LhDropdownItem command="delete">حذف</LhDropdownItem>
</template>
</LhDropdown>
</template>طريقة التشغيل
الافتراضي هو click، ويمكن أيضًا التغيير إلى التمرير بالفأرة أو زر الفأرة الأيمن.
<script setup lang="ts">
import { ref } from 'vue'
const hoverLabel = ref('')
function onHoverCommand(command: string | number | undefined) {
hoverLabel.value = String(command)
}
const contextLabel = ref('')
function onContextCommand(command: string | number | undefined) {
contextLabel.value = String(command)
}
</script>
<template>
<!-- تشغيل بالتمرير -->
<LhDropdown trigger="hover" @command="onHoverCommand">
<LhButton>{{ hoverLabel || 'تشغيل بالتمرير' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="hover-1">الخيار الأول</LhDropdownItem>
<LhDropdownItem command="hover-2">الخيار الثاني</LhDropdownItem>
</template>
</LhDropdown>
<!-- تشغيل بزر الفأرة الأيمن -->
<LhDropdown trigger="contextmenu" @command="onContextCommand">
<LhButton>{{ contextLabel || 'تشغيل بزر الفأرة الأيمن' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="ctx-1">تحديث</LhDropdownItem>
<LhDropdownItem command="ctx-2">نسخ</LhDropdownItem>
</template>
</LhDropdown>
</template><script>
export default {
data() {
return { hoverLabel: '', contextLabel: '' }
},
methods: {
onHoverCommand(command) {
this.hoverLabel = String(command)
},
onContextCommand(command) {
this.contextLabel = String(command)
},
},
}
</script>
<template>
<!-- تشغيل بالتمرير -->
<LhDropdown trigger="hover" @command="onHoverCommand">
<LhButton>{{ hoverLabel || 'تشغيل بالتمرير' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="hover-1">الخيار الأول</LhDropdownItem>
<LhDropdownItem command="hover-2">الخيار الثاني</LhDropdownItem>
</template>
</LhDropdown>
<!-- تشغيل بزر الفأرة الأيمن -->
<LhDropdown trigger="contextmenu" @command="onContextCommand">
<LhButton>{{ contextLabel || 'تشغيل بزر الفأرة الأيمن' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="ctx-1">تحديث</LhDropdownItem>
<LhDropdownItem command="ctx-2">نسخ</LhDropdownItem>
</template>
</LhDropdown>
</template>التشغيل عبر
contextmenuيمنع القائمة السياقية الافتراضية للمتصفح.
التعطيل والفاصل
<script setup lang="ts">
import { ref } from 'vue'
const actionLabel = ref('')
function onActionCommand(command: string | number | undefined) {
actionLabel.value = String(command)
}
</script>
<template>
<!-- قائمة عمليات مع عنصر معطّل وفاصل -->
<LhDropdown @command="onActionCommand">
<LhButton>{{ actionLabel || 'عملية' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="export">تصدير</LhDropdownItem>
<LhDropdownItem command="import">استيراد</LhDropdownItem>
<LhDropdownItem divided command="audit">سجل التدقيق</LhDropdownItem>
<LhDropdownItem disabled command="purge">تفريغ البيانات</LhDropdownItem>
</template>
</LhDropdown>
</template><script>
export default {
data() {
return { actionLabel: '' }
},
methods: {
onActionCommand(command) {
this.actionLabel = String(command)
},
},
}
</script>
<template>
<!-- قائمة عمليات مع عنصر معطّل وفاصل -->
<LhDropdown @command="onActionCommand">
<LhButton>{{ actionLabel || 'عملية' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="export">تصدير</LhDropdownItem>
<LhDropdownItem command="import">استيراد</LhDropdownItem>
<LhDropdownItem divided command="audit">سجل التدقيق</LhDropdownItem>
<LhDropdownItem disabled command="purge">تفريغ البيانات</LhDropdownItem>
</template>
</LhDropdown>
</template>يبقى العنصر المعطّل قابلًا للتركيز (
aria-disabled="true")، فيتمكن قارئ الشاشة من قراءته؛ لكن مفاتيح الأسهم تتخطاه، ولا يُطلق النقر ولا Enter/Space حدثcommand.
الموضع والارتفاع الأقصى
<script setup lang="ts">
import { ref } from 'vue'
const itemLabel = ref('')
function onItemCommand(command: string | number | undefined) {
itemLabel.value = `العنصر ${command}`
}
</script>
<template>
<!-- أسفل مع محاذاة الطرف وتقييد الارتفاع الأقصى -->
<LhDropdown placement="bottom-end" :max-height="160" @command="onItemCommand">
<LhButton>{{ itemLabel || 'أسفل مع محاذاة الطرف' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem v-for="i in 12" :key="i" :command="i">العنصر {{ i }}</LhDropdownItem>
</template>
</LhDropdown>
</template><script>
export default {
data() {
return { itemLabel: '' }
},
methods: {
onItemCommand(command) {
this.itemLabel = `العنصر ${command}`
},
},
}
</script>
<template>
<!-- أسفل مع محاذاة الطرف وتقييد الارتفاع الأقصى -->
<LhDropdown placement="bottom-end" :max-height="160" @command="onItemCommand">
<LhButton>{{ itemLabel || 'أسفل مع محاذاة الطرف' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem v-for="i in 12" :key="i" :command="i">العنصر {{ i }}</LhDropdownItem>
</template>
</LhDropdown>
</template>التحكم الظاهر والإخفاء وسلوك النقر
يتحكم v-model:visible يدويًا في الظهور والإخفاء؛ ويتحكم hide-on-click فيما إذا كانت القائمة تُطوى تلقائيًا بعد الاختيار؛ ويضيف popper-class صنفًا مخصصًا للطبقة المنبثقة؛ ويحدد teleport-target حاوية التركيب.
<script setup lang="ts">
import { ref } from 'vue'
const menuVisible = ref(false)
</script>
<template>
<!-- تحكم ظاهر وإخفاء: لا يُطوى بعد الاختيار -->
<LhDropdown
v-model:visible="menuVisible"
:hide-on-click="false"
popper-class="demo-dropdown-popper"
>
<LhButton>{{ menuVisible ? 'طي' : 'عرض' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="copy">نسخ (لا يُطوى بعد الاختيار)</LhDropdownItem>
<LhDropdownItem command="paste">لصق</LhDropdownItem>
</template>
</LhDropdown>
</template><script>
export default {
data() {
return { menuVisible: false }
},
}
</script>
<template>
<!-- تحكم ظاهر وإخفاء: لا يُطوى بعد الاختيار -->
<LhDropdown
v-model:visible="menuVisible"
:hide-on-click="false"
popper-class="demo-dropdown-popper"
>
<LhButton>{{ menuVisible ? 'طي' : 'عرض' }} ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="copy">نسخ (لا يُطوى بعد الاختيار)</LhDropdownItem>
<LhDropdownItem command="paste">لصق</LhDropdownItem>
</template>
</LhDropdown>
</template><script setup lang="ts">
</script>
<template>
<!-- التركيب في حاوية محددة -->
<LhDropdown teleport-target="#dropdown-zone">
<LhButton>المزيد ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="a">خيار</LhDropdownItem>
</template>
</LhDropdown>
</template><script>
export default {}
</script>
<template>
<!-- التركيب في حاوية محددة -->
<LhDropdown teleport-target="#dropdown-zone">
<LhButton>المزيد ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="a">خيار</LhDropdownItem>
</template>
</LhDropdown>
</template>واجهة برمجة تطبيقات Dropdown
الخصائص
| الخاصية | الوصف | النوع | الافتراضي |
|---|---|---|---|
| trigger | طريقة التشغيل | "hover" | "click" | "contextmenu" | 'click' |
| placement | موضع العرض (12 موضعًا، مماثلة لـ Tooltip) | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | 'bottom-start' |
| visible | حالة الظهور (v-model:visible) | boolean | — |
| disabled | ما إذا كان المُشغِّل معطّلًا | boolean | — |
| hideOnClick | ما إذا كانت القائمة تُغلق تلقائيًا بعد النقر على عنصر | boolean | true |
| teleportTarget | وجهة Teleport (محدِّد أو عنصر) | string | HTMLElement | 'body' |
| popperClass | اسم الصنف المخصص للطبقة المنبثقة | string | — |
| maxHeight | الارتفاع الأقصى للقائمة (رقم يعني px) | string | number | — |
| themeVars | متغيرات السمة على مستوى المكوّن | { [x: string]: string; } | — |
| bgPattern | نمط الخلفية على مستوى المكوّن، يتجاوز إعدادات ConfigProvider | string | — |
| sx | خصائص التخطيط السريع sx (تُطبَّق على العنصر الجذري للمُشغِّل) | LhSxProps | — |
الأحداث
| الحدث | الوصف | المعاملات |
|---|---|---|
| command | يُطلق عند النقر على عنصر في القائمة | (command: string | number | undefined) |
| click | يُطلق عند النقر على عنصر القائمة (LhDropdownItem) | (command: string | number | undefined) |
| visible-change | تغيّر الظهور والإخفاء | (value: boolean) |
| update:visible | تغيّر الظهور والإخفاء (v-model) | (value: boolean) |
الفتحات
| الفتحة | الوصف |
|---|---|
| default | محتوى المُشغِّل |
| dropdown | محتوى القائمة (قائمة LhDropdownItem) |
التوابع
| التابع | الوصف |
|---|---|
| show | عرض برمجي |
| hide | طي برمجي |
| toggle | تبديل برمجي |
واجهة برمجة تطبيقات DropdownItem
الخصائص
| الخاصية | الوصف | النوع | الافتراضي |
|---|---|---|---|
| command | مُعرِّف الأمر، يُطرف مع حدث command عند النقر | string | number | — |
| disabled | ما إذا كان معطّلًا | boolean | false |
| divided | ما إذا كان يُعرض فاصل فوقه | boolean | false |
إمكانية الوصول
- يحمل المُشغِّل
aria-haspopup="menu"وaria-expanded؛ والقائمةrole="menu"، وعناصرهاrole="menuitem" - تتبع لوحة المفاتيح نمط APG للقوائم: ↑/↓ بالتدوير (مع تخطّي العناصر المعطّلة)، وHome/End للانتقال إلى الأول والأخير، وEnter/Space لإطلاق
commandوالإغلاق، وEsc للإغلاق وإعادة التركيز - تستخدم العناصر المعطّلة
aria-disabledبدلًا من إزالتها من شجرة التركيز، ويتخطاها كل من مفاتيح الأسهم ومفتاح الإدخال - عند تجاوز القائمة المنبثقة للمحور الرئيسي ينعكس الاتجاه تلقائيًا، وعند تجاوزها للمحور المتقاطع تُرَدّ إلى إطار العرض
تعريفات الأنواع
بنى TypeScript التي يصدّرها هذا المكوّن خارجيًا (مستخرَجة تلقائيًا من packages/components/dropdown/src/types.ts و@lh-ui/share/core/*؛ انقر كل عنصر لعرض التعريف كاملًا؛ وصف الخصائص في جدول الخصائص أعلاه):
LhDropdownProps
/**
* خاصيات مكوّن Dropdown
*/
export interface LhDropdownProps {
/** طريقة التشغيل */
trigger?: LhDropdownTrigger
/** موضع العرض */
placement?: LhDropdownPlacement
/** حالة العرض المُدارة يدويًا (v-model:visible) */
visible?: boolean
/** ما إذا كان معطّلًا */
disabled?: boolean
/** ما إذا كانت القائمة تُغلق تلقائيًا بعد النقر على عنصر */
hideOnClick?: boolean
/** وجهة Teleport (محدِّد أو عنصر، الافتراضي body) */
teleportTarget?: string | HTMLElement
/** اسم الصنف المخصص للطبقة المنبثقة */
popperClass?: string
/** الارتفاع الأقصى للقائمة */
maxHeight?: string | number
/** متغيرات السمة على مستوى المكوّن، تتجاوز إعدادات ConfigProvider */
themeVars?: Record<string, string>
/** نمط الخلفية على مستوى المكوّن، يتجاوز إعدادات ConfigProvider */
bgPattern?: string
/** خصائص التخطيط السريع sx (تُطبَّق على العنصر الجذري للمُشغِّل) */
sx?: LhSxProps
}LhDropdownItemProps
/**
* خاصيات مكوّن DropdownItem
*/
export interface LhDropdownItemProps {
/** مُعرِّف الأمر، يُطرف مع حدث command عند النقر */
command?: string | number
/** ما إذا كان معطّلًا (تبقى قابلية التركيز للدخول في شجرة إمكانية الوصول) */
disabled?: boolean
/** ما إذا كان يُعرض فاصل */
divided?: boolean
}LhDropdownTrigger (مأخوذ من @lh-ui/share/core/dropdown (logic.ts))
/** طريقة التشغيل */
export type LhDropdownTrigger = 'hover' | 'click' | 'contextmenu'