mod-arch-shared
Version:
Shared UI components and utilities for modular architecture micro-frontend projects
96 lines • 7.58 kB
JavaScript
import * as React from 'react';
import { Pagination, Skeleton, Toolbar, ToolbarContent, ToolbarItem, Tooltip, } from '@patternfly/react-core';
import { Table, Thead, Tr, Th, Caption, Tbody, Td, InnerScrollContainer, } from '@patternfly/react-table';
import { CHECKBOX_FIELD_ID, EXPAND_FIELD_ID, KEBAB_FIELD_ID } from './const';
export const MIN_PAGE_SIZE = 10;
const defaultPerPageOptions = [
{
title: '10',
value: 10,
},
{
title: '20',
value: 20,
},
{
title: '30',
value: 30,
},
];
const TableBase = ({ data, columns, subColumns, hasNestedHeader, rowRenderer, enablePagination, toolbarContent, onClearFilters, bottomToolbarContent, emptyTableView, caption, disableRowRenderSupport, selectAll, footerRow, tbodyProps, perPage = 10, page = 1, perPageOptions = defaultPerPageOptions, onSetPage, onNextClick, onPreviousClick, onPerPageSelect, getColumnSort, itemCount = 0, loading, skeletonRowCount, skeletonRowProps = {}, toggleTemplate, disableItemCount = false, hasStickyColumns, ...props }) => {
const selectAllRef = React.useRef(null);
const showPagination = enablePagination;
const pagination = (variant) => (React.createElement(Pagination, { isCompact: true, ...(!disableItemCount && { itemCount }), perPage: perPage, page: page, onSetPage: onSetPage, onNextClick: onNextClick, onPreviousClick: onPreviousClick, onPerPageSelect: onPerPageSelect, toggleTemplate: toggleTemplate, variant: variant, widgetId: "table-pagination", perPageOptions: perPageOptions, menuAppendTo: "inline", titles: {
paginationAriaLabel: `${variant} pagination`,
} }));
// Use a reference to store the heights of table rows once loaded
const tableRef = React.useRef(null);
const rowHeightsRef = React.useRef();
React.useLayoutEffect(() => {
if (!loading || rowHeightsRef.current == null) {
const heights = [];
const rows = tableRef.current?.querySelectorAll(':scope > tbody > tr');
rows?.forEach((r) => heights.push(r.offsetHeight));
rowHeightsRef.current = heights;
}
}, [loading]);
const renderColumnHeader = (col, i, isSubheader) => {
if (col.field === CHECKBOX_FIELD_ID && selectAll) {
return (React.createElement(React.Fragment, { key: `checkbox-${i}` },
React.createElement(Tooltip, { key: "select-all-checkbox", content: selectAll.tooltip ?? 'Select all page items', triggerRef: selectAllRef }),
React.createElement(Th, { ref: selectAllRef, colSpan: col.colSpan, rowSpan: col.rowSpan, isStickyColumn: col.isStickyColumn, stickyMinWidth: col.stickyMinWidth, select: {
isSelected: selectAll.selected,
onSelect: (e, value) => selectAll.onSelect(value),
isDisabled: selectAll.disabled,
},
// TODO: Log PF bug -- when there are no rows this gets truncated
style: { minWidth: '45px' }, isSubheader: isSubheader, "aria-label": "Select all" })));
}
return col.label ? (React.createElement(Th, { key: col.field + i, colSpan: col.colSpan, rowSpan: col.rowSpan, sort: getColumnSort && col.sortable ? getColumnSort(i) : undefined, width: col.width, info: col.info, isSubheader: isSubheader, isStickyColumn: col.isStickyColumn, stickyMinWidth: col.stickyMinWidth, stickyLeftOffset: col.stickyLeftOffset, hasRightBorder: col.hasRightBorder, modifier: col.modifier, visibility: col.visibility, className: col.className }, col.label)) : (
// Table headers cannot be empty for a11y, table cells can -- https://dequeuniversity.com/rules/axe/4.0/empty-table-header
React.createElement(Td, { key: col.field + i, width: col.width }));
};
const renderRows = () => loading
? // compute the number of items in the upcoming page
new Array(itemCount === 0
? skeletonRowCount || rowHeightsRef.current?.length || MIN_PAGE_SIZE
: Math.max(0, Math.min(perPage, itemCount - perPage * (page - 1))))
.fill(undefined)
.map((_, i) => {
// Set the height to the last known row height or otherwise the same height as the first row.
// When going to a previous page, the number of rows may be greater than the current.
const getRow = () => (React.createElement(Tr, { key: `skeleton-${i}`, ...skeletonRowProps, style: {
...(skeletonRowProps.style || {}),
height: rowHeightsRef.current?.[i] || rowHeightsRef.current?.[0],
} }, columns.map((col) => (React.createElement(Td, { key: col.field,
// assign classes to reserve space
className: col.field === CHECKBOX_FIELD_ID || col.field === EXPAND_FIELD_ID
? 'pf-c-table__toggle'
: col.field === KEBAB_FIELD_ID
? 'pf-c-table__action'
: undefined },
// render placeholders to reserve space
col.field === EXPAND_FIELD_ID || col.field === KEBAB_FIELD_ID ? (React.createElement("div", { style: { width: 46 } })) : col.field === CHECKBOX_FIELD_ID ? (React.createElement("div", { style: { width: 13 } })) : (React.createElement(Skeleton, { width: "50%" })))))));
return disableRowRenderSupport ? (React.createElement(Tbody, { key: `skeleton-tbody-${i}` }, getRow())) : (getRow());
})
: data.map((row, rowIndex) => rowRenderer(row, rowIndex));
const table = (React.createElement(Table, { ...props, ...(hasStickyColumns && { gridBreakPoint: '' }), ref: tableRef },
caption && React.createElement(Caption, null, caption),
React.createElement(Thead, { noWrap: true, hasNestedHeader: hasNestedHeader },
React.createElement(Tr, null, columns.map((col, i) => renderColumnHeader(col, i))),
subColumns?.length ? (React.createElement(Tr, null, subColumns.map((col, i) => renderColumnHeader(col, columns.length + i, true)))) : null),
disableRowRenderSupport ? renderRows() : React.createElement(Tbody, { ...tbodyProps }, renderRows()),
footerRow && footerRow(page)));
return (React.createElement(React.Fragment, null,
(toolbarContent || showPagination) && (React.createElement(Toolbar, { inset: { default: 'insetNone' }, className: "pf-v6-u-w-100", customLabelGroupContent: onClearFilters ? undefined : React.createElement(React.Fragment, null), clearAllFilters: onClearFilters },
React.createElement(ToolbarContent, null,
toolbarContent,
showPagination && (React.createElement(ToolbarItem, { variant: "pagination", align: { default: 'alignEnd' }, className: "pf-v6-u-pr-lg" }, pagination('top')))))),
hasStickyColumns ? React.createElement(InnerScrollContainer, null, table) : table,
!loading && emptyTableView && data.length === 0 && (React.createElement("div", { style: { padding: 'var(--pf-global--spacer--2xl) 0', textAlign: 'center' } }, emptyTableView)),
bottomToolbarContent && (React.createElement(Toolbar, { inset: { default: 'insetNone' }, className: "pf-v6-u-w-100" },
React.createElement(ToolbarContent, { alignItems: "center" }, bottomToolbarContent))),
showPagination && React.createElement(React.Fragment, null, pagination('bottom'))));
};
export default TableBase;
//# sourceMappingURL=TableBase.js.map