UNPKG

@atlaskit/dynamic-table

Version:

A dynamic table displays rows of data with built-in pagination, sorting, and re-ordering functionality.

251 lines (248 loc) 8.24 kB
import _extends from "@babel/runtime/helpers/extends"; import React, { forwardRef, lazy, Suspense, useEffect, useRef, useState } from 'react'; import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler'; import noop from '@atlaskit/ds-lib/noop'; import { assertIsSortable } from '../internal/assert-is-sortable'; import { getPageRows } from '../internal/get-page-rows'; import { validateSortKey } from '../internal/validate-sort-key'; import { Caption } from '../styled/caption'; import { EmptyViewContainer } from '../styled/empty-view-container'; import { EmptyViewWithFixedHeight } from '../styled/empty-view-with-fixed-height'; import { PaginationWrapper } from '../styled/pagination-wrapper'; import { Table } from '../styled/table'; import Body from './body'; import { ErrorBoundary } from './error-boundary'; import LoadingContainer from './loading-container'; import LoadingContainerAdvanced from './loading-container-advanced'; import ManagedPagination from './managed-pagination'; import TableHead from './table-head'; function toggleSortOrder(currentSortOrder) { switch (currentSortOrder) { case 'DESC': return 'ASC'; case 'ASC': return 'DESC'; default: return currentSortOrder; } } const DynamicTable = ({ caption, head, highlightedRowIndex, rows, sortKey, sortOrder, loadingLabel, onPageRowsUpdate, testId, totalRows: passedDownTotalRows, label, isLoading = false, isFixedSize = false, rowsPerPage = Infinity, onSetPage = noop, onSort: providedOnSort = noop, page = 1, emptyView, isRankable = false, isRankingDisabled = false, onRankStart = noop, onRankEnd: providedOnRankEnd = noop, loadingSpinnerSize, paginationi18n = { prev: 'Previous Page', next: 'Next Page', label: 'Pagination', pageLabel: 'Page' } }) => { const [isRanking, setIsRanking] = useState(false); const tableBodyRef = useRef(null); const onSort = usePlatformLeafEventHandler({ fn: providedOnSort, action: 'sorted', componentName: 'dynamicTable', packageName: "@atlaskit/dynamic-table", packageVersion: "19.3.3" }); const onRankEnd = usePlatformLeafEventHandler({ fn: providedOnRankEnd, action: 'ranked', componentName: 'dynamicTable', packageName: "@atlaskit/dynamic-table", packageVersion: "19.3.3" }); useEffect(() => { validateSortKey(sortKey, head); assertIsSortable(head); }, [sortKey, head]); const onSortHandler = item => () => { const { key } = item; if (!key) { return; } if (onSort && isRankable && key === sortKey && sortOrder === 'DESC') { onSort({ key: null, sortOrder: null, item }); return; } const sortOrderFormatted = key !== sortKey ? 'ASC' : toggleSortOrder(sortOrder); if (onSort) { onSort({ key, item, sortOrder: sortOrderFormatted }); } }; const onSetPageHandler = (page, event) => { onSetPage(page, event); }; const onRankStartHandler = params => { setIsRanking(true); onRankStart(params); }; const onRankEndHandler = params => { setIsRanking(false); onRankEnd(params); }; const getSpinnerSize = () => { if (loadingSpinnerSize) { return loadingSpinnerSize; } return getPageRows(rows || [], page, rowsPerPage).length > 2 ? 'large' : 'small'; }; const renderEmptyBody = () => { if (isLoading) { return /*#__PURE__*/React.createElement(EmptyViewWithFixedHeight, { testId: testId }); } return emptyView && /*#__PURE__*/React.createElement(EmptyViewContainer, { testId: testId }, emptyView); }; const rowsLength = rows && rows.length; let totalPages; // set a flag to denote the dynamic table might get only one page of data // for paginated data let isTotalPagesControlledExternally = false; if (passedDownTotalRows && Number.isInteger(passedDownTotalRows) && rowsPerPage && rowsLength && rowsLength <= passedDownTotalRows) { /** * If total number of rows / records have been passed down as prop * Then table is being fed paginated data from server or other sources * In this case, we want to respect information passed down by server or external source * Rather than relying on our computation based on number of rows */ totalPages = Math.ceil(passedDownTotalRows / rowsPerPage); isTotalPagesControlledExternally = true; } else { totalPages = rowsLength && rowsPerPage ? Math.ceil(rowsLength / rowsPerPage) : 0; } totalPages = totalPages < 1 ? 1 : totalPages; const getPageNumber = page > totalPages ? totalPages : page; // page! required, because typescript can't yet see defaultProps to know that this won't be undefined const rowsExist = !!rowsLength; const spinnerSize = getSpinnerSize(); const emptyBody = renderEmptyBody(); return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(LoadingContainerAdvanced, { isLoading: isLoading && rowsExist, spinnerSize: spinnerSize, targetRef: () => tableBodyRef.current, testId: testId, loadingLabel: loadingLabel }, /*#__PURE__*/React.createElement(Table, { isFixedSize: isFixedSize, "aria-label": label, hasDataRow: rowsExist, testId: testId, isLoading: isLoading }, !!caption && /*#__PURE__*/React.createElement(Caption, null, caption), head && /*#__PURE__*/React.createElement(TableHead, { head: head, onSort: onSortHandler, sortKey: sortKey, sortOrder: sortOrder, isRanking: isRanking, isRankable: isRankable, isFixedSize: isFixedSize, testId: testId }), rowsExist && /*#__PURE__*/React.createElement(TableBody, { ref: tableBodyRef, highlightedRowIndex: highlightedRowIndex, rows: rows, head: head, sortKey: sortKey, sortOrder: sortOrder, rowsPerPage: rowsPerPage, page: getPageNumber, isFixedSize: isFixedSize || false, onPageRowsUpdate: onPageRowsUpdate, isTotalPagesControlledExternally: isTotalPagesControlledExternally, testId: testId, isRankable: isRankable, isRanking: isRanking, onRankStart: onRankStartHandler, onRankEnd: onRankEndHandler, isRankingDisabled: isRankingDisabled || isLoading || false }))), totalPages <= 1 ? null : /*#__PURE__*/ // only show pagination if there's MORE than 1 page React.createElement(PaginationWrapper, { testId: testId }, /*#__PURE__*/React.createElement(ManagedPagination, { value: getPageNumber, onChange: onSetPageHandler, total: totalPages, i18n: paginationi18n, isDisabled: isLoading, testId: testId })), !rowsExist && emptyBody && /*#__PURE__*/React.createElement(LoadingContainer, { isLoading: isLoading, spinnerSize: "large", testId: testId, loadingLabel: loadingLabel }, emptyBody)); }; const RankableTableBody = /*#__PURE__*/lazy(() => import( /* webpackChunkName: '@atlaskit-internal_dynamic-table' */'./rankable/body')); const TableBody = /*#__PURE__*/forwardRef(function TableBody({ isRankable = false, isRanking, onRankStart, onRankEnd, isRankingDisabled, ...bodyProps }, ref) { const canRank = isRankable && !bodyProps.sortKey; /** * React 16 does not support SSR for lazy components, * so we avoid rendering the `Suspense` on the server. */ const [canRenderRankable, setCanRenderRankable] = useState(false); useEffect(() => { if (canRank) { setCanRenderRankable(true); } }, [canRank]); const nonRankableBody = /*#__PURE__*/React.createElement(Body, _extends({ ref: ref }, bodyProps)); return canRank && canRenderRankable ? /*#__PURE__*/React.createElement(ErrorBoundary, { fallback: nonRankableBody }, /*#__PURE__*/React.createElement(Suspense, { fallback: nonRankableBody }, /*#__PURE__*/React.createElement(RankableTableBody, _extends({ ref: ref }, bodyProps, { isRanking: isRanking, onRankStart: onRankStart, onRankEnd: onRankEnd, isRankingDisabled: isRankingDisabled })))) : nonRankableBody; }); // eslint-disable-next-line @repo/internal/react/require-jsdoc export default DynamicTable;