@atlaskit/dynamic-table
Version:
A dynamic table displays rows of data with built-in pagination, sorting, and re-ordering functionality.
146 lines (142 loc) • 4.45 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React from 'react';
import { getPageRows } from '../internal/get-page-rows';
import { validateSortKey } from '../internal/validate-sort-key';
const getSortingCellValue = (cells, head, sortKey) => {
for (let i = 0; i < cells.length; i++) {
var _head$cells$i;
if (head.cells[i] && ((_head$cells$i = head.cells[i]) === null || _head$cells$i === void 0 ? void 0 : _head$cells$i.key) === sortKey) {
var _cells$i;
return (_cells$i = cells[i]) === null || _cells$i === void 0 ? void 0 : _cells$i.key;
}
}
return undefined;
};
// sort all rows based on sort key and order
const getSortedRows = (head, rows, sortKey, sortOrder) => {
if (!sortKey || !head) {
return rows;
}
if (!rows) {
return [];
}
const modifier = sortOrder === 'ASC' ? 1 : -1;
// Re-initialising an I18n Collator on every sort is performance intensive, thus constructed outside
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'accent'
});
// Get copy of rows to avoid sorting prop in place
const sortableRows = Array.from(rows);
// Reorder rows in table based on sorting cell value
// Algorithm will sort numerics or strings, but not both
return sortableRows.sort((a, b) => {
const valA = getSortingCellValue(a.cells, head, sortKey);
const valB = getSortingCellValue(b.cells, head, sortKey);
if (valA === undefined || valB === undefined) {
return modifier;
}
if (typeof valA !== typeof valB) {
// numbers are always grouped higher in the sort
if (typeof valA === 'number') {
return -1;
}
if (typeof valB === 'number') {
return 1;
}
// strings are grouped next
if (typeof valA === 'string') {
return -1;
}
if (typeof valB === 'string') {
return 1;
}
}
if (typeof valA === 'string' && typeof valB === 'string') {
return modifier * collator.compare(valA, valB);
}
if (!valA && valA !== 0 || valA < valB) {
return -modifier;
}
if (!valB && valB !== 0 || valA > valB) {
return modifier;
}
if (valA === valB) {
return 0;
}
return 1;
});
};
// get one page of data in table, sorting all rows previously
export default function withSortedPageRows(WrappedComponent) {
// eslint-disable-next-line @repo/internal/react/no-class-components
class WithSortedPageRows extends React.Component {
constructor(...args) {
super(...args);
_defineProperty(this, "state", {
pageRows: []
});
}
static getDerivedStateFromProps(props, state) {
const {
rows,
head,
sortKey,
sortOrder,
page,
rowsPerPage,
isTotalPagesControlledExternally
} = props;
validateSortKey(sortKey, head);
let sortedRows;
let pageRows;
if (isTotalPagesControlledExternally) {
sortedRows = rows;
pageRows = rows;
} else {
sortedRows = getSortedRows(head, rows, sortKey, sortOrder) || [];
pageRows = getPageRows(sortedRows, page, rowsPerPage);
}
return {
...state,
pageRows
};
}
componentDidMount() {
this.props.onPageRowsUpdate && this.props.onPageRowsUpdate(this.state.pageRows);
}
componentDidUpdate(_prevProps, prevState) {
if (this.props.onPageRowsUpdate && this.state.pageRows !== prevState.pageRows) {
this.props.onPageRowsUpdate(this.state.pageRows);
}
}
render() {
const {
rows,
head,
sortKey,
sortOrder,
rowsPerPage,
page,
forwardedRef,
...restProps
} = this.props;
return /*#__PURE__*/React.createElement(WrappedComponent
//@ts-expect-error TODO Fix legit TypeScript 3.9.6 improved inference error
, _extends({
pageRows: this.state.pageRows,
head: head
}, restProps, {
ref: forwardedRef
}));
}
}
return /*#__PURE__*/React.forwardRef((props, ref) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: to unblock React 18.2.0 -> 18.3.1 version bump in Jira
return /*#__PURE__*/React.createElement(WithSortedPageRows, _extends({}, props, {
forwardedRef: ref
}));
});
}