Skip to content
API 风格:

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>
指定コンテナにマウント
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>

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メニュー項目クリック後に自動的に閉じるかどうかbooleantrue
teleportTargetTeleport の転送先(セレクターまたは要素)string | HTMLElement'body'
popperClassポップアップレイヤーのカスタムクラス名string—
maxHeightメニューの最大高さ(数字は px)string | number—
themeVarsコンポーネントレベルのテーマ変数{ [x: string]: string; }—
bgPatternコンポーネントレベルの背景紋様。ConfigProvider の設定を上書きしますstring—
sxsx クイックレイアウトプロパティ(トリガーのルート要素に作用)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プログラムによる切り替え

Props ​

プロパティ説明型デフォルト値
commandコマンド識別子。クリック時に command イベントと共に送出されますstring | number—
disabled無効化するかどうかbooleanfalse
divided上部に区切り線を表示するかどうかbooleanfalse

アクセシビリティ ​

  • トリガーは 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'

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