From b36cd9b20a080e7f0dc34bab8238918be383c24c Mon Sep 17 00:00:00 2001 From: mikebender Date: Fri, 8 Mar 2024 15:02:44 -0500 Subject: [PATCH] Clean up based on review - Move functions to be keyword args on ui.table call instead --- plugins/ui/setup.cfg | 1 + .../ui/src/deephaven/ui/components/table.py | 41 ++++- .../ui/src/deephaven/ui/elements/UITable.py | 167 +++++++++--------- .../js/src/elements/UITableMouseHandler.ts | 2 +- 4 files changed, 119 insertions(+), 92 deletions(-) diff --git a/plugins/ui/setup.cfg b/plugins/ui/setup.cfg index cc9bb3235..14e5882a4 100644 --- a/plugins/ui/setup.cfg +++ b/plugins/ui/setup.cfg @@ -28,6 +28,7 @@ install_requires = deephaven-core>=0.31.0 deephaven-plugin>=0.6.0 json-rpc + typing_extensions include_package_data = True [options.packages.find] diff --git a/plugins/ui/src/deephaven/ui/components/table.py b/plugins/ui/src/deephaven/ui/components/table.py index 4f0200c6a..119c77c1e 100644 --- a/plugins/ui/src/deephaven/ui/components/table.py +++ b/plugins/ui/src/deephaven/ui/components/table.py @@ -1,9 +1,44 @@ from deephaven.table import Table from ..elements import UITable +from ..types import ( + CellPressCallback, + ColumnPressCallback, + RowPressCallback, +) -def table(table: Table) -> UITable: +def table( + table: Table, + *, + on_row_press: RowPressCallback | None = None, + on_row_double_press: RowPressCallback | None = None, + on_cell_press: CellPressCallback | None = None, + on_cell_double_press: CellPressCallback | None = None, + on_column_press: ColumnPressCallback | None = None, + on_column_double_press: ColumnPressCallback | None = None, +) -> UITable: """ - Add some extra methods to the Table class for giving hints to displaying a table + Customization to how a table is displayed, how it behaves, and listen to UI events. + + Args: + table: The table to wrap + on_row_press: The callback function to run when a row is clicked. + The first parameter is the row index, and the second is the row data provided in a dictionary where the + column names are the keys. + on_row_double_press: The callback function to run when a row is double clicked. + The first parameter is the row index, and the second is the row data provided in a dictionary where the + column names are the keys. + on_cell_press: The callback function to run when a cell is clicked. + The first parameter is the cell index, and the second is the row data provided in a dictionary where the + column names are the keys. + on_cell_double_press: The callback function to run when a cell is double clicked. + The first parameter is the cell index, and the second is the row data provided in a dictionary where the + column names are the keys. + on_column_press: The callback function to run when a column is clicked. + The first parameter is the column name. + on_column_double_press: The callback function to run when a column is double clicked. + The first parameter is the column name. """ - return UITable(table) + props = locals() + del props["table"] + return UITable(table, **props) diff --git a/plugins/ui/src/deephaven/ui/elements/UITable.py b/plugins/ui/src/deephaven/ui/elements/UITable.py index f56482ac6..89837416d 100644 --- a/plugins/ui/src/deephaven/ui/elements/UITable.py +++ b/plugins/ui/src/deephaven/ui/elements/UITable.py @@ -2,7 +2,15 @@ import collections import logging +import sys +from warnings import warn from typing import Callable, Literal, Sequence, Any, cast + +if sys.version_info < (3, 11): + from typing_extensions import TypedDict, NotRequired +else: + from typing import TypedDict, NotRequired + from deephaven.table import Table from deephaven import SortDirection from .Element import Element @@ -50,33 +58,83 @@ def remap_sort_direction(direction: TableSortDirection | str) -> Literal["ASC", raise ValueError(f"Invalid table sort direction: {direction}") -class UITable(Element): +class UITableProps(TypedDict): + can_search: NotRequired[bool] """ - Wrap a Table with some extra props for giving hints to displaying a table + Whether the search bar is accessible or not. Use the system default if no value set. + """ + + on_row_press: NotRequired[RowPressCallback] + """ + Callback function to run when a row is clicked. + The first parameter is the row index, and the second is the row data provided in a dictionary where the + column names are the keys. + """ + + on_row_double_press: NotRequired[RowPressCallback] + """ + The callback function to run when a row is double clicked. + The first parameter is the row index, and the second is the row data provided in a dictionary where the + column names are the keys. + """ + + on_cell_press: NotRequired[CellPressCallback] + """ + The callback function to run when a cell is clicked. + The first parameter is the cell index, and the second is the row data provided in a dictionary where the + column names are the keys. + """ + + on_cell_double_press: NotRequired[CellPressCallback] + """ + The callback function to run when a cell is double clicked. + The first parameter is the cell index, and the second is the row data provided in a dictionary where the + column names are the keys. + """ + + on_column_press: NotRequired[ColumnPressCallback] + """ + The callback function to run when a column is clicked. + The first parameter is the column name. """ - _table: Table + on_column_double_press: NotRequired[ColumnPressCallback] + """ + The callback function to run when a column is double clicked. + The first parameter is the column name. + """ + + table: Table + """ + The table to wrap + """ + + +class UITable(Element): """ - The table that is wrapped with some extra props + Wrap a Table with some extra props for giving hints to displaying a table """ - _props: dict[str, Any] + _props: UITableProps """ - The extra props that are added by each method + The props that are passed to the frontend """ - def __init__(self, table: Table, props: dict[str, Any] = {}): + def __init__( + self, + table: Table, + **props: Any, + ): """ Create a UITable from the passed in table. UITable provides an [immutable fluent interface](https://en.wikipedia.org/wiki/Fluent_interface#Immutability) for adding UI hints to a table. Args: table: The table to wrap + props: UITableProps props to pass to the frontend. """ - self._table = table - # Store the extra props that are added by each method - # This is a shallow copy of the props so that we don't mutate the passed in props dict - self._props = {**props} + # Store all the props that were passed in + self._props = UITableProps(**props, table=table) @property def name(self): @@ -94,7 +152,7 @@ def _with_prop(self, key: str, value: Any) -> "UITable": A new UITable with the passed in prop added to the existing props """ logger.debug("_with_prop(%s, %s)", key, value) - return UITable(self._table, {**self._props, key: value}) + return UITable(**self._props, key=value) def _with_appendable_prop(self, key: str, value: Any) -> "UITable": """ @@ -116,9 +174,9 @@ def _with_appendable_prop(self, key: str, value: Any) -> "UITable": value = value if isinstance(value, list) else [value] - return UITable(self._table, {**self._props, key: existing + value}) + return UITable(**self._props, key=existing + value) - def _with_dict_prop(self, prop_name: str, value: dict) -> "UITable": + def _with_dict_prop(self, key: str, value: dict[str, Any]) -> "UITable": """ Create a new UITable with the passed in prop in a dictionary. This will override any existing prop with the same key within @@ -132,14 +190,13 @@ def _with_dict_prop(self, prop_name: str, value: dict) -> "UITable": Returns: A new UITable with the passed in prop added to the existing props """ - logger.debug("_with_dict_prop(%s, %s)", prop_name, value) - existing = self._props.get(prop_name, {}) - new = {**existing, **value} - return UITable(self._table, {**self._props, prop_name: new}) + logger.debug("_with_dict_prop(%s, %s)", key, value) + existing = self._props.get(key, {}) + return UITable(**self._props, key={**existing, **value}) def render(self, context: RenderContext) -> dict[str, Any]: logger.debug("Returning props %s", self._props) - return dict_to_camel_case({**self._props, "table": self._table}) + return dict_to_camel_case({**self._props}) def aggregations( self, @@ -401,20 +458,7 @@ def hide_columns(self, columns: str | list[str]) -> "UITable": """ raise NotImplementedError() - def on_row_press(self, callback: RowPressCallback) -> "UITable": - """ - Add a callback for when a press on a row is released (e.g. a row is clicked). - - Args: - callback: The callback function to run when a row is clicked. - The first parameter is the row index, and the second is the row data provided in a dictionary where the - column names are the keys. - - Returns: - A new UITable - """ - return self._with_prop("on_row_press", callback) - + @DeprecationWarning def on_row_double_press(self, callback: RowPressCallback) -> "UITable": """ Add a callback for when a row is double clicked. @@ -427,62 +471,9 @@ def on_row_double_press(self, callback: RowPressCallback) -> "UITable": Returns: A new UITable """ + warn("Use on_row_double_press arg on ui.table instead", DeprecationWarning) return self._with_prop("on_row_double_press", callback) - def on_cell_press(self, callback: CellPressCallback) -> "UITable": - """ - Add a callback for when a cell is clicked. - - Args: - callback: The callback function to run when a cell is clicked. - The first parameter is the cell index, and the second is the row data provided in a dictionary where the - column names are the keys. - - Returns: - A new UITable - """ - return self._with_prop("on_cell_press", callback) - - def on_cell_double_press(self, callback: CellPressCallback) -> "UITable": - """ - Add a callback for when a cell is double clicked. - - Args: - callback: The callback function to run when a cell is double clicked. - The first parameter is the cell index, and the second is the row data provided in a dictionary where the - column names are the keys. - - Returns: - A new UITable - """ - return self._with_prop("on_cell_double_press", callback) - - def on_column_press(self, callback: ColumnPressCallback) -> "UITable": - """ - Add a callback for when a column is clicked. - - Args: - callback: The callback function to run when a column is clicked. - The first parameter is the column name. - - Returns: - A new UITable - """ - return self._with_prop("on_column_press", callback) - - def on_column_double_press(self, callback: ColumnPressCallback) -> "UITable": - """ - Add a callback for when a column is double clicked. - - Args: - callback: The callback function to run when a column is double clicked. - The first parameter is the column name. - - Returns: - A new UITable - """ - return self._with_prop("on_column_double_press", callback) - def quick_filter( self, filter: dict[ColumnName, QuickFilterExpression] ) -> "UITable": @@ -541,7 +532,7 @@ def sort( remap_sort_direction(direction) for direction in direction_list_unmapped ] - by_list = by if isinstance(by, Sequence) else [by] + by_list = [by] if isinstance(by, str) else by if direction and len(direction_list) != len(by_list): raise ValueError("by and direction must be the same length") diff --git a/plugins/ui/src/js/src/elements/UITableMouseHandler.ts b/plugins/ui/src/js/src/elements/UITableMouseHandler.ts index a41448d39..c8ba66d20 100644 --- a/plugins/ui/src/js/src/elements/UITableMouseHandler.ts +++ b/plugins/ui/src/js/src/elements/UITableMouseHandler.ts @@ -78,7 +78,7 @@ class UITableMouseHandler extends GridMouseHandler { onRowPress: UITableProps['onRowPress'], onRowDoublePress: UITableProps['onRowDoublePress'] ) { - super(850); + super(890); this.model = model; this.onCellPress = onCellPress; this.onCellDoublePress = onCellDoublePress;