Context menu
右クリックまたは長押しで開く操作メニューです。サブメニュー、チェックボックス項目、ラジオ項目に対応し、タッチでは長押しのフィードバックを返します。
Last action: Nothing yet
pnpm dlx shadcn@latest add https://hextaui.com/r/context-menu.jsonコンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。
まだ追加していない場合は、グローバルCSSにテーマトークンを追加してください。
依存関係をインストールします。
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cn次のコードをコピーしてプロジェクトに貼り付けてください。
components/ui/context-menu.tsx インポートパスは、お使いのプロジェクト構成に合わせて更新してください。
ファイルリスト
すべての行に専用のメニューを持たせます。開いている行はハイライトされたままで、破壊的な項目は確認のため alert dialog に引き継がれます。
制御
状態を自分で管理するには、open と onOpenChange を渡します。第 2 引数は、trigger-press、outside-press、escape-key など、変更の理由を示します。closeOnClick={false} の項目は、開いたままにします。
無効
無効な <ContextMenu /> は、その領域をブラウザ自身のメニューに返します。無効な項目は表示されたままですが、キーボードではスキップされます。
長押しのフィードバック
タッチスクリーンでは、長押しの後にメニューが開きます。指を押さえている間、領域がわずかに縮み、押下が認識されたことが伝わります。指を動かすとキャンセルされます。オフにするには holdFeedback={false} を設定します。
長いコンテンツ
長いラベルは最大 20rem の幅の中で折り返され、高いメニューはビューポートに残った空間の中でスクロールします。
ネストしたサブメニュー
サブメニューは、どの階層でも、ホバーまたは矢印キーで開きます。無効なサブメニューのトリガーは開きません。
シートの中
メニューは他のオーバーレイの上に重なり、Escape は背後のシートではなく、メニューだけを閉じます。
別の要素としてレンダリング
render を使うと、トリガーを figure など任意の要素にしたり、項目をリンクにしたりできます。
右から左
メニューはトリガーの方向を読み取るため、サブメニューは左に開き、矢印キーは反転します。
| キー | アクション |
|---|---|
| ↓ | 次の項目をハイライトし、末尾では先頭に回り込みます。 |
| ↑ | 前の項目をハイライトし、先頭では末尾に回り込みます。 |
| Home | 最初の項目をハイライトします。 |
| End | 最後の項目をハイライトします。 |
| EnterSpace | ハイライト中の項目を実行します。チェックボックスとラジオの項目は切り替わり、メニューは開いたままになります。 |
| → | ハイライト中のサブメニューを開き、その中に移動します。右から左のレイアウトでは ←。 |
| ← | 現在のサブメニューを閉じ、そのトリガーに戻ります。右から左のレイアウトでは →。 |
| Esc | 現在のメニューを閉じます。サブメニューでは、そのサブメニューだけが閉じます。 |
| A–Z | その文字で始まる次の項目をハイライトします。 |
- コンテキストメニューはショートカットです。右クリックや長押しをしない人も多いため、メニュー内のすべてのアクションに、表示されたボタンやドロップダウンメニューなど、別の方法でも到達できるようにしてください。
- ブラウザは、フォーカスされた要素での Shift F10 や Menu キーでもコンテキストメニューのイベントを発火するため、トリガー内にフォーカス可能な要素があれば、キーボードユーザーも開けます。
<ContextMenuShortcut />のショートカットはラベルのみです。キーのバインドは自分で行ってください。- モーションの低減が有効な場合、長押しのフィードバックと項目の点滅はスキップされ、メニューはフェードするだけになります。
Base UI の context menu をベースにしています。各パーツは、ラップしているプリミティブの props と、パーツの state を受け取る関数形式の className を受け付けます。
| プロパティ | 型 | デフォルト |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChangedetails.reason は、変更の原因を示します。 | (open: boolean, details) => void | – |
onOpenChangeComplete開閉のアニメーションが終わった後に実行されます。 | (open: boolean) => void | – |
disabled代わりに、ブラウザのネイティブメニューを表示します。 | boolean | false |
loopFocus矢印キーによる移動を、端で反対側に回り込ませます。 | boolean | true |
highlightItemOnHover | boolean | true |
actionsRefメニューをコードから閉じます。 | RefObject<{ close, unmount }> | – |
| プロパティ | 型 | デフォルト |
|---|---|---|
holdFeedbackタッチスクリーンで長押ししている間、領域をわずかに縮めます。 | boolean | true |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-trigger" | CSSでトリガーを指定します。 |
data-popup-open | メニューが開いている間存在します。 |
data-holding | 長押しされている間、付与されます。 |
data-pressed | トリガーが押されている間存在します。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
sideポインターを基準とした、優先する側。 | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "start" |
sideOffset | number | 0 |
alignOffset | number | 0 |
collisionPaddingメニューとビューポートの端の間に確保する空間。 | number | { top, right, bottom, left } | – |
collisionAvoidanceメニューがはみ出しそうなときの、反転またはシフトの方法。 | CollisionAvoidance | – |
anchorポインター以外のものを基準に配置します。 | Element | VirtualElement | RefObject | – |
finalFocusメニューを閉じた後にフォーカスが移動する先。 | boolean | RefObject | (closeType) => HTMLElement | boolean | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-content" | メニューのポップアップ。 |
data-open | 開いている間存在します。 |
data-starting-style | メニューが表示アニメーションをしている間、付与されます。 |
data-ending-style | メニューが非表示アニメーションをしている間、付与されます。 |
data-side | 衝突の調整後に配置された側。 |
data-chosen | 項目がクリックされた後に付与されます。フェードアウトは点滅を待ちます。 |
--transform-origin | スケールアニメーションの拡大の起点となる点。 |
--available-height | ビューポートに残っている空間。メニューの高さの上限になります。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
variant | "default" | "destructive" | "default" |
insetラベルをインデントして、チェックボックス項目と揃えます。 | boolean | false |
onClickクリック、Enter、Space で実行されます。メニューは、短い点滅の後に閉じます。 | (event) => void | – |
closeOnClick | boolean | true |
disabled | boolean | false |
labelchildren がプレーンテキストでない場合に、先頭文字検索(typeahead)に使われるテキスト。 | string | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-item" | CSSで項目を指定します。 |
data-variant | 現在のバリアント。 |
data-highlighted | ポインターの下、またはキーボードフォーカスのある項目に付与されます。 |
data-disabled | 項目が無効のときに存在します。 |
data-inset | inset が設定されているときに付与されます。 |
data-chosen | 点滅している間、クリックされた項目に付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
checked | boolean | – |
defaultChecked | boolean | false |
onCheckedChange | (checked: boolean, details) => void | – |
closeOnClick | boolean | false |
inset | boolean | false |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-checkbox-item" | CSS でチェックボックス項目を指定します。 |
data-checked | チェックされているときに付与されます。 |
data-unchecked | チェックされていないときに付与されます。 |
data-highlighted | ポインターの下、またはキーボードフォーカスのある項目に付与されます。 |
data-disabled | 項目が無効のときに存在します。 |
data-inset | inset が設定されているときに付与されます。 |
data-chosen | 点滅している間、クリックされた項目に付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
value | any | – |
defaultValue | any | – |
onValueChange | (value: any, details) => void | – |
disabled | boolean | false |
| プロパティ | 型 | デフォルト |
|---|---|---|
value | any | – |
closeOnClick | boolean | false |
inset | boolean | false |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-radio-item" | CSS でラジオ項目を指定します。 |
data-checked | 選択されているときに付与されます。 |
data-highlighted | ポインターの下、またはキーボードフォーカスのある項目に付与されます。 |
data-disabled | 項目が無効のときに存在します。 |
data-inset | inset が設定されているときに付与されます。 |
data-chosen | 点滅している間、クリックされた項目に付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
inset | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
<ContextMenuGroup /> または <ContextMenuRadioGroup /> の内側では、支援技術向けにグループのラベルになります。それ以外では、通常の見出しです。
| プロパティ | 型 | デフォルト |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChange | (open: boolean, details) => void | – |
disabled | boolean | false |
closeParentOnEscEscape で、このサブメニューだけでなくメニュー全体を閉じます。 | boolean | false |
| プロパティ | 型 | デフォルト |
|---|---|---|
inset | boolean | false |
openOnHover | boolean | true |
delayサブメニューが開くまでのホバー時間(ミリ秒)。 | number | 100 |
closeDelay | number | 0 |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-sub-trigger" | CSS でサブメニューのトリガーを指定します。 |
data-popup-open | サブメニューが開いている間、付与されます。 |
data-highlighted | ハイライトされている間存在します。 |
data-disabled | 無効のときに存在します。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
sideデフォルトでは、行末側に向かって開きます。 | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | – |
align | "start" | "center" | "end" | – |
sideOffset | number | 0 |
alignOffset | number | -4 |
collisionPaddingメニューとビューポートの端の間に確保する空間。 | number | { top, right, bottom, left } | – |
collisionAvoidanceメニューがはみ出しそうなときの、反転またはシフトの方法。 | CollisionAvoidance | – |
anchorポインター以外のものを基準に配置します。 | Element | VirtualElement | RefObject | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-sub-content" | サブメニューのポップアップ。メニューと同じ state 属性を持ちます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
dir⇧⌘S のようなショートカットを、右から左のメニューでも順序どおりに保ちます。 | "ltr" | "rtl" | "ltr" |
| 属性 | 説明 |
|---|---|
data-slot="context-menu-shortcut" | ショートカットのラベル。 |
<ContextMenuGroup /> は関連する項目をラベルの下にまとめます。<ContextMenuSeparator /> は区切り線を描画します。どちらも render と className を受け付け、data-slot として context-menu-group と context-menu-separator を持ちます。
- Alert dialog破壊的または重要な操作のための確認ダイアログです。非同期処理を待ち、スマートフォンではボトムシートになります。
- Commandインラインまたは⌘Kパレットとして使える、検索可能な操作リストです。ページ、ショートカット、一致箇所のハイライトに対応します。
- Dialogフォームや集中して行う作業のための、ページ上のウィンドウです。固定のヘッダーとフッター、入れ子に対応し、スマートフォンではスワイプできるボトムシートになります。
- Drawer任意の端からスライドインし、指に追従するパネルです。スナップポイント、実際に使えるハンドル、重なっていく入れ子のドロワーに対応します。
- Dropdown menuボタンの背後に、操作とオプションをまとめたメニューです。グループ、サブメニュー、チェックボックス項目、ラジオ項目、ショートカットに対応します。
- Hover cardリンクにホバーまたはフォーカスすると開くプレビューカードです。視覚で閲覧するユーザーがさっと確認できる内容向けです。
使用しているブロック
Context menu の上に構築されるブロック。