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>これはリッチコンテンツの例で、任意の markup を配置できます。</p>
    </template>
  </LhPopover>
</template>
vue
<script>
export default {}
</script>

<template>
  <LhPopover title="水墨ヒント" placement="top">
    <LhButton>クリックでポップアップ</LhButton>
    <template #content>
      <p>これはリッチコンテンツの例で、任意の markup を配置できます。</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>

12 種類の方位 ​

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ポップアップ層のカスタムクラス名string—
teleportTargetTeleport 先(セレクタまたは要素)string | HTMLElement'body'
showAfter遅延表示時間(ms)number—
hideAfter遅延非表示時間(ms)number—
themeVarsコンポーネントレベルのテーマ変数。ConfigProvider の設定を上書き{ [x: string]: string; }—
bgPatternコンポーネントレベルの背景紋様。ConfigProvider の設定を上書きstring—
sxsx クイックレイアウトプロパティ(トリガーのルート要素に作用)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
/**
 * Popover コンポーネント Props
 */
export interface LhPopoverProps {
  /** 表示位置 */
  placement?: LhPlacement
  /** トリガー方式 */
  trigger?: LhPopoverTrigger
  /** タイトル */
  title?: string
  /** 手動で制御する表示状態(v-model:visible) */
  visible?: boolean
  /** 無効にするか */
  disabled?: boolean
  /** 矢印を表示するか */
  arrow?: boolean
  /** ポップアップ層の幅 */
  width?: string | number
  /** ポップアップ層のカスタムクラス名 */
  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 種類の placement 配置、主軸溢れ反転(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'

国風の美で、コードの基を築く