diff --git a/plugins/ui/src/deephaven/ui/components/table.py b/plugins/ui/src/deephaven/ui/components/table.py index afced9696..9f9558614 100644 --- a/plugins/ui/src/deephaven/ui/components/table.py +++ b/plugins/ui/src/deephaven/ui/components/table.py @@ -3,6 +3,7 @@ from deephaven.table import Table from ..elements import UITable +from .types import AlignSelf, DimensionValue, JustifySelf, LayoutFlex, Position from ..types import ( CellPressCallback, ColumnGroup, @@ -44,6 +45,41 @@ def table( ) = None, databars: list[DatabarConfig] | None = None, key: str | 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_row_start: str | None = None, + grid_row_end: str | None = None, + grid_column: 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, + start: DimensionValue | None = None, + end: DimensionValue | None = None, + left: DimensionValue | None = None, + right: DimensionValue | None = None, + z_index: int | None = None, ) -> UITable: """ Customization to how a table is displayed, how it behaves, and listen to UI events. @@ -91,7 +127,41 @@ def table( May also be a function that receives the column header data and returns the context menu items or None. databars: Databars are experimental and will be moved to column_formatting in the future. key: A unique identifier used by React to render elements in a list. - + 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 much the element will grow to fit the space available. + flex_shrink: When used in a flex layout, specifies how much the element will shrink to fit the space available. + flex_basis: When used in a flex layout, specifies the initial size of the element. + align_self: Overrides the align_items property of a flex or grid container. + justify_self: Specifies 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: The name of the grid area to place the element in. + grid_row: The name of the grid row to place the element in. + grid_row_start: The name of the grid row to start the element in. + grid_row_end: The name of the grid row to end the element in. + grid_column: The name of the grid column to place the element in. + grid_column_start: The name of the grid column to start the element in. + grid_column_end: The name of the grid column to end the element in. + margin: The margin to apply around the element. + margin_top: The margin to apply above the element. + margin_bottom: The margin to apply below the element. + margin_start: The margin to apply before the element. + margin_end: The margin to apply after the element. + margin_x: The margin to apply to the left and right of the element. + margin_y: The margin to apply to the top and bottom 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 distance from the top of the containing element. + bottom: The distance from the bottom of the containing element. + start: The distance from the start of the containing element. + end: The distance from the end of the containing element. + left: The distance from the left of the containing element. + right: The distance from the right of the containing element. + z_index: The stack order of the element. Returns: The rendered Table. """ diff --git a/plugins/ui/src/js/src/elements/UITable/UITable.tsx b/plugins/ui/src/js/src/elements/UITable/UITable.tsx index ab9347b3f..6ebb6ea9f 100644 --- a/plugins/ui/src/js/src/elements/UITable/UITable.tsx +++ b/plugins/ui/src/js/src/elements/UITable/UITable.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import classNames from 'classnames'; import { DehydratedQuickFilter, IrisGrid, @@ -11,7 +12,9 @@ import { import { colorValueStyle, resolveCssVariablesInRecord, + useStyleProps, useTheme, + viewStyleProps, } from '@deephaven/components'; import { useApi } from '@deephaven/jsapi-bootstrap'; import { TableUtils } from '@deephaven/jsapi-utils'; @@ -65,6 +68,7 @@ export function UITable({ contextMenu, contextHeaderMenu, databars: databarsProp, + ...userStyleProps }: UITableProps): JSX.Element | null { const [error, setError] = useState(null); @@ -76,6 +80,39 @@ export function UITable({ throw error; } + // Margin looks wrong with ui.table, so we want to map margin to padding instead + const { + margin, + marginTop, + marginBottom, + marginStart, + marginEnd, + marginX, + marginY, + ...restStyleProps + } = userStyleProps ?? {}; + const { styleProps } = useStyleProps( + { + padding: margin, + paddingTop: marginTop, + paddingBottom: marginBottom, + paddingStart: marginStart, + paddingEnd: marginEnd, + paddingX: marginX, + paddingY: marginY, + ...restStyleProps, + // Add min and max height if the user set height or width explicitly + // This fixes issues in flex boxes where one table is auto sized and the other explicit + // The explicit table will never reach its size because the auto sized table has width/height 100% + // We don't want to set flex-shrink because it could be the cross-axis value that is explicitly set + minHeight: restStyleProps.minHeight ?? restStyleProps.height, + maxHeight: restStyleProps.maxHeight ?? restStyleProps.height, + minWidth: restStyleProps.minWidth ?? restStyleProps.width, + maxWidth: restStyleProps.maxWidth ?? restStyleProps.width, + }, + viewStyleProps // Needed so spectrum applies styles from view instead of base which doesn't have padding + ); + const dh = useApi(); const theme = useTheme(); const [irisGrid, setIrisGrid] = useState(null); @@ -305,7 +342,11 @@ export function UITable({ useEffect(() => () => model?.close(), [model]); return model ? ( -
+
setIrisGrid(ref)} model={model} diff --git a/plugins/ui/src/js/src/elements/UITable/UITableUtils.tsx b/plugins/ui/src/js/src/elements/UITable/UITableUtils.tsx index 876726a6b..bbaa7f8cc 100644 --- a/plugins/ui/src/js/src/elements/UITable/UITableUtils.tsx +++ b/plugins/ui/src/js/src/elements/UITable/UITableUtils.tsx @@ -1,3 +1,4 @@ +import { type StyleProps } from '@react-types/shared'; import type { dh } from '@deephaven/jsapi-types'; import { ColumnName, DehydratedSort } from '@deephaven/iris-grid'; import { ELEMENT_KEY, ElementNode, isElementNode } from '../utils/ElementUtils'; @@ -37,7 +38,7 @@ export type DatabarConfig = { markers?: { value: number | string; color?: string }[]; }; -export type UITableProps = { +export type UITableProps = StyleProps & { table: dh.WidgetExportedObject; onCellPress?: (data: CellData) => void; onCellDoublePress?: (data: CellData) => void; @@ -61,6 +62,7 @@ export type UITableProps = { contextMenu?: ResolvableUIContextItem | ResolvableUIContextItem[]; contextHeaderMenu?: ResolvableUIContextItem | ResolvableUIContextItem[]; databars?: DatabarConfig[]; + [key: string]: unknown; // Needed because StyleProps is an interface which removes the implicit index signature of the type }; export type UITableNode = Required< diff --git a/plugins/ui/src/js/src/styles.scss b/plugins/ui/src/js/src/styles.scss index 973da5aa9..4fa048b52 100644 --- a/plugins/ui/src/js/src/styles.scss +++ b/plugins/ui/src/js/src/styles.scss @@ -7,8 +7,18 @@ overflow: hidden; } -.ui-object-container { - display: contents; +.ui-table-container { + // If all browsers properly supported 'stretch' for height and width + // we could swap to that, but right now only Chrome properly implements it. + // Stretch is prefixed as -webkit-fill-available for Chrome and Webkit + // and -moz-available for Firefox. + // Firefox and Webkit only seem to apply properly stretch to width, not height. + // The benefit to swapping width/height to stretch is that a user + // specifying margin on a table would basically be treated as padding. + // This would create a better experience so tables with margin don't overflow the panel + // like they do with 100% width/height. + height: 100%; + width: 100%; position: relative; } @@ -54,7 +64,7 @@ &:has(.dh-inner-react-panel > .iris-grid:only-child), &:has( .dh-inner-react-panel - > .ui-object-container:only-child + > .ui-table-container:only-child > .iris-grid:only-child ), &:has(.dh-inner-react-panel > .chart-wrapper:only-child) { diff --git a/tests/app.d/ui_flex.py b/tests/app.d/ui_flex.py index 0789adfe7..4c0292aff 100644 --- a/tests/app.d/ui_flex.py +++ b/tests/app.d/ui_flex.py @@ -83,3 +83,10 @@ def ui_flex_test_component(): ui.button("Test"), _t_flex, ) +flex_22 = ui.panel( + ui.flex(ui.table(_t_flex, margin="20px"), _t_flex, direction="column") +) +flex_23 = ui.panel(ui.flex(ui.table(_t_flex, margin="20px"), _t_flex, direction="row")) +flex_24 = ui.panel( + ui.flex(ui.table(_t_flex, height="200px"), _t_flex, direction="column") +) diff --git a/tests/ui.spec.ts b/tests/ui.spec.ts index af9461b3d..144f7bbb3 100644 --- a/tests/ui.spec.ts +++ b/tests/ui.spec.ts @@ -104,6 +104,9 @@ test.describe('UI flex components', () => { { name: 'flex_19', traces: 1 }, { name: 'flex_20', traces: 1 }, { name: 'flex_21', traces: 1 }, + { name: 'flex_22', traces: 0 }, + { name: 'flex_23', traces: 0 }, + { name: 'flex_24', traces: 0 }, ].forEach(i => { test(i.name, async ({ page }) => { await gotoPage(page, ''); diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-chromium-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-chromium-linux.png new file mode 100644 index 000000000..73ffe63c9 Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-chromium-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-firefox-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-firefox-linux.png new file mode 100644 index 000000000..72226c1ca Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-firefox-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-webkit-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-webkit-linux.png new file mode 100644 index 000000000..24d59bd70 Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-22-1-webkit-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-chromium-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-chromium-linux.png new file mode 100644 index 000000000..3c7a41eac Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-chromium-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-firefox-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-firefox-linux.png new file mode 100644 index 000000000..a18dab590 Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-firefox-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-webkit-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-webkit-linux.png new file mode 100644 index 000000000..952d3e50e Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-23-1-webkit-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-chromium-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-chromium-linux.png new file mode 100644 index 000000000..88a3e3aa0 Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-chromium-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-firefox-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-firefox-linux.png new file mode 100644 index 000000000..b7e8aa542 Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-firefox-linux.png differ diff --git a/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-webkit-linux.png b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-webkit-linux.png new file mode 100644 index 000000000..041923f4d Binary files /dev/null and b/tests/ui.spec.ts-snapshots/UI-flex-components-flex-24-1-webkit-linux.png differ