# Menu

Menus display a list of actions or options that a user can choose.

## Example

The menu is wrapped in a [`ui.menu_trigger`](./menu_trigger.md) along with a trigger element such as an [`ui.action_button`](./action_button.md).

```python
from deephaven import ui


my_menu_example = ui.menu_trigger(
    ui.action_button("Edit"),
    ui.menu(
        ui.item("Cut", key="cut"),
        ui.item("Copy", key="copy"),
        ui.item("Paste", key="paste"),
        ui.item("Replace", key="replace"),
        on_action=lambda key: print(key),
    ),
)
```

![Menu Basic Example](../_assets/menu_basic.png)

## Content

`menu` accepts `item` elements as children, each with a `key` prop. Basic usage of `menu`, seen in the example above, shows multiple items populated with a string.

## Events

Use the `on_change` prop as a callback to handle press events on items when `selection_mode` is either `single` or `multiple`. See [Selection](#selection) for more information.

Menu also supports the `on_action` callback when `selection_mode` is `none` (default).

```python
from deephaven import ui


@ui.component
def open_action_example():
    action, set_action = ui.use_state()
    return ui.flex(
        ui.menu_trigger(
            ui.action_button("Edit"),
            ui.menu(
                ui.item("Cut", key="cut"),
                ui.item("Copy", key="copy"),
                ui.item("Paste", key="paste"),
                on_action=set_action,
            ),
        ),
        ui.text(f"Action {action}"),
        gap="size-100",
        align_items="center",
    )


my_open_action_example = open_action_example()
```

## Selection

Menu supports multiple selection modes. By default, selection is disabled. However, this can be changed using the `selection_mode` prop. Use `default_selected_keys` to provide a default set of selected items (uncontrolled) or `selected_keys` to set the selected items (controlled). The values of the selected keys must match the key props of the items.

```python
from deephaven import ui


@ui.component
def single_selection_example():
    selected, set_selected = ui.use_state(["middle"])
    return ui.flex(
        ui.menu_trigger(
            ui.action_button("Align"),
            ui.menu(
                ui.item("Left", key="left"),
                ui.item("Middle", key="middle"),
                ui.item("Right", key="right"),
                selection_mode="single",
                selected_keys=selected,
                on_change=set_selected,
            ),
        ),
        ui.text(f"Current selection (controlled) {selected}"),
        gap="size-100",
        align_items="center",
    )


my_single_selection_example = single_selection_example()
```

Set `selection_mode` prop to `multiple` to allow more than one selection.

```python
from deephaven import ui


@ui.component
def multiple_selection_example():
    selected, set_selected = ui.use_state("all")
    return ui.flex(
        ui.menu_trigger(
            ui.action_button("Show"),
            ui.menu(
                ui.item("Sidebar", key="sidebar"),
                ui.item("Searchbar", key="searchbar"),
                ui.item("Tools", key="tools"),
                ui.item("Console", key="console"),
                selection_mode="multiple",
                selected_keys=selected,
                on_change=set_selected,
            ),
            close_on_select=False,
        ),
        ui.text(f"Current selection (controlled) {selected}"),
        gap="size-100",
        align_items="center",
    )


my_multiple_selection_example = multiple_selection_example()
```

## Links

By default, interacting with an item in a Menu triggers `on_action` and optionally `on_change` depending on the `selection_mode`. Alternatively, items may be links to another page or website. This can be achieved by passing the `href` prop to the `item` component. Link items in a menu are not selectable.

```python
from deephaven import ui


my_link_example = ui.menu_trigger(
    ui.action_button("Links"),
    ui.menu(
        ui.item("Adobe", href="https://adobe.com/", target="_blank"),
        ui.item("Apple", href="https://apple.com/", target="_blank"),
        ui.item("Google", href="https://google.com/", target="_blank"),
        ui.item("Microsoft", href="https://microsoft.com/", target="_blank"),
    ),
)
```

## Sections

```python
from deephaven import ui


@ui.component
def sections_example():
    selected, set_selected = ui.use_state(["bold", "left"])
    return (
        ui.menu_trigger(
            ui.action_button("Show"),
            ui.menu(
                ui.section(
                    ui.item("Bold", key="bold"),
                    ui.item("Underline", key="underline"),
                    title="Styles",
                ),
                ui.section(
                    ui.item("Left", key="left"),
                    ui.item("Middle", key="middle"),
                    ui.item("Right", key="right"),
                    title="Align",
                ),
                selection_mode="multiple",
                selected_keys=selected,
                on_change=set_selected,
            ),
            close_on_select=False,
        ),
    )


my_sections_example = sections_example()
```

## Unavailable items

`contextual_help_trigger` disables a menu item’s action and replaces it with a popover with information on why the item is unavailable and may link users to more information elsewhere.

The `contextual_help_trigger` accepts exactly two children: the `item` which triggers opening of the `dialog` and the Dialog itself. The trigger must be the first child passed into the `contextual_help_trigger` and should be an `item`. Similar to `contextual_help`, the layout of the `dialog` is very deliberate. See [`contextual_help`](./contextual_help.md) for further explanation.

Setting the `is_unavailable` prop on the `contextual_help_trigger` makes the menu item unavailable and enables the `dialog` with contextual help, allowing for programmatic control.

Note that the `menu`s `on_action` and `on_change` callbacks will not fire for items made unavailable by a `contextual_help_trigger`.

The example below illustrates how one would set up a `menu` to use `contextual_help_trigger`.

```python
from deephaven import ui


my_menu_example = ui.menu_trigger(
    ui.action_button("Edit"),
    ui.menu(
        ui.item("Undo", key="undo"),
        ui.item("Redo", key="redo"),
        ui.contextual_help_trigger(
            ui.item("Cut", key="cut"),
            ui.dialog(
                ui.heading("Cut"),
                ui.content("Please select text for 'Cut' to be enabled."),
            ),
            is_unavailable=True,
        ),
        ui.contextual_help_trigger(
            ui.item("Copy", key="copy"),
            ui.dialog(
                ui.heading("Copy"),
                ui.content("Please select text for 'Copy' to be enabled."),
            ),
            is_unavailable=True,
        ),
        ui.contextual_help_trigger(
            ui.item("Paste", key="paste"),
            ui.dialog(ui.heading("Paste"), ui.content("You have nothing to 'Paste'.")),
        ),
    ),
)
```

## Submenus

Submenus can be created by wrapping an item and a menu in a `submenu_trigger`. The `submenu_trigger` accepts exactly two children: the `item` which triggers opening of the submenu, and the `menu` itself. Each submenu’s `menu` accepts its own set of menu props, allowing you to customize its user action and selection behavior.

```python
from deephaven import ui


my_submenu_example = ui.menu_trigger(
    ui.action_button("Actions"),
    ui.menu(
        ui.item("Cut", key="cut"),
        ui.item("Copy", key="copy"),
        ui.item("Paste", key="paste"),
        ui.submenu_trigger(
            ui.item("Share", key="share"),
            ui.menu(
                ui.item("Copy link", key="copy link"),
                ui.submenu_trigger(
                    ui.item("Email", key="email"),
                    ui.menu(
                        ui.item("Email as attachment", key="attachment"),
                        ui.item("Email as link", key="link"),
                        on_action=lambda key: print(f"Email menu {key} action"),
                    ),
                ),
                ui.item("SMS", key="sms"),
                on_action=lambda key: print(f"Share menu {key} action"),
            ),
        ),
        ui.item("Delete", key="delete"),
        on_action=lambda key: print(f"Root menu {key} action"),
    ),
)
```

## API Reference

A menu displays a list of actions or options that a user can choose.

**Returns:** `BaseElement` The menu element.

<ParamTable param={{"module_name": "deephaven.ui.", "name": "menu", "parameters": [{"name": "*children", "type": "str | int | float | bool | BaseElement", "description": "The contents of the collection."}, {"name": "auto_focus", "type": "bool | Literal['first', 'last'] | None", "description": "Where the focus should be set.", "default": "None"}, {"name": "should_focus_wrap", "type": "bool | None", "description": "Whether keyboard navigation is circular.", "default": "None"}, {"name": "disabled_keys", "type": "List[str | int | float | bool] | None", "description": "The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.", "default": "None"}, {"name": "selection_mode", "type": "Literal['none', 'single', 'multiple'] | None", "description": "The type of selection that is allowed in the collection.", "default": "None"}, {"name": "disallow_empty_selection", "type": "bool | None", "description": "Whether the collection allows empty selection.", "default": "None"}, {"name": "selected_keys", "type": "Literal['all'] | List[str | int | float | bool] | None", "description": "The currently selected keys in the collection (controlled).", "default": "None"}, {"name": "default_selected_keys", "type": "Literal['all'] | List[str | int | float | bool] | None", "description": "The default selected keys in the collection (uncontrolled).", "default": "None"}, {"name": "on_action", "type": "Callable[[str | int | float | bool], None] | None", "description": "Handler that is called when an item is selected.", "default": "None"}, {"name": "on_close", "type": "Callable[[], None] | None", "description": "Handler that is called when the menu should close after selecting an item.", "default": "None"}, {"name": "on_change", "type": "Callable[[Literal['all'] | List[str | int | float | bool]], None] | None", "description": "Handler that is called when the selection changes.", "default": "None"}, {"name": "flex", "type": "str | float | bool | None", "description": "When used in a flex layout, specifies how the element will grow or shrink to fit the space available.", "default": "None"}, {"name": "flex_grow", "type": "float | None", "description": "When used in a flex layout, specifies how much the element will grow to fit the space available.", "default": "None"}, {"name": "flex_shrink", "type": "float | None", "description": "When used in a flex layout, specifies how much the element will shrink to fit the space available.", "default": "None"}, {"name": "flex_basis", "type": "str | float | None", "description": "When used in a flex layout, specifies the initial size of the element.", "default": "None"}, {"name": "align_self", "type": "Literal['auto', 'normal', 'start', 'end', 'center', 'flex-start', 'flex-end', 'self-start', 'self-end', 'stretch'] | None", "description": "Overrides the align_items property of a flex or grid container.", "default": "None"}, {"name": "justify_self", "type": "Literal['auto', 'normal', 'start', 'end', 'flex-start', 'flex-end', 'self-start', 'self-end', 'center', 'left', 'right', 'stretch'] | None", "description": "Specifies how the element is justified inside a flex or grid container.", "default": "None"}, {"name": "order", "type": "int | None", "description": "The layout order for the element within a flex or grid container.", "default": "None"}, {"name": "grid_area", "type": "str | None", "description": "The name of the grid area to place the element in.", "default": "None"}, {"name": "grid_row", "type": "str | None", "description": "The name of the grid row to place the element in.", "default": "None"}, {"name": "grid_row_start", "type": "str | None", "description": "The name of the grid row to start the element in.", "default": "None"}, {"name": "grid_row_end", "type": "str | None", "description": "The name of the grid row to end the element in.", "default": "None"}, {"name": "grid_column", "type": "str | None", "description": "The name of the grid column to place the element in.", "default": "None"}, {"name": "grid_column_start", "type": "str | None", "description": "The name of the grid column to start the element in.", "default": "None"}, {"name": "grid_column_end", "type": "str | None", "description": "The name of the grid column to end the element in.", "default": "None"}, {"name": "margin", "type": "str | float | None", "description": "The margin to apply around the element.", "default": "None"}, {"name": "margin_top", "type": "str | float | None", "description": "The margin to apply above the element.", "default": "None"}, {"name": "margin_bottom", "type": "str | float | None", "description": "The margin to apply below the element.", "default": "None"}, {"name": "margin_start", "type": "str | float | None", "description": "The margin to apply before the element.", "default": "None"}, {"name": "margin_end", "type": "str | float | None", "description": "The margin to apply after the element.", "default": "None"}, {"name": "margin_x", "type": "str | float | None", "description": "The margin to apply to the left and right of the element.", "default": "None"}, {"name": "margin_y", "type": "str | float | None", "description": "The margin to apply to the top and bottom of the element.", "default": "None"}, {"name": "width", "type": "str | float | None", "description": "The width of the element.", "default": "None"}, {"name": "height", "type": "str | float | None", "description": "The height of the element.", "default": "None"}, {"name": "min_width", "type": "str | float | None", "description": "The minimum width of the element.", "default": "None"}, {"name": "min_height", "type": "str | float | None", "description": "The minimum height of the element.", "default": "None"}, {"name": "max_width", "type": "str | float | None", "description": "The maximum width of the element.", "default": "None"}, {"name": "max_height", "type": "str | float | None", "description": "The maximum height of the element.", "default": "None"}, {"name": "position", "type": "Literal['static', 'relative', 'absolute', 'fixed', 'sticky'] | None", "description": "Specifies how the element is positioned.", "default": "None"}, {"name": "top", "type": "str | float | None", "description": "The distance from the top of the containing element.", "default": "None"}, {"name": "bottom", "type": "str | float | None", "description": "The distance from the bottom of the containing element.", "default": "None"}, {"name": "start", "type": "str | float | None", "description": "The distance from the start of the containing element.", "default": "None"}, {"name": "end", "type": "str | float | None", "description": "The distance from the end of the containing element.", "default": "None"}, {"name": "left", "type": "str | float | None", "description": "The distance from the left of the containing element.", "default": "None"}, {"name": "right", "type": "str | float | None", "description": "The distance from the right of the containing element.", "default": "None"}, {"name": "z_index", "type": "int | None", "description": "The stack order of the element.", "default": "None"}, {"name": "is_hidden", "type": "bool | None", "description": "Whether the element is hidden.", "default": "None"}, {"name": "id", "type": "str | None", "description": "A unique identifier for the element.", "default": "None"}, {"name": "exclude_from_tab_order", "type": "bool | None", "description": "Whether the element should be excluded from the tab order.", "default": "None"}, {"name": "aria_label", "type": "str | None", "description": "The label for the element.", "default": "None"}, {"name": "aria_labelledby", "type": "str | None", "description": "The id of the element that labels the element.", "default": "None"}, {"name": "aria_describedby", "type": "str | None", "description": "The id of the element that describes the element.", "default": "None"}, {"name": "aria_details", "type": "str | None", "description": "The details for the element.", "default": "None"}, {"name": "UNSAFE_class_name", "type": "str | None", "description": "A CSS class to apply to the element.", "default": "None"}, {"name": "UNSAFE_style", "type": "Dict[str, Any] | None", "description": "A CSS style to apply to the element.", "default": "None"}, {"name": "key", "type": "str | None", "description": "A unique identifier used by React to render elements in a list.", "default": "None"}]}} />
