useMenuPress@astryxdesign/core v0.6.6 · useMenuPress

Usage

The press model of macOS and iOS menus, for any pointer: the row under the pointer when it is RELEASED is the row that acts, the highlight follows the pointer while it is held, a mouse opens the menu on press and can drag straight into it, and a finger held on the trigger opens it with the finger still down. The pointer is tracked at document level by pointerId, so a finger that slid off the row it landed on is still followed; the click the browser reports at the end of a touch, aimed at the row where the touch began, is swallowed so nothing acts twice. DropdownMenu, ContextMenu, DropdownMenuSubMenu, Selector and the menu bottom sheet already mount it; reach for it directly only when building a menu-like surface of your own.

ts
import {useMenuPress} from '@astryxdesign/core/hooks'

Best practices

GuidancePractices
Do

Pass a selector for ENABLED rows only, so a disabled row or a divider under the pointer clears the highlight instead of lighting up.

Do

Let the default highlight move focus in a menu; supply onHighlight only for a listbox that must keep focus on its combobox and highlight through aria-activedescendant.

Do

Declare touch-action on the menu root: none when its rows fit, pan-y when it scrolls, so the browser — not the hook — decides when a finger is scrolling.

Don't

Act on a row from its own pointerdown or pointerup handler as well; the hook already activates the row under the release, and a second path acts twice.

Don't

Read a row click with detail 0 as a keyboard activation; the hook dispatches its pointer activation with detail 0 too. Use isMenuPressActivation() to tell them apart.

Parameters

ParamTypeDescription
optionsrequired

Configuration object.

options.menuRefrequired
RefObject<HTMLElement | null>

The menu or listbox root — the surface whose rows a press picks from.

options.itemSelectorrequired
string

Selector matching the ENABLED rows. A pointer over anything else inside the menu (a divider, a heading, a disabled row) highlights nothing.

options.triggerRef
RefObject<HTMLElement | null>

The control that opens the menu, when a press may start there.

options.onTriggerPress
(pointerType:
) => boolean

A mouse pressed the trigger, or a finger rested on it for the long-press delay: open the menu under the held pointer and return whether it opened. Return false when the press closed an open menu instead.

options.onHighlight
(row: HTMLElement | null) => void

Move the highlight; null clears it. Defaults to moving DOM focus with preventScroll onto the row, and onto the menu root when there is no row. A picker that highlights through aria-activedescendant supplies its own.

options.onActivate
(row: HTMLElement, release: PointerEvent) => void

Act on the row under the release. Defaults to dispatching a click on the row that carries the release button and modifier keys.

options.onDismiss
() => void

A MOUSE was released outside the menu with nothing acting: close it. A finger released outside leaves the menu open, so this is never called then.

options.getScroller
() => HTMLElement | null

The element to scroll while a tracked pointer rests near its top or bottom edge. Defaults to the menu root when it overflows.

options.longPressDelayMs
number (default: 500)

How long a finger must rest on the trigger before the menu opens under it.

options.isEnabled
boolean (default: true)

Whether the model is live.

Returns

FieldTypeDescription
menuProps{onPointerDown; "data-astryx-menu-press": ""}

Spread onto the menu root. Claims presses that begin inside it and marks the root as carrying the press model.

triggerProps{onPointerDown; onContextMenu}

Spread onto the trigger: a mouse press opens the menu at once; a finger held for the delay opens it with the finger still down.

isTriggerClickFromPress() => boolean

Whether the click reaching the trigger belongs to the gesture that just pressed it. That press already opened or closed the menu, so the click must neither toggle nor reopen.

cancel() => void

End the gesture in flight with nothing acting, for when the menu closes under it.