Skip to content
API 风格:

Popover (مربع منبثق) ​

طبقة منبثقة غنية بالمحتوى: تحمل معلومات أكثر من Tooltip (عنوان، متن، أزرار عمليات). يُغلَّف المُطلِّق في الفتحة الافتراضية، ويُمرَّر المحتوى المنبثق عبر فتحة content. عند الإطلاق بالنقر تكون الطبقة المنبثقة role="dialog" وتحبس التركيز، أما عند الإطلاق بالمرور فتعرض المحتوى فقط دون قطع تدفّق لوحة المفاتيح.

الاستخدام الأساسي ​

إطلاق بالنقر
vue
<script setup lang="ts">
</script>

<template>
  <LhPopover title="تلميح رسم الحبر" placement="top">
    <LhButton>انقر للإظهار</LhButton>
    <template #content>
      <p>هذا محتوى غني، يمكن وضع أي ترميز فيه.</p>
    </template>
  </LhPopover>
</template>
vue
<script>
export default {}
</script>

<template>
  <LhPopover title="تلميح رسم الحبر" placement="top">
    <LhButton>انقر للإظهار</LhButton>
    <template #content>
      <p>هذا محتوى غني، يمكن وضع أي ترميز فيه.</p>
    </template>
  </LhPopover>
</template>

طرق الإطلاق ​

hover / focus / manual
vue
<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>
vue
<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>

اثنا عشر موضعًا ​

placement
vue
<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>
vue
<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)، ويتحوّل موضع السهم تبعًا لذلك.

محتوى تفاعلي متداخل ​

مثال مع نموذج
vue
<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>
vue
<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>

خصائص إضافية ​

disabled / arrow / width / hideAfter / popperClass / teleportTarget
حاوية Teleport (#demo-popover-host)
vue
<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>
vue
<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 يدويًا في إظهار الطبقة المنبثقة وإخفائها.

إظهار وإخفاء موجَّهان
vue
<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>
vue
<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هل يُعرَض السهمbooleantrue
widthعرض الطبقة المنبثقةstring | number—
popperClassصنف CSS مخصَّص للطبقة المنبثقةstring—
teleportTargetهدف Teleport (مُحدِّد أو عنصر)string | HTMLElement'body'
showAfterزمن تأخير الإظهار (ms)number—
hideAfterزمن تأخير الإخفاء (ms)number—
themeVarsمتغيّرات السمة على مستوى المكوّن، تتجاوز إعداد ConfigProvider{ [x: string]: string; }—
bgPatternنمط الخلفية على مستوى المكوّن، يتجاوز إعداد ConfigProviderstring—
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
ts
/**
 * 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)
ts
/**
 * محرّك تحديد موضع 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))
ts
/** طريقة الإطلاق */
export type LhPopoverTrigger = 'click' | 'hover' | 'focus' | 'manual'

جمال التقاليد، أساس الكود