UNPKG

mod-arch-shared

Version:

Shared UI components and utilities for modular architecture micro-frontend projects

96 lines 7.58 kB
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