Popover (مربع منبثق)
طبقة منبثقة غنية بالمحتوى: تحمل معلومات أكثر من Tooltip (عنوان، متن، أزرار عمليات). يُغلَّف المُطلِّق في الفتحة الافتراضية، ويُمرَّر المحتوى المنبثق عبر فتحة content. عند الإطلاق بالنقر تكون الطبقة المنبثقة role="dialog" وتحبس التركيز، أما عند الإطلاق بالمرور فتعرض المحتوى فقط دون قطع تدفّق لوحة المفاتيح.
الاستخدام الأساسي
<script setup lang="ts">
</script>
<template>
<LhPopover title="تلميح رسم الحبر" placement="top">
<LhButton>انقر للإظهار</LhButton>
<template #content>
<p>هذا محتوى غني، يمكن وضع أي ترميز فيه.</p>
</template>
</LhPopover>
</template><script>
export default {}
</script>
<template>
<LhPopover title="تلميح رسم الحبر" placement="top">
<LhButton>انقر للإظهار</LhButton>
<template #content>
<p>هذا محتوى غني، يمكن وضع أي ترميز فيه.</p>
</template>
</LhPopover>
</template>طرق الإطلاق
<template>
<!-- إطلاق بالمرور -->
<LhPopover trigger="hover" title="إطلاق بالمرور" :show-after="0">
<LhButton>مرّر فوقّي</LhButton>
<template #content><p>تحريك المؤشر داخل الطبقة المنبثقة لا يُغلقها.</p></template>
</LhPopover>
<!-- إطلاق بالتركيز -->
<LhPopover trigger="focus" title="إطلاق بالتركيز" :show-after="0">
<LhInput placeholder="ركّز على حقل الإدخال" />
<template #content><p>يُعرض عندما يحصل حقل الإدخال على التركيز.</p></template>
</LhPopover>
<!-- تحكّم يدوي -->
<LhPopover v-model:visible="manualVisible" trigger="manual" title="تحكّم يدوي">
<LhButton @click="manualVisible = !manualVisible">مفتاح خارجي</LhButton>
<template #content><p>الإظهار والإخفاء موجَّهان بالكامل عبر v-model خارجي.</p></template>
</LhPopover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const manualVisible = ref(false)
</script><template>
<!-- إطلاق بالمرور -->
<LhPopover trigger="hover" title="إطلاق بالمرور" :show-after="0">
<LhButton>مرّر فوقّي</LhButton>
<template #content><p>تحريك المؤشر داخل الطبقة المنبثقة لا يُغلقها.</p></template>
</LhPopover>
<!-- إطلاق بالتركيز -->
<LhPopover trigger="focus" title="إطلاق بالتركيز" :show-after="0">
<LhInput placeholder="ركّز على حقل الإدخال" />
<template #content><p>يُعرض عندما يحصل حقل الإدخال على التركيز.</p></template>
</LhPopover>
<!-- تحكّم يدوي -->
<LhPopover v-model:visible="manualVisible" trigger="manual" title="تحكّم يدوي">
<LhButton @click="manualVisible = !manualVisible">مفتاح خارجي</LhButton>
<template #content><p>الإظهار والإخفاء موجَّهان بالكامل عبر v-model خارجي.</p></template>
</LhPopover>
</template>
<script>
export default {
data() {
return { manualVisible: false }
},
}
</script>اثنا عشر موضعًا
<template>
<LhPopover
v-for="p in placements"
:key="p"
:placement="p"
:title="p"
trigger="hover"
:show-after="0"
>
<LhButton size="small">{{ p }}</LhButton>
<template #content><p>الموضع: {{ p }}</p></template>
</LhPopover>
</template>
<script setup lang="ts">
const placements = [
'top', 'top-start', 'top-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end',
'right', 'right-start', 'right-end',
] as const
</script><template>
<LhPopover
v-for="p in placements"
:key="p"
:placement="p"
:title="p"
trigger="hover"
:show-after="0"
>
<LhButton size="small">{{ p }}</LhButton>
<template #content><p>الموضع: {{ p }}</p></template>
</LhPopover>
</template>
<script>
export default {
data() {
return {
placements: [
'top', 'top-start', 'top-end',
'bottom', 'bottom-start', 'bottom-end',
'left', 'left-start', 'left-end',
'right', 'right-start', 'right-end',
],
}
},
}
</script>عندما لا تكفي مساحة إطار العرض، تنقلب الطبقة المنبثقة تلقائيًا (flip) وتُحبَس على المحور المتقاطع (shift)، ويتحوّل موضع السهم تبعًا لذلك.
محتوى تفاعلي متداخل
<template>
<LhPopover placement="right" title="ملاحظة سريعة">
<LhButton>إضافة ملاحظة</LhButton>
<template #content>
<div class="demo-popover-form">
<LhInput v-model="note" placeholder="اكتب محتوى الملاحظة" />
<LhButton type="primary" @click="saveNote">حفظ</LhButton>
</div>
</template>
</LhPopover>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const note = ref('')
const saved = ref('')
function saveNote() {
saved.value = note.value
note.value = ''
}
</script><template>
<LhPopover placement="right" title="ملاحظة سريعة">
<LhButton>إضافة ملاحظة</LhButton>
<template #content>
<div class="demo-popover-form">
<LhInput v-model="note" placeholder="اكتب محتوى الملاحظة" />
<LhButton type="primary" @click="saveNote">حفظ</LhButton>
</div>
</template>
</LhPopover>
</template>
<script>
export default {
data() {
return { note: '', saved: '' }
},
methods: {
saveNote() {
this.saved = this.note
this.note = ''
},
},
}
</script>خصائص إضافية
<script setup lang="ts">
</script>
<template>
<!-- معطَّل -->
<LhPopover disabled title="معطَّل">
<LhButton disabled>مُطلِّق معطَّل</LhButton>
<template #content><p>عندما يكون disabled، لا يستجيب المُطلِّق ولا الطبقة المنبثقة.</p></template>
</LhPopover>
<!-- بلا سهم -->
<LhPopover title="بلا سهم" :arrow="false" :show-after="0">
<LhButton>بلا سهم</LhButton>
<template #content><p>عندما يكون arrow يساوي false لا يُعرَض سهم التوجيه.</p></template>
</LhPopover>
<!-- عرض ثابت -->
<LhPopover title="عرض ثابت" width="240" :show-after="0">
<LhButton>عرض ثابت</LhButton>
<template #content><p>يُحدَّد عرض الطبقة المنبثقة عبر width، وهنا 240px.</p></template>
</LhPopover>
<!-- تأخير الإخفاء -->
<LhPopover title="تأخير الإخفاء" :hide-after="800" :show-after="0">
<LhButton>تأخير الإخفاء</LhButton>
<template #content><p>يُطوى بعد 800ms من مغادرة المؤشر للمُطلِّق.</p></template>
</LhPopover>
<!-- صنف مخصَّص -->
<LhPopover title="صنف مخصَّص" popper-class="demo-popper-accent" :show-after="0">
<LhButton>صنف مخصَّص</LhButton>
<template #content><p>يُضاف popperClass إلى العنصر الجذري للطبقة المنبثقة، ويمكن استخدامه لتخصيص الأنماط.</p></template>
</LhPopover>
<!-- تحديد هدف النقل -->
<LhPopover title="تحديد هدف النقل" teleport-target="#demo-popover-host" :show-after="0">
<LhButton>حاوية Teleport</LhButton>
<template #content><p>تُنقَل الطبقة المنبثقة إلى الحاوية أدناه بدلاً من body.</p></template>
</LhPopover>
</template><script>
export default {}
</script>
<template>
<!-- معطَّل -->
<LhPopover disabled title="معطَّل">
<LhButton disabled>مُطلِّق معطَّل</LhButton>
<template #content><p>عندما يكون disabled، لا يستجيب المُطلِّق ولا الطبقة المنبثقة.</p></template>
</LhPopover>
<!-- بلا سهم -->
<LhPopover title="بلا سهم" :arrow="false" :show-after="0">
<LhButton>بلا سهم</LhButton>
<template #content><p>عندما يكون arrow يساوي false لا يُعرَض سهم التوجيه.</p></template>
</LhPopover>
<!-- عرض ثابت -->
<LhPopover title="عرض ثابت" width="240" :show-after="0">
<LhButton>عرض ثابت</LhButton>
<template #content><p>يُحدَّد عرض الطبقة المنبثقة عبر width، وهنا 240px.</p></template>
</LhPopover>
<!-- تأخير الإخفاء -->
<LhPopover title="تأخير الإخفاء" :hide-after="800" :show-after="0">
<LhButton>تأخير الإخفاء</LhButton>
<template #content><p>يُطوى بعد 800ms من مغادرة المؤشر للمُطلِّق.</p></template>
</LhPopover>
<!-- صنف مخصَّص -->
<LhPopover title="صنف مخصَّص" popper-class="demo-popper-accent" :show-after="0">
<LhButton>صنف مخصَّص</LhButton>
<template #content><p>يُضاف popperClass إلى العنصر الجذري للطبقة المنبثقة، ويمكن استخدامه لتخصيص الأنماط.</p></template>
</LhPopover>
<!-- تحديد هدف النقل -->
<LhPopover title="تحديد هدف النقل" teleport-target="#demo-popover-host" :show-after="0">
<LhButton>حاوية Teleport</LhButton>
<template #content><p>تُنقَل الطبقة المنبثقة إلى الحاوية أدناه بدلاً من body.</p></template>
</LhPopover>
</template>إظهار وإخفاء موجَّهان
يتحكّم v-model:visible يدويًا في إظهار الطبقة المنبثقة وإخفائها.
<script setup lang="ts">
import { ref } from 'vue'
const popVisible = ref(false)
</script>
<template>
<LhPopover v-model:visible="popVisible" title="طبقة منبثقة موجَّهة" trigger="manual">
<LhButton>{{ popVisible ? 'طيّ' : 'عرض' }}</LhButton>
</LhPopover>
</template><script>
export default {
data() {
return { popVisible: false }
},
}
</script>
<template>
<LhPopover v-model:visible="popVisible" title="طبقة منبثقة موجَّهة" trigger="manual">
<LhButton>{{ popVisible ? 'طيّ' : 'عرض' }}</LhButton>
</LhPopover>
</template>Popover API
Props
| اسم الخاصية | الوصف | النوع | الافتراضي |
|---|---|---|---|
| placement | موضع العرض | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | 'top' |
| trigger | طريقة الإطلاق | "click" | "hover" | "focus" | "manual" | 'click' |
| title | العنوان (يمكن أيضًا استخدام فتحة title) | string | — |
| visible | حالة العرض الموجَّهة يدويًا (v-model:visible) | boolean | — |
| disabled | هل يعطَّل | boolean | — |
| arrow | هل يُعرَض السهم | boolean | true |
| width | عرض الطبقة المنبثقة | string | number | — |
| popperClass | صنف CSS مخصَّص للطبقة المنبثقة | string | — |
| teleportTarget | هدف Teleport (مُحدِّد أو عنصر) | string | HTMLElement | 'body' |
| showAfter | زمن تأخير الإظهار (ms) | number | — |
| hideAfter | زمن تأخير الإخفاء (ms) | number | — |
| themeVars | متغيّرات السمة على مستوى المكوّن، تتجاوز إعداد ConfigProvider | { [x: string]: string; } | — |
| bgPattern | نمط الخلفية على مستوى المكوّن، يتجاوز إعداد ConfigProvider | string | — |
| sx | خصائص التخطيط السريع sx (تُطبَّق على العنصر الجذري للمُطلِّق) | LhSxProps | — |
Events
| اسم الحدث | الوصف | وسائط الاستدعاء |
|---|---|---|
| update:visible | تغيّر الإظهار والإخفاء | (value: boolean) |
| show | يُطلق بعد الظهور | — |
| hide | يُطلق بعد الاختفاء | — |
Slots
| اسم الفتحة | الوصف |
|---|---|
| default | محتوى المُطلِّق |
| title | عنوان مخصَّص |
| content | المحتوى المنبثق |
Methods
| اسم الطريقة | الوصف |
|---|---|
| show | إظهار برمجي |
| hide | إخفاء برمجي |
| toggle | تبديل برمجي |
إمكانية الوصول
- عند الإطلاق بالنقر تكون الطبقة المنبثقة
role="dialog"+aria-modal="true"، ويحمل المُطلِّقaria-haspopup/aria-expanded - الإطلاق بالنقر ينقل التركيز إلى أوّل عنصر قابل للتركيز داخل الطبقة المنبثقة، ويدور Tab داخلها، ويُعاد التركيز إلى المُطلِّق بعد الإغلاق
- عند الإطلاق بالمرور أو التركيز لا يُنقَل التركيز، تفاديًا لقطع عملية لوحة المفاتيح
- Escape يُغلق، والنقر خارج الطبقة المنبثقة يُغلقها؛ وعند وجود عنوان يُربَط عبر
aria-labelledby - محرّك تحديد المواقع دوال نقية مطوَّرة داخليًا (flip/shift لمنع التجاوز)، بلا أي اعتماد تشغيلي على طرف ثالث
يُطوى المُطلِّق عند خروجه من إطار العرض: الطبقة المنبثقة ذات موضع
fixed، وبمجرد خروج المُطلِّق من إطار العرض لم تعد تتبعه، بل تبقى عائمة فوق محتوى الصفحة بـ z-index عالٍ. لذلك مهما كانت طريقة الإطلاق (بما فيها الوضع الموجَّهmanual)، عندما يخرج المُطلِّق بالكامل من إطار العرض تُطوى اللوحة، ويُطلَقupdate:visibleلمزامنة الحالة الخارجية.
تعريفات الأنواع
بنى TypeScript التي يصدّرها هذا المكوّن للخارج (مستخرجة تلقائيًا من packages/components/popover/src/types.ts و@lh-ui/share/core/*؛ انقر كل عنصر لتوسيع تعريفه الكامل؛ لأوصاف الخصائص انظر جدول Props أعلاه):
LhPopoverProps
/**
* Props مكوّن Popover
*/
export interface LhPopoverProps {
/** موضع العرض */
placement?: LhPlacement
/** طريقة الإطلاق */
trigger?: LhPopoverTrigger
/** العنوان */
title?: string
/** حالة العرض الموجَّهة يدويًا (v-model:visible) */
visible?: boolean
/** هل يعطَّل */
disabled?: boolean
/** هل يُعرَض السهم */
arrow?: boolean
/** عرض الطبقة المنبثقة */
width?: string | number
/** صنف CSS مخصَّص للطبقة المنبثقة */
popperClass?: string
/** هدف Teleport (مُحدِّد أو عنصر، الافتراضي body) */
teleportTarget?: string | HTMLElement
/** زمن تأخير الإظهار (ms) */
showAfter?: number
/** زمن تأخير الإخفاء (ms) */
hideAfter?: number
/** متغيّرات السمة على مستوى المكوّن، تتجاوز إعداد ConfigProvider */
themeVars?: Record<string, string>
/** نمط الخلفية على مستوى المكوّن، يتجاوز إعداد ConfigProvider */
bgPattern?: string
/** خصائص التخطيط السريع sx (تُطبَّق على العنصر الجذري للمُطلِّق) */
sx?: LhSxProps
}LhPopoverPlacement (مأخوذ من @lh-ui/share/core/shared/popper)
/**
* محرّك تحديد موضع Popper (دوال نقية، بلا اعتماد على إطار عمل)
*
* حسابات هندسية مشتركة بين الطبقات المنبثقة
* (Tooltip / Popover / Popconfirm / Dropdown وغيرها):
* وضع 12 موضعًا، الانقلاب عند تجاوز المحور الرئيسي (flip)،
* والحبس على المحور المتقاطع (shift).
* جميع المُدخلات قيم rect حرفية، لا تُلامس DOM، ويمكن اختبارها مباشرة.
*/
/** جهة الطبقة المنبثقة (اتجاه + محاذاة) */
export type LhPlacement =
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'
| 'right'
| 'right-start'
| 'right-end'LhPopoverTrigger (مأخوذ من @lh-ui/share/core/popover (logic.ts))
/** طريقة الإطلاق */
export type LhPopoverTrigger = 'click' | 'hover' | 'focus' | 'manual'