diff --git a/packages/bento-design-system/src/Table/Config.ts b/packages/bento-design-system/src/Table/Config.ts index 0a6e0f4b5..d4d83175d 100644 --- a/packages/bento-design-system/src/Table/Config.ts +++ b/packages/bento-design-system/src/Table/Config.ts @@ -2,6 +2,7 @@ import { TooltipPlacement } from "../Field/FieldProps"; import { IconProps } from "../Icons"; import { IllustrationProps } from "../Illustrations"; import { BentoSprinkles } from "../internal"; +import { vars } from "../vars.css"; type CellPaddingConfig = { paddingX: BentoSprinkles["paddingX"]; @@ -16,6 +17,10 @@ export type TableConfig = { hintPlacement: TooltipPlacement; cellTooltipPlacement: TooltipPlacement; evenRowsBackgroundColor: BentoSprinkles["background"]; + // NOTE(gabro): not using BentoSprinkles["background"] because we only want + // "plain" values to use directly in CSS and not conditional objects like + // { default: ..., hover: ... } + selectedRowBackgroundColor: keyof typeof vars.backgroundColor; padding: { header: CellPaddingConfig; defaultCell: CellPaddingConfig; diff --git a/packages/bento-design-system/src/Table/Table.css.ts b/packages/bento-design-system/src/Table/Table.css.ts index 67885f47d..73eb34bed 100644 --- a/packages/bento-design-system/src/Table/Table.css.ts +++ b/packages/bento-design-system/src/Table/Table.css.ts @@ -1,5 +1,6 @@ -import { style } from "@vanilla-extract/css"; +import { createVar, style } from "@vanilla-extract/css"; import { bentoSprinkles } from "../internal"; +import { strictRecipe } from "../util/strictRecipe"; export const table = style({ gridAutoRows: "max-content", @@ -29,11 +30,34 @@ export const stickyColumnHeader = bentoSprinkles({ top: 0, }); -export const cellContainer = bentoSprinkles({ - height: "full", - display: "flex", - flexDirection: "column", - justifyContent: "center", +export const rowContainer = style({ + // NOTE(gabro): this allows us to use the entire row as a parent selector, + // for applying a hover effect on all of its children or clicking on row, + // without intrucing a DOM container that would break the grid layout. + display: "contents", +}); + +export const selectedRowBackgroundColor = createVar(); + +export const cellContainerRecipe = strictRecipe({ + base: bentoSprinkles({ + height: "full", + display: "flex", + flexDirection: "column", + justifyContent: "center", + }), + variants: { + interactiveRow: { + true: { + selectors: { + [`${rowContainer}:hover &`]: { + cursor: "pointer", + background: selectedRowBackgroundColor, + }, + }, + }, + }, + }, }); export const sectionHeaderContainer = style([ diff --git a/packages/bento-design-system/src/Table/Table.tsx b/packages/bento-design-system/src/Table/Table.tsx index 1b6471f2c..371b41917 100644 --- a/packages/bento-design-system/src/Table/Table.tsx +++ b/packages/bento-design-system/src/Table/Table.tsx @@ -26,13 +26,16 @@ import { IconButton, Tooltip, FeedbackProps, + vars, } from ".."; import { - cellContainer, + cellContainerRecipe, columnHeader, lastLeftStickyColumn, + rowContainer, sectionHeader, sectionHeaderContainer, + selectedRowBackgroundColor, sortIconContainer, stickyColumnHeader, table, @@ -42,6 +45,7 @@ import { useLayoutEffect, useMemo, useState, CSSProperties, useEffect } from "re import { IconHelp, IconInfo } from "../Icons"; import { match, __ } from "ts-pattern"; import { useBentoConfig } from "../BentoConfigContext"; +import { assignInlineVars } from "@vanilla-extract/dynamic"; type SortFn>> = ( a: Row>, @@ -80,6 +84,7 @@ type Props>> = { initialSorting?: Array>; stickyHeaders?: boolean; height?: { custom: string | number }; + onRowPress?: (row: Row>) => void; } & SortingProps; /** @@ -113,6 +118,7 @@ export function Table>>({ initialSorting, stickyHeaders, height, + onRowPress, }: Props) { const config = useBentoConfig().table; const customOrderByFn = useMemo( @@ -273,7 +279,11 @@ export function Table>>({ .map(({ gridWidth = "fit-content" }) => gridWidthStyle(gridWidth)) .join(" "); - function renderCells>(cells: Array>, rowIndex: number) { + function renderCells>( + cells: Array>, + rowIndex: number, + interactiveRow: boolean + ) { return cells.map((cell, index) => ( >>({ style={stickyLeftColumnStyle[cell.column.id]} first={index === 0} last={(index + 1) % columns.length === 0} + interactiveRow={interactiveRow} > {cell.render("Cell")} @@ -321,18 +332,45 @@ export function Table>>({ />, ...row.leafRows.map((row, index) => { prepareRow(row); - return renderCells(row.cells, index); + return renderCells(row.cells, index, false); }), ]; } else { prepareRow(row); - return [renderCells(row.cells, index)]; + return ( + + {renderCells(row.cells, index, onRowPress !== undefined)} + + ); } })} ); } +function RowContainer>>({ + row, + children, + onPress, +}: { + row: Row>; + onPress: ((row: Row>) => void) | undefined; + children: Children; +}) { + const config = useBentoConfig().table; + return ( + onPress?.(row)} + > + {children} + + ); +} + function ColumnHeader>({ column, style, @@ -484,6 +522,7 @@ function CellContainer({ lastLeftSticky, first, last, + interactiveRow, ...props }: { children: any; @@ -492,6 +531,7 @@ function CellContainer({ lastLeftSticky: boolean; first: boolean; last: boolean; + interactiveRow: boolean; } & TableCellProps) { const tableConfig = useBentoConfig().table; @@ -499,7 +539,7 @@ function CellContainer({