From 37cb5a7c5bf01902a65274c26ec98099e45aabc5 Mon Sep 17 00:00:00 2001 From: ethanalvizo <55671206+ethanalvizo@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:33:36 -0400 Subject: [PATCH] docs: list view (#769) Closes #686 Note: Empty state example was intentionally omitted as this [bug](https://github.com/deephaven/deephaven-plugins/issues/744) was found when rendering an empty list view component. Can add in the example once that is fixed --- plugins/ui/docs/components/list_view.md | 313 ++++++++++++++++++ .../src/deephaven/ui/components/list_view.py | 155 +++++++-- .../deephaven/ui/components/types/layout.py | 1 - .../deephaven/ui/components/types/validate.py | 2 + plugins/ui/src/deephaven/ui/types/types.py | 2 + 5 files changed, 448 insertions(+), 25 deletions(-) create mode 100644 plugins/ui/docs/components/list_view.md diff --git a/plugins/ui/docs/components/list_view.md b/plugins/ui/docs/components/list_view.md new file mode 100644 index 000000000..c66ddc668 --- /dev/null +++ b/plugins/ui/docs/components/list_view.md @@ -0,0 +1,313 @@ +# List View + +List view displays a list of interactive items, and allows a user to navigate, select, or perform an action. It offers greater flexibility in the contents it can render and can distinguish between row selection and actions performed on a row. This makes list view an ideal component for turning table columns into interactive lists. + +## Example + +```python +from deephaven import ui + + +@ui.component +def ui_list_view(): + return ui.list_view( + ui.item("Option 1"), + ui.item("Option 2"), + ui.item("Option 3"), + ui.item("Option 4"), + default_selected_keys=["Option 2", "Option 3"], + ) + + +my_list_view = ui_list_view() +``` + +## Table Source Example + +List view items can also be generated from a table directly or using `item_table_source`. + +### Passing Table Directly + +This method is ideal for quickly displaying a static dataset. By default, the first column is used as the key and label. + +```python +from deephaven import ui, new_table +from deephaven.column import string_col + +_colors = new_table( + [ + string_col("Colors", ["Red", "Blue", "Green"]), + ] +) + + +@ui.component +def ui_list_view_table(): + return ui.list_view(_colors) + + +my_list_view_table = ui_list_view_table() +``` + +### 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 ui_list_view_table_source(): + source = ui.item_table_source(_table, key_column="Keys", label_column="Labels") + return ui.list_view(source) + + +my_list_view_table_source = ui_list_view_table_source() +``` + +## Events + +List view accepts an action that can be triggered when a user performs an action on an item. + +```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 ui_list_view_actions(): + action_item_keys, set_action_item_keys = ui.use_state(["", ""]) + on_action = ui.use_callback( + lambda action_key, item_key: set_action_item_keys([action_key, str(item_key)]), + [], + ) + + source = ui.item_table_source( + _table, + key_column="Keys", + label_column="Labels", + actions=ui.list_action_group( + "Edit", + "Delete", + on_action=on_action, + ), + ) + lv = ui.list_view(source) + + text_action = ui.text("Action: " + " ".join(map(str, action_item_keys))) + + return lv, text_action + + +my_list_view_actions = ui_list_view_actions() +``` + +List view can also accept a handler that is called when the selection is changed. + +```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 ui_list_view_selection(): + value, set_value = ui.use_state(["key-2"]) + + def handle_change(e): + set_value(e) + print("Selection: " + ", ".join(map(str, e))) + + source = ui.item_table_source( + _table, + key_column="Keys", + label_column="Labels", + ) + lv = ui.list_view(source, on_change=handle_change) + + return lv + + +my_list_view_selection = ui_list_view_selection() +``` + +## Disabled Options + +To disable certain rows in the `ListView` component, use the `disabled_keys` prop. By setting this prop with an array of keys, you can prevent interaction with those rows, providing greater control and customization options for the `ListView` behavior. + +```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 ui_list_view_disabled(): + value, set_value = ui.use_state(["key-2"]) + + source = ui.item_table_source( + _table, + key_column="Keys", + label_column="Labels", + ) + lv = ui.list_view( + source, selected_keys=value, on_change=set_value, disabled_keys=["key-0"] + ) + + return lv + + +my_list_view_disabled = ui_list_view_disabled() +``` + +## Quiet State + +```python +from deephaven import ui + + +@ui.component +def ui_list_view_quiet(): + value, set_value = ui.use_state(["Text 2"]) + + quiet_list = ui.list_view( + "Text 1", + "Text 2", + "Text 3", + aria_label="List View - Quiet", + on_change=set_value, + selected_keys=value, + is_quiet=True, + ) + + default_list = ui.list_view( + "Text 1", + "Text 2", + "Text 3", + aria_label="List View - Default", + on_change=set_value, + selected_keys=value, + ) + return quiet_list, default_list + + +my_list_view_quiet = ui_list_view_quiet() +``` + +## Modifying Density + +To adjust the vertical padding of each row in the list view, use the `density` prop. + +```python +from deephaven import ui + + +@ui.component +def ui_list_view_density(): + value, set_value = ui.use_state(["Text 2"]) + + compact_list = ui.list_view( + "Text 1", + "Text 2", + "Text 3", + aria_label="List View - Compact", + on_change=set_value, + selected_keys=value, + density="compact", + ) + + spacious_list = ui.list_view( + "Text 1", + "Text 2", + "Text 3", + aria_label="List View - Spacious", + on_change=set_value, + selected_keys=value, + density="spacious", + ) + return compact_list, spacious_list + + +my_list_view_density = ui_list_view_density() +``` + +## Overflow Mode +The default behavior is to truncate content that overflows its row. Text can be wrapped instead by adding `wrap` to the `overflow_mode` prop. + +Note: Currently not supported if a table source is used. + +```python +from deephaven import ui + + +@ui.component +def ui_list_view_overflow(): + value, set_value = ui.use_state(["Text 2"]) + + truncated_list = ui.list_view( + "Really long Text 1", + "Really long Text 2", + "Really long Text 3", + aria_label="List View - Quiet", + on_change=set_value, + selected_keys=value, + overflow_mode="truncate", + width="150px", + ) + + wrapped_list = ui.list_view( + "Really long Text 1", + "Really long Text 2", + "Really long Text 3", + aria_label="List View - Quiet", + on_change=set_value, + selected_keys=value, + overflow_mode="wrap", + width="150px", + ) + return truncated_list, wrapped_list + + +my_list_view_overflow = ui_list_view_overflow() +``` + + +## API reference + +```{eval-rst} +.. dhautofunction:: deephaven.ui.list_view +``` + +## Item Table Source API reference + +```{eval-rst} +.. dhautofunction:: deephaven.ui.item_table_source +``` \ No newline at end of file diff --git a/plugins/ui/src/deephaven/ui/components/list_view.py b/plugins/ui/src/deephaven/ui/components/list_view.py index 365a40385..a7956ed00 100644 --- a/plugins/ui/src/deephaven/ui/components/list_view.py +++ b/plugins/ui/src/deephaven/ui/components/list_view.py @@ -9,7 +9,24 @@ from .._internal.utils import create_props, unpack_item_table_source from .basic import component_element from .item import Item -from ..types import ListViewDensity, Selection, SelectionMode +from ..types import ( + ListViewDensity, + ListViewOverflowMode, + Selection, + SelectionMode, + SelectionStyle, + Key, +) +from .types import ( + LoadingState, + DisabledBehavior, + LayoutFlex, + DimensionValue, + AlignSelf, + JustifySelf, + Position, + CSSProperties, +) ListViewElement = Element @@ -25,14 +42,64 @@ def list_view( *children: Item | Table | ItemTableSource, density: ListViewDensity | None = "COMPACT", - default_selected_keys: Selection | None = None, - selected_keys: Selection | None = None, - selection_mode: SelectionMode | None = "MULTIPLE", + is_quiet: bool | None = None, + loading_state: LoadingState | None = None, + overflow_mode: ListViewOverflowMode = "truncate", render_empty_state: Element | None = None, + disabled_behavior: DisabledBehavior | None = None, + disabled_keys: Selection | None = None, + selection_mode: SelectionMode | None = "MULTIPLE", + disallow_empty_selection: bool | None = None, + selected_keys: Selection | None = None, + default_selected_keys: Selection | None = None, + selection_style: SelectionStyle | None = None, + on_action: Callable[[Key, str], None] | None = None, on_selection_change: Callable[[Selection], None] | None = None, on_change: Callable[[Selection], None] | None = None, + flex: LayoutFlex | None = None, + flex_grow: float | None = None, + flex_shrink: float | None = None, + flex_basis: DimensionValue | None = None, + align_self: AlignSelf | None = None, + justify_self: JustifySelf | None = None, + order: int | None = None, + grid_area: str | None = None, + grid_row: str | None = None, + grid_column: str | None = None, + grid_row_start: str | None = None, + grid_row_end: str | None = None, + grid_column_start: str | None = None, + grid_column_end: str | None = None, + margin: DimensionValue | None = None, + margin_top: DimensionValue | None = None, + margin_bottom: DimensionValue | None = None, + margin_start: DimensionValue | None = None, + margin_end: DimensionValue | None = None, + margin_x: DimensionValue | None = None, + margin_y: DimensionValue | None = None, + width: DimensionValue | None = None, + height: DimensionValue | None = None, + min_width: DimensionValue | None = None, + min_height: DimensionValue | None = None, + max_width: DimensionValue | None = None, + max_height: DimensionValue | None = None, + position: Position | None = None, + top: DimensionValue | None = None, + bottom: DimensionValue | None = None, + left: DimensionValue | None = None, + right: DimensionValue | None = None, + start: DimensionValue | None = None, + end: DimensionValue | None = None, + z_index: int | None = None, + is_hidden: bool | None = None, + id: str | None = None, + aria_label: str | None = None, + aria_labelledby: str | None = None, + aria_describedby: str | None = None, + aria_details: str | None = None, + UNSAFE_class_name: str | None = None, + UNSAFE_style: CSSProperties | None = None, key: str | None = None, - **props: Any, ) -> ListViewElement: """ A list view that can be used to create a list of items. Children should be one of three types: @@ -47,25 +114,65 @@ def list_view( Args: *children: The options to render within the list_view. - density: - Sets the amount of vertical padding within each cell. - default_selected_keys: - The initial selected keys in the collection (uncontrolled). - selected_keys: - The currently selected keys in the collection (controlled). - selection_mode: - By default `"MULTIPLE"`, which allows multiple selection. - May also be `"SINGLE"` to allow only single selection, or `"None"`/`None` to allow no selection. - render_empty_state: - Sets what the `list_view` should render when there is no content to display. - on_selection_change: - Handler that is called when the selection changes. - on_change: - Alias of `on_selection_change`. Handler that is called when the selection changes. - key: - A unique identifier used by React to render elements in a list. - **props: - Any other ListView prop, except items, dragAndDropHooks, and onLoadMore. + density: Sets the amount of vertical padding within each cell. + is_quiet: Whether the ListView should use the quiet style. + loading_state: The loading state of the ListView. Determines whether to show a loading spinner. + overflow_mode: The behaviour of the text when it overflows the cell. + render_empty_state: Sets what the `list_view` should render when there is no content to display. + disabled_behavior: Whether disabled_keys applies to all interactions or just selection. + disabled_keys: The keys that should be disabled. These cannot be selected, focused, or interacted with + selection_mode: By default `"MULTIPLE"`, which allows multiple selection. May also be `"SINGLE"` to allow only single selection, or `"None"`/`None` to allow no selection. + disallow_empty_selection: Whether the ListView should disallow empty selection. + selected_keys: The currently selected keys in the collection (controlled). + default_selected_keys: The initial selected keys in the collection (uncontrolled). + selection_style: How the selection should be displayed. + on_action: Handler that is called when the user performs an action on an item. The user event depends on the collection's selection_style and interaction modality. + on_change: Handler that is called when the selection changes. + on_selection_change: Deprecated. Use on_change instead.Handler that is called when the selection changes. + flex: When used in a flex layout, specifies how the element will grow or shrink to fit the space available. + flex_grow: When used in a flex layout, specifies how the element will grow to fit the space available. + flex_shrink: When used in a flex layout, specifies how the element will shrink to fit the space available. + flex_basis: When used in a flex layout, specifies the initial main size of the element. + align_self: Overrides the alignItems property of a flex or grid container. + justify_self: Species how the element is justified inside a flex or grid container. + order: The layout order for the element within a flex or grid container. + grid_area: When used in a grid layout, specifies the named grid area that the element should be placed in within the grid. + grid_row: When used in a grid layout, specifies the row the element should be placed in within the grid. + grid_column: When used in a grid layout, specifies the column the element should be placed in within the grid. + grid_row_start: When used in a grid layout, specifies the starting row to span within the grid. + grid_row_end: When used in a grid layout, specifies the ending row to span within the grid. + grid_column_start: When used in a grid layout, specifies the starting column to span within the grid. + grid_column_end: When used in a grid layout, specifies the ending column to span within the grid. + margin: The margin for all four sides of the element. + margin_top: The margin for the top side of the element. + margin_bottom: The margin for the bottom side of the element. + margin_start: The margin for the logical start side of the element, depending on layout direction. + margin_end: The margin for the logical end side of the element, depending on layout direction. + margin_x: The margin for the left and right sides of the element. + margin_y: The margin for the top and bottom sides of the element. + width: The width of the element. + height: The height of the element. + min_width: The minimum width of the element. + min_height: The minimum height of the element. + max_width: The maximum width of the element. + max_height: The maximum height of the element. + position: Specifies how the element is positioned. + top: The top position of the element. + bottom: The bottom position of the element. + left: The left position of the element. + right: The right position of the element. + start: The logical start position of the element, depending on layout direction. + end: The logical end position of the element, depending on layout direction. + z_index: The stacking order for the element + is_hidden: Hides the element. + id: The unique identifier of the element. + aria_label: Defines a string value that labels the current element. + aria_labelledby: Identifies the element (or elements) that labels the current element. + aria_describedby: Identifies the element (or elements) that describes the object. + aria_details: Identifies the element (or elements) that provide a detailed, extended description for the object. + UNSAFE_class_name: Set the CSS className for the element. Only use as a last resort. Use style props instead. + UNSAFE_style: Set the inline style for the element. Only use as a last resort. Use style props instead. + key: A unique identifier used by React to render elements in a list. Returns: The rendered ListView. diff --git a/plugins/ui/src/deephaven/ui/components/types/layout.py b/plugins/ui/src/deephaven/ui/components/types/layout.py index dfccfa931..68cac2a9d 100644 --- a/plugins/ui/src/deephaven/ui/components/types/layout.py +++ b/plugins/ui/src/deephaven/ui/components/types/layout.py @@ -129,7 +129,6 @@ Overflow = Union[Literal["visible", "hidden", "clip", "scroll", "auto"], str] OverflowMode = Literal["wrap", "collapse"] - Alignment = Literal["start", "end"] ButtonGroupAlignment = Literal["start", "center", "end"] diff --git a/plugins/ui/src/deephaven/ui/components/types/validate.py b/plugins/ui/src/deephaven/ui/components/types/validate.py index 81687390d..a95ff39d9 100644 --- a/plugins/ui/src/deephaven/ui/components/types/validate.py +++ b/plugins/ui/src/deephaven/ui/components/types/validate.py @@ -22,3 +22,5 @@ AutoCompleteModes = Literal["on", "off"] AutoCapitalizeModes = Literal["off", "none", "on", "sentences", "words", "characters"] + +DisabledBehavior = Literal["selection", "all"] diff --git a/plugins/ui/src/deephaven/ui/types/types.py b/plugins/ui/src/deephaven/ui/types/types.py index d0fa28513..6e1f2ccb1 100644 --- a/plugins/ui/src/deephaven/ui/types/types.py +++ b/plugins/ui/src/deephaven/ui/types/types.py @@ -463,6 +463,7 @@ class SliderChange(TypedDict): TableData = Dict[ColumnName, ColumnData] SelectionArea = Literal["CELL", "ROW", "COLUMN"] SelectionMode = Literal["SINGLE", "MULTIPLE"] +SelectionStyle = Literal["checkbox", "highlight"] Sentinel = Any TransformedData = Any ActionMenuDirection = Literal["bottom", "top", "left", "right", "start", "end"] @@ -502,6 +503,7 @@ class SliderChange(TypedDict): ] Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"] ListViewDensity = Literal["COMPACT", "NORMAL", "SPACIOUS"] +ListViewOverflowMode = Literal["truncate", "wrap"] ActionGroupDensity = Literal["compact", "regular"] TabDensity = Literal["compact", "regular"] Dependencies = Union[Tuple[Any], List[Any]]