{
  "name": "Menu",
  "source": "src/components/Menu.tsx",
  "propsType": "MenuProps",
  "description": "Menu — Figma `Menu` (82:306), `Menu Item` and `Menu Section Title`.\n\nA real ARIA menu: `role=\"menu\"`, one tab stop, arrow keys, Home and End,\ntypeahead, and disabled rows listed but skipped. Figma's `Type` is\n`selectionMode` — Single draws one check, Multi draws several — and with\na selection mode the rows become `menuitemradio` / `menuitemcheckbox` and\nannounce their checked state.\n\nOn its own it is the surface only, rendered in flow. Inside a MenuTrigger it\nfloats, opens and closes, is named by its trigger, and can open submenus.",
  "import": "import { Menu } from 'ionbase-ui';",
  "status": "stable",
  "summary": "An ARIA menu: one tab stop, arrow keys, typeahead, and optional single or multiple selection. Children are MenuItem and MenuSection. Inside a MenuTrigger it floats, opens and closes, and can open submenus; on its own it renders in flow.",
  "useWhen": [
    "a list of actions or choices for the thing the user is on — row actions, a workspace switcher, a sort order",
    "the rows need icons, a selected check, or grouping under headings — more than a native <select> can hold"
  ],
  "useInstead": [
    {
      "when": "a button opens it",
      "use": "MenuTrigger",
      "why": "it gives the trigger aria-haspopup and aria-expanded, opens on ArrowDown, and closes on an action and returns focus — a Popover round a Menu does none of that"
    },
    {
      "when": "one value is picked for a form field from a modest list of plain text options",
      "use": "Select",
      "why": "a form value is a listbox, not a menu; the native picker brings type-ahead and the mobile platform picker for free"
    },
    {
      "when": "the user types to narrow a long list down to one value",
      "use": "Combobox"
    },
    {
      "when": "it is primary navigation rather than a list of actions",
      "use": "NavItem",
      "why": "a menu's rows are actions; links to pages belong in the document's navigation, where a screen reader's landmarks find them"
    },
    {
      "when": "any action or page should be reachable by searching",
      "use": "CommandPalette"
    }
  ],
  "composition": {
    "order": [
      "Menu",
      "MenuSection",
      "MenuItem"
    ],
    "note": "MenuItem and MenuSection are collection elements: Menu reads them from its DIRECT children to build the collection, and neither renders anything itself. Every MenuItem needs a `key`, and `onAction`, `selectedKeys` and `disabledKeys` all speak in those keys. To open it from a button, put the Button and the Menu inside a MenuTrigger. Rows that depend on state are written out, not collected into an array: a ternary between two rows, or `cond ? <MenuItem /> : null` for a row that comes and goes. A list that comes from data is `items` with a render function, or a `.map()` that is a MenuSection's only child.",
    "example": "<Menu aria-label=\"Row actions\" onAction={(key) => run(key)}><MenuSection title=\"Edit\"><MenuItem key=\"rename\" icon={<Icon as={Pencil} />}>Rename</MenuItem><MenuItem key=\"duplicate\">Duplicate</MenuItem></MenuSection><MenuSection aria-label=\"Danger\"><MenuItem key=\"archive\" isDisabled>Archive</MenuItem></MenuSection></Menu>"
  },
  "variants": {
    "selectionMode": {
      "none": {
        "use": "the default — a list of actions. Rows are `menuitem`, and there is no check slot"
      },
      "single": {
        "use": "Figma's Type=Single — one current choice, such as a sort order or the active workspace. Rows are `menuitemradio`"
      },
      "multiple": {
        "use": "Figma's Type=Multi — independent toggles, such as visible columns. Rows are `menuitemcheckbox`"
      }
    }
  },
  "slots": {
    "children": {
      "accepts": [
        "MenuItem",
        "MenuSection"
      ]
    }
  },
  "a11y": {
    "role": "menu",
    "guarantees": [
      "one tab stop; arrow keys move between rows, Home and End jump, and typing a letter moves to the next row starting with it",
      "disabled rows are listed and announced as unavailable, and the arrow keys skip them",
      "with a selection mode, rows are `menuitemradio` or `menuitemcheckbox` and announce `aria-checked` — the check glyph is never the only signal",
      "the pointer and the keyboard share one highlighted row: hovering a row focuses it"
    ],
    "requires": [
      "an accessible name — `aria-label` or `aria-labelledby` naming what the actions are for"
    ],
    "notes": [
      "A menu is not a form control. It has no `name` and submits nothing; `selectedKeys` is state you hold.",
      "Titled sections are named groups. An untitled section needs `aria-label`, and is set off by a rule.",
      "Submenus need a MenuTrigger. In a standalone Menu a row with MenuItem children renders as a plain row: there is no root to open a submenu from."
    ]
  },
  "antiPatterns": [
    {
      "dont": "wrapping MenuItems in a <div>, a fragment or your own component inside Menu",
      "why": "Menu reads its collection from its direct children; a wrapped row is invisible to it and renders nothing"
    },
    {
      "dont": "collecting rows into `const rows: React.ReactElement[] = []` with `push`, then `<Menu>{rows}</Menu>`",
      "why": "the children are typed `CollectionChildren`: one collection element, an array of them, or a render function. `ReactElement` defaults its props to `unknown`, which is not an item's props, so the array fails to compile. Contracts-only generations of the demo's Agents screen did this on 2 of 3 tries. Write the rows out, with a ternary for one that changes."
    },
    {
      "dont": "`{cond && <MenuItem />}`, or written-out rows beside a `.map()`",
      "why": "neither compiles: `false` is not a collection element, and an array beside elements is not one either. Write `cond ? <MenuItem /> : null`, and put the mapped rows in a MenuSection of their own"
    },
    {
      "dont": "an `onClick` on MenuItem",
      "why": "MenuItem renders nothing, so there is no element to click. Pass `onAction` to Menu; it receives the item's key, from the pointer, Enter and Space alike"
    },
    {
      "dont": "using Menu for a form field's value",
      "why": "a screen reader announces a menu as actions, and a form cannot read it. Use Select or Combobox"
    },
    {
      "dont": "putting links to other pages in a Menu",
      "why": "they vanish from the page's link list and landmarks. Navigation belongs in NavItem or Sidebar"
    }
  ],
  "stylesheet": "src/styles/menu.css",
  "tokens": [
    "--border-focus",
    "--border-strong",
    "--border-subtle",
    "--border-width-default",
    "--border-width-thick",
    "--font-family-sans",
    "--font-weight-regular",
    "--icon-default",
    "--icon-disabled",
    "--icon-primary",
    "--icon-size-sm",
    "--ion-menu-min-width",
    "--ion-shadow-shadow-lg",
    "--radius-sm",
    "--radius-xl",
    "--spacing-12",
    "--spacing-2",
    "--spacing-4",
    "--spacing-40",
    "--spacing-6",
    "--spacing-8",
    "--surface-default",
    "--surface-hover",
    "--surface-primary-subtle",
    "--text-disabled",
    "--text-primary",
    "--text-secondary",
    "--text-tertiary",
    "--type-body",
    "--type-body-line-height",
    "--type-caption",
    "--type-caption-line-height"
  ],
  "props": {
    "className": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    },
    "style": {
      "type": "React.CSSProperties | undefined",
      "required": false,
      "origin": "own"
    },
    "escapeKeyBehavior": {
      "type": "\"none\" | \"clearSelection\" | undefined",
      "required": false,
      "origin": "aria",
      "description": "Whether pressing the escape key should clear selection in the menu or not.\n\nMost experiences should not modify this option as it eliminates a keyboard user's ability to\neasily clear selection. Only use if the escape key is being handled externally or should not\ntrigger selection clearing contextually.",
      "values": [
        "none",
        "clearSelection"
      ],
      "tags": {
        "default": "'clearSelection'"
      }
    },
    "autoFocus": {
      "type": "boolean | FocusStrategy | undefined",
      "required": false,
      "origin": "aria",
      "description": "Where the focus should be set."
    },
    "shouldFocusWrap": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "aria",
      "description": "Whether keyboard navigation is circular."
    },
    "onAction": {
      "type": "((key: Key, value: T) => void) | undefined",
      "required": false,
      "origin": "aria",
      "description": "Handler that is called when an item is selected."
    },
    "onClose": {
      "type": "(() => void) | undefined",
      "required": false,
      "origin": "aria",
      "description": "Handler that is called when the menu should close after selecting an item."
    },
    "children": {
      "type": "CollectionChildren<T>",
      "required": true,
      "origin": "aria",
      "description": "The contents of the collection."
    },
    "items": {
      "type": "Iterable<T> | undefined",
      "required": false,
      "origin": "aria",
      "description": "Item objects in the collection."
    },
    "disabledKeys": {
      "type": "Iterable<Key> | undefined",
      "required": false,
      "origin": "aria",
      "description": "The item keys that are disabled. These items cannot be selected, focused, or otherwise\ninteracted with."
    },
    "selectionMode": {
      "type": "SelectionMode | undefined",
      "required": false,
      "origin": "aria",
      "description": "The type of selection that is allowed in the collection.",
      "values": [
        "multiple",
        "single",
        "none"
      ]
    },
    "disallowEmptySelection": {
      "type": "boolean | undefined",
      "required": false,
      "origin": "aria",
      "description": "Whether the collection allows empty selection."
    },
    "selectedKeys": {
      "type": "Iterable<Key> | \"all\" | undefined",
      "required": false,
      "origin": "aria",
      "description": "The currently selected keys in the collection (controlled)."
    },
    "defaultSelectedKeys": {
      "type": "Iterable<Key> | \"all\" | undefined",
      "required": false,
      "origin": "aria",
      "description": "The initial selected keys in the collection (uncontrolled)."
    },
    "onSelectionChange": {
      "type": "((keys: Selection) => void) | undefined",
      "required": false,
      "origin": "aria",
      "description": "Handler that is called when the selection changes."
    },
    "id": {
      "type": "string | undefined",
      "required": false,
      "origin": "aria",
      "description": "The element's unique identifier. See\n[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id)."
    },
    "aria-label": {
      "type": "string | undefined",
      "required": false,
      "origin": "aria",
      "description": "Defines a string value that labels the current element."
    },
    "aria-labelledby": {
      "type": "string | undefined",
      "required": false,
      "origin": "aria",
      "description": "Identifies the element (or elements) that labels the current element."
    },
    "aria-describedby": {
      "type": "string | undefined",
      "required": false,
      "origin": "aria",
      "description": "Identifies the element (or elements) that describes the object."
    },
    "aria-details": {
      "type": "string | undefined",
      "required": false,
      "origin": "aria",
      "description": "Identifies the element (or elements) that provide a detailed, extended description for the\nobject."
    }
  },
  "propCounts": {
    "own": 2,
    "aria": 18,
    "dom": 0,
    "other": 0
  }
}
