Skip to content

Commit

Permalink
enable checkbox column roe selection in Table (#1329)
Browse files Browse the repository at this point in the history
* enable checkbox column roe selection in Table

* fix type issue
  • Loading branch information
heswell authored May 9, 2024
1 parent d2ff832 commit 129f3cf
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 21 deletions.
7 changes: 7 additions & 0 deletions vuu-ui/packages/vuu-table/src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ export interface TableProps
* TODO this should just live in CSS
*/
selectionBookendWidth?: number;
/**
* Selection behaviour for Table:
* `none` selection disabled
* `single` no more than one row may be selected
* `extended` (default) multiple rows can be selected
* `checkbox` same behaviour as extended, with checkbox column for selection
*/
selectionModel?: TableSelectionModel;
/**
* if false, table rendered without headers. Useful when table is being included in a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useCallback } from "react";
import { memo, useCallback } from "react";
import { TableCellRendererProps } from "@finos/vuu-table-types";
import { CheckboxIcon, WarnCommit } from "@finos/vuu-ui-controls";
import { Checkbox } from "@salt-ds/core";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.vuuTableCell {
.vuuCheckboxRowSelectorIcon {
margin-top: calc(var(--row-height) / 2 - 6px );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TableCellRendererProps } from "@finos/vuu-table-types";
import { isRowSelected, registerComponent } from "@finos/vuu-utils";
import { Checkbox } from "@salt-ds/core";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";

import checkboxRowSelectorCss from "./CheckboxRowSelectorCell.css";

export const CheckboxRowSelectorCell: React.FC<TableCellRendererProps> = ({
row,
}) => {
const targetWindow = useWindow();
useComponentCssInjection({
testId: "vuu-checkbox-row-selector-cell",
css: checkboxRowSelectorCss,
window: targetWindow,
});

const isChecked = isRowSelected(row);

return <Checkbox checked={isChecked} className="vuuCheckboxRowSelector" />;
};
CheckboxRowSelectorCell.displayName = "CheckboxCell";

registerComponent(
"checkbox-row-selector-cell",
CheckboxRowSelectorCell,
"cell-renderer",
{
serverDataType: "boolean",
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./CheckboxRowSelectorCell";
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-table/src/cell-renderers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./checkbox-cell";
export * from "./checkbox-row-selector";
export * from "./input-cell";
export * from "./toggle-cell";
10 changes: 9 additions & 1 deletion vuu-ui/packages/vuu-table/src/useSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getRowElementAtIndex,
isRowSelected,
metadataKeys,
queryClosest,
selectItem,
} from "@finos/vuu-utils";
import { Selection, SelectionChangeHandler } from "@finos/vuu-data-types";
Expand Down Expand Up @@ -51,13 +52,20 @@ export const useSelection = ({
);

const handleRowClick = useCallback<TableRowClickHandlerInternal>(
(evt, row, rangeSelect, keepExistingSelection) => {
(e, row, rangeSelect, keepExistingSelection) => {
const { [IDX]: idx } = row;
const { current: active } = lastActiveRef;
const { current: selected } = selectedRef;

const selectOperation = isRowSelected(row) ? deselectItem : selectItem;

if (selectionModel === "checkbox") {
const cell = queryClosest(e.target, ".vuuTableCell");
if (!cell?.querySelector(".vuuCheckboxRowSelector")) {
return;
}
}

const newSelected = selectOperation(
selectionModel,
selected,
Expand Down
2 changes: 1 addition & 1 deletion vuu-ui/packages/vuu-table/src/useTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export const useTable = ({
headings,
tableAttributes,
tableConfig,
} = useTableModel(config, dataSource);
} = useTableModel(config, dataSource, selectionModel);

useLayoutEffectSkipFirst(() => {
dispatchTableModelAction({
Expand Down
55 changes: 46 additions & 9 deletions vuu-ui/packages/vuu-table/src/useTableModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TableAttributes,
TableConfig,
TableHeadings,
TableSelectionModel,
} from "@finos/vuu-table-types";
import {
applyFilterToColumns,
Expand Down Expand Up @@ -60,6 +61,20 @@ const getDataType = (
}
};

const checkboxColumnDescriptor: ColumnDescriptor = {
label: "",
name: "",
width: 25,
sortable: false,
isSystemColumn: true,
type: {
name: "checkbox",
renderer: {
name: "checkbox-row-selector-cell",
},
},
};

/**
* TableModel represents state used internally to manage Table. It is
* derived initially from the TableConfig provided by user, along with the
Expand Down Expand Up @@ -219,12 +234,17 @@ const columnReducer: GridModelReducer = (state, action) => {

export const useTableModel = (
tableConfigProp: TableConfig,
dataSource: DataSource
dataSource: DataSource,
selectionModel: TableSelectionModel
) => {
const [state, dispatchTableModelAction] = useReducer<
GridModelReducer,
InitialConfig
>(columnReducer, { tableConfig: tableConfigProp, dataSource }, init);
>(
columnReducer,
{ tableConfig: tableConfigProp, dataSource, selectionModel },
init
);

const { columns, headings, tableConfig, ...tableAttributes } = state;

Expand All @@ -239,24 +259,41 @@ export const useTableModel = (

type InitialConfig = {
dataSource: DataSource;
// TODO are we at risk of losing selectionModel on updates ?
selectionModel?: TableSelectionModel;
tableConfig: TableConfig;
};

function init({ dataSource, tableConfig }: InitialConfig): InternalTableModel {
function init({
dataSource,
selectionModel,
tableConfig,
}: InitialConfig): InternalTableModel {
const { columns, ...tableAttributes } = tableConfig;
const { config: dataSourceConfig, tableSchema } = dataSource;
const toRuntimeColumnDescriptor = columnDescriptorToRuntimeColumDescriptor(
tableAttributes,
tableSchema
);
const runtimeColumns = columns
.filter(subscribedOnly(dataSourceConfig?.columns))
.map(
columnDescriptorToRuntimeColumDescriptor(tableAttributes, tableSchema)
);
.map(toRuntimeColumnDescriptor);

const maybePinnedColumns = runtimeColumns.some(isPinned)
const columnsInRenderOrder = runtimeColumns.some(isPinned)
? sortPinnedColumns(runtimeColumns)
: runtimeColumns;

if (selectionModel === "checkbox") {
columnsInRenderOrder.splice(
0,
0,
toRuntimeColumnDescriptor(checkboxColumnDescriptor, -1)
);
}

let state: InternalTableModel = {
columns: maybePinnedColumns,
headings: getTableHeadings(maybePinnedColumns),
columns: columnsInRenderOrder,
headings: getTableHeadings(columnsInRenderOrder),
tableConfig,
...tableAttributes,
};
Expand Down
13 changes: 4 additions & 9 deletions vuu-ui/packages/vuu-utils/src/row-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
import type { DataSourceRow, DataSourceRowObject } from "@finos/vuu-data-types";
import type { MutableRefObject } from "react";
import { ColumnMap, metadataKeys } from "./column-utils";
import { isRowSelected } from "./selection-utils";

const { IS_LEAF, KEY, IDX, SELECTED } = metadataKeys;
const { IS_LEAF, KEY, IDX } = metadataKeys;

export type RowOffsetFunc = (
row: DataSourceRow,
Expand Down Expand Up @@ -95,19 +96,13 @@ export const asDataSourceRowObject = (
row: DataSourceRow,
columnMap: ColumnMap
): DataSourceRowObject => {
console.log({ columnMap });
const {
[IS_LEAF]: isLeaf,
[KEY]: key,
[IDX]: index,
[SELECTED]: selected,
} = row;
const { [IS_LEAF]: isLeaf, [KEY]: key, [IDX]: index } = row;

const rowObject: DataSourceRowObject = {
key,
index,
isGroupRow: !isLeaf,
isSelected: selected > 0,
isSelected: isRowSelected(row),
data: {},
};

Expand Down
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-utils/src/selection-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const RowSelected = {

export const isRowSelected = (row: DataSourceRow): boolean =>
(row[SELECTED] & RowSelected.True) === RowSelected.True;

export const isRowSelectedLast = (row?: DataSourceRow): boolean =>
row !== undefined && (row[SELECTED] & RowSelected.Last) === RowSelected.Last;

Expand Down
45 changes: 45 additions & 0 deletions vuu-ui/showcase/src/examples/Table/TableSelection.examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { getSchema, SimulTableName, vuuModule } from "@finos/vuu-data-test";
import { Table, TableProps } from "@finos/vuu-table";
import { useCallback, useMemo } from "react";

import "./Table.examples.css";

let displaySequence = 1;

export const CheckboxSelection = () => {
const tableProps = useMemo<
Pick<TableProps, "config" | "dataSource" | "selectionModel">
>(() => {
const tableName: SimulTableName = "instruments";
return {
config: {
columns: getSchema(tableName).columns,
rowSeparators: true,
zebraStripes: true,
},
dataSource:
vuuModule<SimulTableName>("SIMUL").createDataSource(tableName),
selectionModel: "checkbox",
};
}, []);

const onSelect = useCallback((row) => {
console.log("onSelect", { row });
}, []);
const onSelectionChange = useCallback((selected) => {
console.log("onSelectionChange", { selected });
}, []);

return (
<Table
{...tableProps}
height={645}
navigationStyle="row"
renderBufferSize={5}
onSelect={onSelect}
onSelectionChange={onSelectionChange}
width={723}
/>
);
};
CheckboxSelection.displaySequence = displaySequence++;
1 change: 1 addition & 0 deletions vuu-ui/showcase/src/examples/Table/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * as TableList from "./TableList.examples";
export * as Table from "./Table.examples";
export * as TableSelection from "./TableSelection.examples";
export * as BASKET from "./BASKET.examples";
export * as SIMUL from "./SIMUL.examples";
export * as TEST from "./TEST.examples";
Expand Down

0 comments on commit 129f3cf

Please sign in to comment.