import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks'
import * as MenuStories from './Menu.stories'

<Meta of={MenuStories} />

# Menu

A list of actions from a trigger. **The menu owns the trigger**, so a caller
writes no open state, no placement and no dismiss — it hands over the control
and the rows, and everything else is Base UI's.

Rows are **MenuItem**, headings are **MenuSection**, a non-interactive line at
row geometry is **MenuRow**, a row that opens another menu is **MenuSub**, and
**EnterHint** is the `↩ Enter` chip a picker row shows while highlighted.

**MenuPanel** is the container on its own, without the behaviour — for the one
case that cannot have it: a type-ahead popup inside a text editor, where the
editor's own plugin already owns the keyboard and a menu's roving focus would
take focus out of the text. `Menu` renders `MenuPanel`, so the surface has one
definition either way.

<Canvas of={MenuStories.Items} />

## When

- A click-opened list of actions: a `⋮` more-menu, an account menu, a
  right-click's worth of verbs.
- A picker fed by typing: rows are `MenuItem size="tall"` — content height
  with a 40px floor, room for a 32px face via `leading` and a `description`
  line — with an `EnterHint` on the highlighted row.
- Grouped choices where one is current: `MenuSection` over rows, `selected`
  on the chosen one.

<Canvas of={MenuStories.Sections} />

## When not

- **A panel of content — a field, a form, a row of controls** → **Popover**.
  It is this surface with none of a menu's keyboard, which is what content
  needs: a menu's typeahead would fight a text field.
- Picking one value for a form field → **Select**.
- Rows that toggle and stay open → that is a listbox, not a menu; a Checkbox
  inside a `menuitem` is invalid. Build it as a list in a **DialogShell**.
- A strip of icon actions → **Toolbar**.
- Who-you-are and where-you-are → **IdentityMenu**, already built on this.

## How

```tsx
import { Menu, MenuItem } from '@estiva-app/ui'

<Menu trigger={<IconButton aria-label="More" tooltip="More"><IconDots /></IconButton>}>
  <MenuItem label="Rename" onClick={rename} />
  <MenuItem label="Delete" destructive onClick={confirmDelete} />
</Menu>
```

That is the whole of it. **No `open` state, no anchor, no `onClose`** — the
menu is always mounted and Base UI decides when it is shown, which is what
lets it place itself against the trigger, return focus to it, highlight the
first row when it was opened from the keyboard, and treat a second press of
the trigger as "close" rather than as a press outside.

- **`trigger`** is any element that forwards its ref and spreads its props —
  this package's `Button`, `IconButton` and `PersonTrigger` all do. An
  icon-only trigger owes a `tooltip` and an `aria-label`.
- **`align="right"`** hangs the menu's right edge from the trigger's, for a
  control at the right of a row or a bar.
- **`open` / `onOpenChange`** are there for a caller that must know or must
  force it. Leave them off and the menu keeps its own state.
- **`actionsRef`** gives `close()`, for content that is not a `MenuItem` and
  so does not close the menu by being chosen.
- A menu stands as tall as the room it opens into and scrolls only when the
  screen has none; it flips above the trigger when the room below is worse,
  and never leaves the screen.

<Canvas of={MenuStories.OnACard} />

- **Planned:** one part for the pop-up lists while typing in an editor (UIG-31).

## What it owns

It does these by itself. Write none of them around it.

| It does | So you never write | Refused when written by hand |
|---|---|---|
| Floats on top of the page | a portal, or a `z-index` to win | ✓ |
| Stays attached to its anchor on scroll and resize | a scroll or resize listener, or placement arithmetic | ✓ |
| Closes on a press outside | a click listener on the page | ✓ |
| Takes its keys by itself: Enter and Space choose a row, Escape closes it | a key listener on the page | ✓ |
| Moves through its items with the arrow keys, and jumps to a row by its first letters | an arrow-key handler | ✓ |
| Says what it is to assistive technology: a menu, and each row a menu item | `role="menu"` or `role="menuitem"` | ✓ |
| Keeps its own open state, and closes when a row is chosen | `useState` for open |  |
| Gives focus back to the trigger when it closes | focus code |  |

## Keys

| Input | What happens |
|---|---|
| click / Enter / Space on the trigger | opens; from the keyboard, the first row is highlighted |
| ↑ / ↓ | move between rows, and wrap at the ends |
| Home / End | first row / last row |
| typing a row's first letters | jumps to that row |
| Enter / Space on the highlighted row | activates it, and closes the menu |
| → on a submenu row | opens the submenu **and highlights its first row**; ← closes it and comes back to the row |
| Escape | closes, and gives focus back to the trigger |
| a press outside, or a second press of the trigger | closes |

Focus moves into the menu when it opens and returns to the trigger when it
closes. None of that existed before stage 4: a menu was a portalled box of
buttons you tabbed through, and this page said the arrow keys did nothing.

### Hover-opened menus

`openOnHover` opens the menu on hover and closes it shortly after the pointer
leaves — for a control that only appears while the pointer is on a card.

**Never use it on a menu that has a `MenuSub`** — the two together strand the
menu open, and the component says so in the console if you try.

Measured: enter the submenu's panel, then leave in any direction that does not
cross back over the parent, and neither the submenu nor the menu ever closes
again — at 200ms, 500ms, 1s and 2s. Leaving by a path that crosses the parent
closes correctly, which is why it is easy to miss. Both triggers hard-code
Floating UI's `safePolygon({ blockPointerEvents: true })`, which blocks pointer
events while the path from a row to its panel is live; leaving that way never
resolves the polygon, and Base UI exposes no prop for it.

Without a submenu it is exact: opens on hover, closes ~200ms after the pointer
leaves. `PLAN.md` §6.2 carries the measurements.

### Rows without a menu

`MenuItem` and `MenuSection` work on a bare `MenuPanel`, and there they are
plain buttons: no `role="menuitem"`, because ARIA requires a menu item to sit
inside a `menu` or a `menubar` and a `MenuPanel` is a `<div>` with no role.
Four surfaces used to claim menu items of nothing.

### Alignment

A menu whose rows are `px-3` (tall picker rows) aligns its headings by passing
`className="px-3"` to `MenuSection` — the heading row is `px-2` by default,
matching the default rows.

**Between groups, a `MenuSeparator`**: the line, the width of the rows it
separates, with its own 4px above and below. Not a `Divider`, whose room is
its caller's.

## Props

<Controls of={MenuStories.FromATrigger} />
