diff --git a/plugins/ui/docs/components/menu.md b/plugins/ui/docs/components/menu.md new file mode 100644 index 000000000..fe6793722 --- /dev/null +++ b/plugins/ui/docs/components/menu.md @@ -0,0 +1,246 @@ +# Menu + +Menus display a list of actions or options that a user can choose. + +## Example + +```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), + ), +) +``` + +## 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_selection_change` prop as a callback to handle press events on items when `selection_mode` is either `single` or `multiple`. See 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 value of the selected keys must match the key prop 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_selection_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(["sidebar", "console"]) + 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_selection_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_selection_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_selection_change=set_selected, + ), + close_on_select=False, + ), + ) + + +my_sections_example = sections_example() +``` + +## 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", "attachment"), + ui.item("Email as link", "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"), + ), +) +``` + +## Using item_table_source + +`item_table_source` is used to create complex items from a table (ie., defining which columns are the keys/labels of the data). + +```python +from deephaven import ui, new_table +from deephaven.column import string_col + +_table = new_table( + [ + string_col("Keys", ["key-0", "key-1", "key-2"]), + string_col("Labels", ["Option 0", "Option 1", "Option 2"]), + ] +) + + +@ui.component +def menu_table_source(): + source = ui.item_table_source(_table, key_column="Keys", label_column="Labels") + return ui.menu_trigger( + ui.action_button("Table source"), + ui.menu(source), + ) + + +my_menu_table_source = menu_table_source() +``` + +## API Reference + +```{eval-rst} +.. dhautofunction:: deephaven.ui.menu +``` diff --git a/plugins/ui/docs/components/menu_trigger.md b/plugins/ui/docs/components/menu_trigger.md new file mode 100644 index 000000000..34071e8e4 --- /dev/null +++ b/plugins/ui/docs/components/menu_trigger.md @@ -0,0 +1,210 @@ +# Menu Trigger + +The `menu_trigger` serves as a wrapper around a `menu` and its associated trigger, linking the menu's open state with the trigger's press state. + +## Example + +```python +from deephaven import ui + + +my_menu_trigger_example = ui.menu_trigger( + ui.action_button("Edit"), + ui.menu( + ui.item("Cut"), + ui.item("Copy"), + ui.item("Paste"), + ), +) +``` + +## Content + +The `menu_trigger` accepts exactly two children: the element which triggers the opening of the `menu` and the `menu` itself. The trigger element must be the first child passed into the `menu_trigger` and should support press events. + +## Events + +`menu_trigger` accepts an `on_open_change` handler which is triggered whenever the `menu` is opened or closed. + +```python +from deephaven import ui + + +@ui.component +def open_change_example(): + is_open, set_open = ui.use_boolean() + return ui.flex( + ui.menu_trigger( + ui.action_button("Edit"), + ui.menu( + ui.item("Cut"), + ui.item("Copy"), + ui.item("Paste"), + ), + on_open_change=set_open.toggle, + ), + ui.text(f"Currently open: {is_open}"), + gap="size-100", + align_items="center", + ) + + +my_open_change_example = open_change_example() +``` + +## Long press + +By default, a menu_trigger's menu is opened by pressing the trigger element or activating it via the Space or Enter keys. However, there may be cases in which your trigger element should perform a separate default action on press such as selection, and should only display the menu when long pressed. This behavior can be changed by providing `longPress` to the `trigger` prop. With this prop, the menu will only be opened upon pressing and holding the trigger element or by using the Option (Alt on Windows) + Down Arrow/Up Arrow keys while focusing the trigger element. + +The example below illustrates how one would setup a `menu_trigger` to have long press behavior. + +```python +from deephaven import ui + + +my_long_press_example = ui.menu_trigger( + ui.action_button("Crop tool", on_press=print("Cropping!")), + ui.menu( + ui.item("Crop Rotate"), + ui.item("Slice"), + ui.item("Clone stamp"), + ), + trigger="longPress", +) +``` + +## Visual options + +### Align and direction + +The `align` prop aligns the `menu` relative to the `trigger` and the direction prop controls the `direction` the `menu` will render + +```python +from deephaven import ui + + +my_align_example = ui.flex( + ui.menu_trigger( + ui.action_button("Edit"), + ui.menu( + ui.item("Cut"), + ui.item("Copy"), + ui.item("Paste"), + ), + align="start", + ), + ui.menu_trigger( + ui.action_button("View"), + ui.menu( + ui.item("Side bar"), + ui.item("Page options"), + ui.item("Edit panel"), + ), + align="end", + direction="top", + should_flip=False, + ), + ui.menu_trigger( + ui.action_button("Edit"), + ui.menu( + ui.item("Cut"), + ui.item("Copy"), + ui.item("Paste"), + ), + align="start", + direction="start", + ), + ui.menu_trigger( + ui.action_button("View"), + ui.menu( + ui.item("Side bar"), + ui.item("Page options"), + ui.item("Edit panel"), + ), + align="end", + direction="end", + ), + gap="size-100", +) +``` + +### Close on selection + +By default, the `menu` closes when an item is selected. To change this, set the `close_on_select` prop to `False`. This might be useful when multiple selection is used. + +```python +my_close_on_selection_example = ui.menu_trigger( + ui.action_button("View"), + ui.menu( + ui.item("Side bar"), + ui.item("Page options"), + ui.item("Edit panel"), + selection_mode="multiple", + ), + close_on_selection=False, +) +``` + +### Flipping + +By default, the `menu` flips direction automatically upon opening when space is limited. To change this, set the `should_flip` prop to `False`. Try scrolling the viewport close to the edge of the trigger in the example to see this in action. + +```python +from deephaven import ui + + +my_flip_example = ui.flex( + ui.menu_trigger( + ui.action_button("Edit"), + ui.menu( + ui.item("Cut"), + ui.item("Copy"), + ui.item("Paste"), + ), + should_flip=True, + ), + ui.menu_trigger( + ui.action_button("View"), + ui.menu( + ui.item("Side bar"), + ui.item("Page options"), + ui.item("Edit panel"), + ), + should_flip=False, + ), + gap="size-100", +) +``` + +### Open + +The `is_open` and `default_open` props on the `menu_trigger` control whether the Menu is open by default. They apply controlled and uncontrolled behavior on the `menu` respectively. + +```python +from deephaven import ui + + +@ui.component +def open_example(): + is_open, set_open = ui.use_boolean() + ui.menu_trigger( + ui.action_button("View"), + ui.menu( + ui.item("Side bar"), + ui.item("Page options"), + ui.item("Edit panel"), + selection_mode="multiple", + ), + is_open=is_open, + on_open_chnage=set_open, + ), + + +my_open_example = open_example() +``` + +## API Reference + +```{eval-rst} +.. dhautofunction:: deephaven.ui.menu_trigger +```