Files
headlessui/playgrounds/react/pages/menu/menu.tsx
T
Robin Malfait 1461b65810 Add a quick trigger action to the Menu, Listbox and Combobox components (#3700)
This PR adds a new quick trigger feature to the `Menu`. Not sure what
the best
name for this is, but essentially this is the behavior:

Recently we made sure that the `Menu` opens on `mousedown` (not just
`click`).

This means that we can perform the following quick action:
1. `mousedown` on the `MenuButton` — this will open the `Menu`
2. Without releasing the mouse button yet, move your mouse over one of
the `MenuItem`s — this will highlight the currently active `MenuItem`.
3. Release the mouse button — this will invoke the currently active
`MenuItem` and close the `Menu`.

This now means that you can perform actions very quickly.

What this PR doesn't do yet is if you have a scrollable list, then it
won't scroll up or down when you reach the ends of the list. For this we
would need to introduce some new elements. The native Menu items on
macOS show a little placeholder arrow. If you put your cursor in that
area, it starts scrolling:

<img width="489" alt="image"
src="https://github.com/user-attachments/assets/e3a90d5a-daa7-4711-9e19-050578be3e02"
/>


## Test plan

1. Everything still works as expected
2. Quick release has been added:

- Listbox:
https://headlessui-react-git-feat-quick-trigger-tailwindlabs.vercel.app/listbox/listbox-with-pure-tailwind
- Menu:
https://headlessui-react-git-feat-quick-trigger-tailwindlabs.vercel.app/menu/menu
- Combobox:
https://headlessui-react-git-feat-quick-trigger-tailwindlabs.vercel.app/combobox/combobox-countries
2025-04-24 16:01:38 +02:00

75 lines
2.6 KiB
TypeScript

import { Menu } from '@headlessui/react'
import { Button } from '../../components/button'
import { classNames } from '../../utils/class-names'
export default function Home() {
return (
<div className="flex h-full w-screen justify-center bg-gray-50 p-12">
<div className="relative inline-block text-left">
<Menu>
<span className="shadow-xs rounded-md">
<Menu.Button as={Button}>
<span>Options</span>
<svg
className="-mr-1 ml-2 h-5 w-5 transition-transform duration-150"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</Menu.Button>
</span>
<Menu.Items className="outline-hidden absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg">
<div className="px-4 py-3">
<p className="text-sm leading-5">Signed in as</p>
<p className="truncate text-sm font-medium leading-5 text-gray-900">
tom@example.com
</p>
</div>
<div className="py-1">
<CustomMenuItem href="#account-settings">Account settings</CustomMenuItem>
<CustomMenuItem href="#support">Support</CustomMenuItem>
<CustomMenuItem disabled href="#new-feature">
New feature (soon)
</CustomMenuItem>
<CustomMenuItem href="#license">License</CustomMenuItem>
</div>
<div className="py-1">
<CustomMenuItem href="#sign-out">Sign out</CustomMenuItem>
</div>
</Menu.Items>
</Menu>
</div>
</div>
)
}
function CustomMenuItem(props) {
return (
<Menu.Item {...props}>
{({ active, disabled }) => (
<button
onClick={() => {
alert(`You clicked on ${props.href}`)
}}
className={classNames(
'flex w-full justify-between px-4 py-2 text-left text-sm leading-5',
active ? 'bg-indigo-500 text-white' : 'text-gray-700',
disabled && 'cursor-not-allowed opacity-50'
)}
>
<span className={classNames(active && 'font-bold')}>{props.children}</span>
<kbd className={classNames('font-sans', active && 'text-indigo-50')}>K</kbd>
</button>
)}
</Menu.Item>
)
}