Skip to content

Commit

Permalink
lib: Type ListingTable
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollmer committed Dec 12, 2024
1 parent 35090b7 commit 02a14ce
Showing 1 changed file with 88 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
ExpandableRowContent,
Table, Thead, Tbody, Tr, Th, Td,
SortByDirection,
TdProps, ThProps, TrProps, TableProps,
OnSelect,
} from '@patternfly/react-table';
import { EmptyState, EmptyStateBody, EmptyStateFooter, EmptyStateActions } from "@patternfly/react-core/dist/esm/components/EmptyState/index.js";
import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/esm/components/Text/index.js";
Expand Down Expand Up @@ -65,11 +67,56 @@ const _ = cockpit.gettext;
* event, isSelected, rowIndex, rowData, extraData. rowData contains props with an id property of the clicked row.
* - onHeaderSelect: event, isSelected.
*/

interface ListingTableRowColumnProps {
title: React.ReactNode;
sortKey?: string;
props?: TdProps | ThProps;
}

type ListingTableRowColumn = React.ReactNode | ListingTableRowColumnProps;

export interface ListingTableRowProps {
columns: ListingTableRowColumn[];
props?: TrProps;
expandedContent?: React.ReactNode;
selected?: boolean;
initiallyExpanded?: boolean;
hasPadding?: boolean;
}

export interface ListingTableColumnProps {
title: string;
header?: boolean;
sortable?: boolean;
props?: ThProps;
}

export interface ListingTableProps extends Omit<TableProps, 'rows' | 'onSelect'> {
actions?: React.ReactNode[],
afterToggle?: (expanded: boolean) => void,
caption?: string,
className?: string,
columns: (string | ListingTableColumnProps)[],
emptyCaption?: string,
emptyCaptionDetail?: React.ReactNode,
emptyComponent?: React.ReactNode,
isEmptyStateInTable?: boolean,
loading?: string,
onRowClick?: (event: React.KeyboardEvent | React.MouseEvent | undefined, row: ListingTableRowProps) => void,
onSelect?: OnSelect;
onHeaderSelect?: OnSelect,
rows: ListingTableRowProps[],
showHeader?: boolean,
sortBy?: { index: number, direction: SortByDirection },
sortMethod?: (rows: ListingTableRowProps[], dir: SortByDirection, index: number) => ListingTableRowProps[],
}

export const ListingTable = ({
actions = [],
afterToggle,
caption = '',
className,
className = '',
columns: cells = [],
emptyCaption = '',
emptyCaptionDetail,
Expand All @@ -84,11 +131,11 @@ export const ListingTable = ({
sortBy,
sortMethod,
...extraProps
}) => {
} : ListingTableProps) => {
let rows = [...tableRows];
const [expanded, setExpanded] = useState({});
const [newItems, setNewItems] = useState([]);
const [currentRowsKeys, setCurrentRowsKeys] = useState([]);
const [expanded, setExpanded] = useState<Record<string | number, boolean>>({});
const [newItems, setNewItems] = useState<React.Key[]>([]);
const [currentRowsKeys, setCurrentRowsKeys] = useState<React.Key[]>([]);
const [activeSortIndex, setActiveSortIndex] = useState(sortBy?.index ?? 0);
const [activeSortDirection, setActiveSortDirection] = useState(sortBy?.direction ?? SortByDirection.asc);
const rowKeys = rows.map(row => row.props?.key)
Expand All @@ -98,8 +145,8 @@ export const ListingTable = ({

useEffect(() => {
// Don't highlight all when the list gets loaded
const _currentRowsKeys = JSON.parse(currentRowsKeysStr);
const _rowKeys = JSON.parse(rowKeysStr);
const _currentRowsKeys: React.Key[] = JSON.parse(currentRowsKeysStr);
const _rowKeys: React.Key[] = JSON.parse(rowKeysStr);

if (_currentRowsKeys.length !== 0) {
const new_keys = _rowKeys.filter(key => _currentRowsKeys.indexOf(key) === -1);
Expand All @@ -112,10 +159,10 @@ export const ListingTable = ({
setCurrentRowsKeys(crk => [...new Set([...crk, ..._rowKeys])]);
}, [currentRowsKeysStr, rowKeysStr]);

const isSortable = cells.some(col => col.sortable);
const isSortable = cells.some(col => typeof col != "string" && col.sortable);
const isExpandable = rows.some(row => row.expandedContent);

const tableProps = {};
const tableProps: TableProps = {};

/* Basic table properties */
tableProps.className = "ct-table";
Expand Down Expand Up @@ -174,17 +221,30 @@ export const ListingTable = ({
rows = [{ columns: emptyStateCell }];
}

const sortRows = () => {
const sortRows = (): ListingTableRowProps[] => {
function sortkey(col: ListingTableRowColumn): string {
if (typeof col == "string")
return col;
if (col && typeof col == "object") {
const props = col as ListingTableRowColumnProps;
if (props.sortKey)
return props.sortKey;
if (typeof props.title == "string")
return props.title;
}
return "";
}

const sortedRows = rows.sort((a, b) => {
const aitem = a.columns[activeSortIndex];
const bitem = b.columns[activeSortIndex];

return ((typeof aitem == 'string' ? aitem : (aitem.sortKey || aitem.title)).localeCompare(typeof bitem == 'string' ? bitem : (bitem.sortKey || bitem.title)));
return sortkey(aitem).localeCompare(sortkey(bitem));
});
return activeSortDirection === SortByDirection.asc ? sortedRows : sortedRows.reverse();
};

const onSort = (event, index, direction) => {
const onSort = (_event: unknown, index: number, direction: SortByDirection) => {
setActiveSortIndex(index);
setActiveSortDirection(direction);
};
Expand All @@ -199,6 +259,8 @@ export const ListingTable = ({
if (rowProps.key && newItems.indexOf(rowProps.key) >= 0)
rowProps.className = (rowProps.className || "") + " ct-new-item";

cockpit.assert(typeof rowProps.key != "bigint");

const rowKey = rowProps.key || rowIndex;
const isExpanded = expanded[rowKey] === undefined ? !!row.initiallyExpanded : expanded[rowKey];
let columnSpanCnt = 0;
Expand All @@ -208,7 +270,7 @@ export const ListingTable = ({
{isExpandable
? (row.expandedContent
? <Td expand={{
rowIndex: rowKey,
rowIndex,
isExpanded,
onToggle: () => {
if (afterToggle)
Expand All @@ -230,23 +292,27 @@ export const ListingTable = ({
}} />
}
{row.columns.map(cell => {
const { key, ...cellProps } = cell.props || {};
const cell_as_props = (cell as ListingTableRowColumnProps).props || {};
const { key, expand, select, ...cellProps } = cell_as_props;
const headerCell = cells[columnSpanCnt];
const dataLabel = typeof headerCell == 'object' ? headerCell.title : headerCell;
const colKey = dataLabel || columnSpanCnt;

columnSpanCnt += cellProps.colSpan || 1;

if (headerCell?.header)
if (typeof headerCell != "string" && headerCell?.header) {
return (
<Th key={key || `row_${rowKey}_cell_${colKey}`} dataLabel={dataLabel} {...cellProps}>
{typeof cell == 'object' ? cell.title : cell}
<Th key={key || `row_${rowKey}_cell_${colKey}`} dataLabel={dataLabel}
{...cellProps as ThProps}>
{typeof cell == 'object' ? cell_as_props.title : cell}
</Th>
);
}

return (
<Td key={key || `row_${rowKey}_cell_${colKey}`} dataLabel={dataLabel} {...cellProps}>
{typeof cell == 'object' ? cell.title : cell}
<Td key={key || `row_${rowKey}_cell_${colKey}`} dataLabel={dataLabel}
{...cellProps as TdProps}>
{typeof cell == 'object' ? cell_as_props.title : cell}
</Td>
);
})}
Expand All @@ -259,7 +325,7 @@ export const ListingTable = ({
</React.Fragment>
);

return <Tbody key={rowKey} isExpanded={row.expandedContent && isExpanded}>{rowPair}</Tbody>;
return <Tbody key={rowKey} isExpanded={Boolean(row.expandedContent) && isExpanded}>{rowPair}</Tbody>;
});

return (
Expand All @@ -279,9 +345,9 @@ export const ListingTable = ({
isSelected: rows.every(r => r.selected)
}} />}
{cells.map((column, columnIndex) => {
const columnProps = column.props;
const columnProps = typeof column == "string" ? {} : column.props;
const sortParams = (
column.sortable
(typeof column != "string" && column.sortable)
? {
sort: {
sortBy: {
Expand Down

0 comments on commit 02a14ce

Please sign in to comment.