diff --git a/plugins/ui/DESIGN.md b/plugins/ui/DESIGN.md index 39effc0d5..d0d48ae44 100644 --- a/plugins/ui/DESIGN.md +++ b/plugins/ui/DESIGN.md @@ -892,6 +892,440 @@ def my_dashboard(): d = my_dashboard() ``` +#### ui.table + +`ui.table` is a wrapper for a Deephaven `Table` object that allows you to add UI customizations or callbacks. The basic syntax for creating a `UITable` is: + +```py +import deephaven.ui as ui +ui_table = ui.table(table: Table, **view_args: dict) -> UITable +``` + +It has an [immutable fluent](https://en.wikipedia.org/wiki/Fluent_interface#Immutability) interface, similar to Deephaven `Table`. That means each method below will return a new `UITable` object, rather than modifying the existing one. This allows you to chain multiple customizations together, e.g.: + +```py +from deephaven import ui + +# Create a table with some customizations +ui_table = ( + ui.table(source) + .color_column("X", ["X = Y > 5 ? RED : NO_FORMATTING"]) + .column_group("Group 1", ["Col1", "Col2"], "RED") +) +``` + +You can also set margins and padding by passing in the appropriate arguments to the `ui.table` function: + +```py +ui_table = ui.table(source, padding="size-250") +``` + +`ui.table` will support the below methods. + +##### aggregations + +Set the totals table to display below the main table. + +###### Syntax + +```py +ui_table.aggregations( + operations: dict[ColumnName, list[AggregationOperation]], + operation_order: list[AggregationOperation] = [], + default_operation: AggregationOperation = "Skip", + group_by: list[ColumnName] = [], + show_on_top: bool = False, +) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------- | +| `operations` | `dict[ColumnName, list[AggregationOperation]]` | The operations to apply to the columns of the table. | +| `operation_order` | `list[AggregationOperation]` | The order in which to display the operations. | +| `default_operation` | `AggregationOperation` | The default operation to apply to columns that do not have an operation specified. | +| `group_by` | `list[ColumnName]` | The columns to group by. | +| `show_on_top` | `bool` | Whether to show the totals table above the main table. | + +##### always_fetch_columns + +Set the columns to always fetch from the server. These will not be affected by the users current viewport/horizontal scrolling. Useful if you have a column with key value data that you want to always include in the data sent for row click operations. + +###### Syntax + +```py +ui_table.always_fetch_columns(columns: str | list[str]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | ------------------------------------------------------------------------- | +| `columns` | `str \| list[str]` | The columns to always fetch from the server. May be a single column name. | + +##### back_columns + +Set the columns to show at the back of the table. These will not be moveable in the UI. + +###### Syntax + +```py +ui_table.back_columns(columns: str | list[str]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | -------------------------------------------------------------------------- | +| `columns` | `str \| list[str]` | The columns to show at the back of the table. May be a single column name. | + +##### can_search + +Set the search bar to explicitly be accessible or inaccessible, or use the system default. + +###### Syntax + +```py +ui_table.can_search(mode: SearchMode) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------ | ------------------------------------------------------------------------------------------ | +| `mode` | `SearchMode` | Set the search bar to explicitly be accessible or inaccessible, or use the system default. | + +##### column_group + +Create a group for columns in the table. + +###### Syntax + +```py +ui_table.column_group(name: str, children: list[str], color: str | None) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ---------- | ------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `name` | `str` | The group name. Must be a valid column name and not a duplicate of another column or group. | +| `children` | `list[str]` | The children in the group. May contain column names or other group names. Each item may only be specified as a child once. | +| `color` | `str \| None` | The hex color string or Deephaven color name. | + +##### color_column + +Applies color formatting to a column of the table. + +###### Syntax + +```py +ui_table.color_column( + column: ColumnName, + where: QuickFilterExpression | None = None, + color: Color | None = None, + background_color: Color | None = None, +) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ------------------ | ------------------------------- | ----------------------------------------------------------------------------- | +| `column` | `ColumnName` | The column name | +| `where` | `QuickFilterExpression \| None` | The filter to apply to the expression. Uses quick filter format (e.g. `>10`). | +| `color` | `Color \| None` | The text color. Accepts hex color strings or Deephaven color names. | +| `background_color` | `Color \| None` | The background color. Accepts hex color strings or Deephaven color names. | + + + + +##### color_row + +Applies color formatting to rows of the table conditionally based on the value of a column. + +###### Syntax + +```py +ui_table.color_row( + column: ColumnName, + where: QuickFilterExpression, + color: Color | None = None, + background_color: Color | None = None +) -> UITable +``` + + + +###### Parameters + +| Parameter | Type | Description | +| ------------------ | ----------------------- | ----------------------------------------------------------------------------- | +| `column` | `str` | The column name | +| `where` | `QuickFilterExpression` | The filter to apply to the expression. Uses quick filter format (e.g. `>10`). | +| `color` | `Color \| None` | The text color. Accepts hex color strings or Deephaven color names. | +| `background_color` | `Color \| None` | The background color. Accepts hex color strings or Deephaven color names. | + +##### context_menu + +Add custom items to the context menu. You can provide a list of actions that always appear, or a callback that can process the selection and send back menu items asynchronously. You can also specify whether you want the menu items provided for a cell context menu, a header context menu, or some combination of those. You can also chain multiple sets of menu items by calling `.context_menu` multiple times. + +###### Syntax + +```py +ui_table.context_menu( + items: ContextMenuAction + | list[ContextMenuAction] + | Callable[[CellIndex, RowData], ContextMenuAction | list[ContextMenuAction]], + mode: ContextMenuMode = "CELL", +) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `items` | `ContextMenuAction \| list[ContextMenuAction] \| Callable[[CellIndex, RowData], ContextMenuAction \| list[ContextMenuAction]]` | The items to add to the context menu. May be a single `ContextMenuAction`, a list of `ContextMenuAction` objects, or a callback function that takes the cell index and row data and returns either a single `ContextMenuAction` or a list of `ContextMenuAction` objects. | +| `mode` | `ContextMenuMode` | Which specific context menu(s) to add the menu item(s) to. Can be one or more modes. Using `None` will add menu items in all cases.
- `CELL`: Triggered from a cell.
- `ROW_HEADER`: Triggered from a row header.
- `COLUMN_HEADER`: Triggered from a column header. | + +##### data_bar + +Applies data bar formatting to the specified column. + +###### Syntax + +```py +ui_table.data_bar(self, + col: str, + value_col: str = None, + min: float | str = None, + max: float | str = None, + axis: DataBarAxis | None = None, + positive_color: Color | list[Color] = None, + negative_color: Color | list[Color] = None, + value_placement: DataBarValuePlacement | None = None, + direction: DataBarDirection | None = None, + opacity: float = None, + marker_col: str = None, + marker_color: Color = None +) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ----------------- | ------------------------------- | -------------------------------------------------------------- | +| `col` | `str` | Column to generate data bars in | +| `value_col` | `str` | Column containing the values to generate data bars from | +| `min` | `float \| str` | Minimum value for data bar scaling or column to get value from | +| `max` | `float \| str` | Maximum value for data bar scaling or column to get value from | +| `axis` | `DataBarAxis \| None` | Orientation of data bar relative to cell | +| `positive_color` | `Color \| list[Color]` | Color for positive bars. Use list of colors to form a gradient | +| `negative_color` | `Color \| list[Color]` | Color for negative bars. Use list of colors to form a gradient | +| `value_placement` | `DataBarValuePlacement \| None` | Orientation of values relative to data bar | +| `direction` | `DataBarDirection \| None` | Orientation of data bar relative to horizontal axis | +| `opacity` | `float` | Opacity of data bars. Accepts values from 0 to 1 | +| `marker_col` | `str` | Column containing the values to generate markers from | +| `marker_color` | `'Color'` | Color for markers | + +##### format + +Specify the formatting to display a column in. + +###### Syntax + +```py +ui_table.format(column: ColumnName,format: str) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ----- | ------------------------------------------------------------------------ | +| `column` | `str` | The column name | +| `format` | `str` | The format to display the column in. Valid format depends on column type | + + + +##### freeze_columns + +Set the columns to freeze to the front of the table. These will always be visible and not affected by horizontal scrolling. + +###### Syntax + +```py +ui_table.freeze_columns(columns: str | list[str]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | ----------------------------------------------------------------------------- | +| `columns` | `str \| list[str]` | The columns to freeze to the front of the table. May be a single column name. | + +##### front_columns + +Set the columns to show at the front of the table. These will not be moveable in the UI. + +###### Syntax + +```py +ui_table.front_columns(columns: str | list[str]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | --------------------------------------------------------------------------- | +| `columns` | `str \| list[str]` | The columns to show at the front of the table. May be a single column name. | + +##### hide_columns + +Set the columns to hide by default in the table. The user can still resize the columns to view them. + +###### Syntax + +```py +ui_table.hide_columns(columns: str | list[str]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ------------------ | ---------------------------------------------------------------- | +| `columns` | `str \| list[str]` | The columns to hide from the table. May be a single column name. | + +##### on_row_press + +Add a callback for when a press on a row is released (e.g. a row is clicked). + +###### Syntax + +```py +ui_table.on_row_press(callback: Callable[[RowIndex, RowData], None]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ---------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `callback` | `Callable[[RowIndex, RowData], None]` | 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 + +Add a callback for when a row is double clicked. + +###### Syntax + +```py +ui_table.on_row_double_press(callback: Callable[[RowIndex, RowData], None]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ---------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `callback` | `Callable[[RowIndex, RowData], None]` | 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. | + +##### quick_filter + +Add a quick filter for the UI to apply to the table. + +###### Syntax + +```py +ui_table.quick_filter(filter: dict[ColumnName, QuickFilterExpression]) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | ----------------------------------------- | --------------------------------------- | +| `filter` | `dict[ColumnName, QuickFilterExpression]` | The quick filter to apply to the table. | + +##### selection_mode + +Set the selection mode for the table. + +###### Syntax + +```py +ui_table.selection_mode(mode: SelectionMode) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| --------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `mode` | `SelectionMode` | The selection mode to use. Must be one of `"ROW"`, `"COLUMN"`, or `"CELL"`:
  • `"ROW"` selects the entire row of the cell you click on.
  • `"COLUMN"` selects the entire column of the cell you click on.
  • `"CELL"` selects only the cells you click on.
  • | + +##### sort + +Provide the default sort that will be used by the UI. + +###### Syntax + +```py +ui_table.sort( + order_by: str | Sequence[str], + order: SortDirection | Sequence[SortDirection] | None = None +) -> UITable +``` + +###### Parameters + +| Parameter | Type | Description | +| ----------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `by` | `str \| Sequence[str]` | The column(s) to sort by. May be a single column name, or a list of column names. | +| `direction` | `SortDirection \| Sequence[SortDirection] \| None` | The sort direction(s) to use. If provided, that must match up with the columns provided. Defaults to "ASC". | + +#### Deprecations + +The functionality provided my `ui.table` replaces some of the existing functions on `Table`. Below are the functions that are planned for deprecation/deletion of the `Table` interface, and their replacements with the new `ui.table` interface. + +| Table Function | ui.table Replacement | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `format_columns`
    `format_column_where`
    `format_row_where` | [color_column](#color_column)
    [color_row](#color_row)
    [format](#format) | +| `layout_hints` | [back_columns](#back_columns)
    [front_columns](#front_columns)
    [column_group](#column_groups)
    [freeze_columns](#freeze_columns)
    [hide_columns](#hide_columns)
    [search_display](#search_display) | +| `dropColumnFormats` | No replacement | +| `setTotalsTable` | [aggregations](#aggregations) | + +#### Custom Types + +Below are some of the custom types that are used in the above API definitions: + +```py +AggregationOperation = Literal["COUNT", "COUNT_DISTINCT", "DISTINCT", "MIN", "MAX", "SUM", "ABS_SUM", "VAR", "AVG", "STD", "FIRST", "LAST", "UNIQUE", "SKIP"] +CellIndex = [RowIndex, ColumnIndex] +Color = DeephavenColor | HexColor +# A ColumnIndex of None indicates a header row +ColumnIndex = int | None +ColumnName = str +ContextMenuAction = dict[str, Any] +ContextMenuModeOption = Literal["CELL", "ROW_HEADER", "COLUMN_HEADER"] +ContextMenuMode = ContextMenuModeOption | list[ContextMenuModeOption] | None +DataBarAxis = Literal["PROPORTIONAL", "MIDDLE", "DIRECTIONAL"] +DataBarDirection = Literal["LTR", "RTL"] +DataBarValuePlacement = Literal["BESIDE", "OVERLAP", "HIDE"] +# TODO: Fill in the list of Deephaven Colors we allow +DeephavenColor = Literal[...] +HexColor = str +QuickFilterExpression = str +RowData = dict[str, Any] +# A RowIndex of None indicates a header column +RowIndex = int | None +SearchMode = Literal["SHOW", "HIDE", "DEFAULT"] +SelectionMode = Literal["CELL", "ROW", "COLUMN"] +SortDirection = Literal["ASC", "DESC"] +``` + #### Context By default, the context of a `@ui.component` will be created per client session (same as [Parameterized Query's "parallel universe" today](https://github.com/deephaven-ent/iris/blob/868b868fc9e180ee948137b10b6addbac043605e/ParameterizedQuery/src/main/java/io/deephaven/query/parameterized/impl/ParameterizedQueryServerImpl.java#L140)). However, it would be interesting if it were possible to share a context among all sessions for the current user, and/or share a context with other users even; e.g. if one user selects and applies a filter, it updates immediately for all other users with that dashboard open. So three cases: