Dropdown ドロップダウン
下方向に展開するコマンドメニューです。トリガーは任意の要素にでき、メニュー項目は command で識別し、クリック時に command イベントとして送出されます。WAI-ARIA の menu パターンに準拠して実装されています:方向キーで循環、Home/End で先頭・末尾、Enter/Space でトリガー、Esc で閉じてフォーカスを戻します。
基本的な使い方
click トリガー + command イベント
直近のコマンド:—
vue
<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>vue
<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 です。ホバーまたは右クリックでの展開にも変更できます。
hover / contextmenu
vue
<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">オプション 1</LhDropdownItem>
<LhDropdownItem command="hover-2">オプション 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>vue
<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">オプション 1</LhDropdownItem>
<LhDropdownItem command="hover-2">オプション 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トリガーはブラウザのデフォルト右クリックメニューを抑制します。
無効化と区切り線
disabled / divided
vue
<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>vue
<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をトリガーしません。
位置と最大高さ
placement と maxHeight
vue
<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>vue
<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 でマウント先を指定できます。
表示の制御
vue
<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>vue
<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>指定コンテナにマウント
Teleport コンテナ(#dropdown-zone)
vue
<script setup lang="ts">
</script>
<template>
<!-- 指定コンテナにマウント -->
<LhDropdown teleport-target="#dropdown-zone">
<LhButton>その他 ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="a">オプション</LhDropdownItem>
</template>
</LhDropdown>
</template>vue
<script>
export default {}
</script>
<template>
<!-- 指定コンテナにマウント -->
<LhDropdown teleport-target="#dropdown-zone">
<LhButton>その他 ▾</LhButton>
<template #dropdown>
<LhDropdownItem command="a">オプション</LhDropdownItem>
</template>
</LhDropdown>
</template>Dropdown API
Props
| プロパティ | 説明 | 型 | デフォルト値 |
|---|---|---|---|
| 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 | — |
Events
| イベント | 説明 | 引数 |
|---|---|---|
| command | メニュー項目をクリックしたときにトリガー | (command: string | number | undefined) |
| click | メニュー項目(LhDropdownItem)がクリックされたときにトリガー | (command: string | number | undefined) |
| visible-change | 表示・非表示の変化 | (value: boolean) |
| update:visible | 表示・非表示の変化(v-model) | (value: boolean) |
Slots
| スロット | 説明 |
|---|---|
| default | トリガーのコンテンツ |
| dropdown | メニューのコンテンツ(LhDropdownItem のリスト) |
Methods
| メソッド | 説明 |
|---|---|
| show | プログラムによる展開 |
| hide | プログラムによる折りたたみ |
| toggle | プログラムによる切り替え |
DropdownItem API
Props
| プロパティ | 説明 | 型 | デフォルト値 |
|---|---|---|---|
| command | コマンド識別子。クリック時に command イベントと共に送出されます | string | number | — |
| disabled | 無効化するかどうか | boolean | false |
| divided | 上部に区切り線を表示するかどうか | boolean | false |
アクセシビリティ
- トリガーは
aria-haspopup="menu"とaria-expandedを持ちます。メニューはrole="menu"、項目はrole="menuitem"です - キーボード操作は APG の menu パターンに準拠します:↑/↓ で循環(無効項目はスキップ)、Home/End で先頭・末尾へ、Enter/Space で
commandをトリガーして閉じる、Esc で閉じてフォーカスを戻す - 無効項目はフォーカスツリーから除外するのではなく
aria-disabledを使用します。方向キーと Enter はいずれもそれをスキップします - メニュー展開時に主軸方向へはみ出すと自動的に方向が反転し、交差軸方向のはみ出しはビューポート内にクランプされます
型定義
このコンポーネントが外部にエクスポートする TypeScript の型構造を以下に示します(packages/components/dropdown/src/types.ts および @lh-ui/share/core/* のソースから自動抽出。各エントリーをクリックすると完全な定義が表示されます。プロパティの説明は上の Props 表を参照してください):
LhDropdownProps
ts
/**
* Dropdown コンポーネントの Props
*/
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
ts
/**
* DropdownItem コンポーネントの Props
*/
export interface LhDropdownItemProps {
/** コマンド識別子。クリック時に command イベントと共に送出 */
command?: string | number
/** 無効化するかどうか(アクセシビリティツリーに含めるためフォーカス可能性は保持) */
disabled?: boolean
/** 区切り線を表示するかどうか */
divided?: boolean
}LhDropdownTrigger (@lh-ui/share/core/dropdown (logic.ts) より)
ts
/** トリガー方式 */
export type LhDropdownTrigger = 'hover' | 'click' | 'contextmenu'