@spaced-out/ui-design-system
Version:
Sense UI components library
213 lines (196 loc) • 6.27 kB
Flow
// @flow strict
import * as React from 'react';
import get from 'lodash/get';
import xor from 'lodash/xor';
import {useWindowSize} from '../../hooks/useWindowSize';
import {sizeFluid} from '../../styles/variables/_size';
import {classify} from '../../utils/classify';
import type {ClassNameComponent} from '../../utils/makeClassNameComponent';
import {makeClassNameComponent} from '../../utils/makeClassNameComponent';
import {DefaultRow, EmptyRow} from './DefaultRow';
import {DefaultTableHeader} from './DefaultTableHeader';
import type {SortDirection} from './hooks';
import type {GenericObject, TableProps} from './Table';
import css from './Table.module.css';
export const BasicTable: ClassNameComponent<'table'> = makeClassNameComponent(
css.defaultTable,
'table',
);
export const BasicTableBody: ClassNameComponent<'tbody'> =
makeClassNameComponent(css.defaultTableBody, 'tbody');
/**
* A Static Default Table.
*
* Our
*/
export function StaticTable<Data: GenericObject, Extras: GenericObject>(props: {
...TableProps<Data, Extras>,
handleSortClick?: (sortKey: string) => mixed,
sortKey?: string,
sortDirection?: SortDirection,
rowKeys?: string[],
}): React.Node {
const {
classNames,
className,
TableRow,
entries,
extras,
rowKeys,
headers,
showHeader = true,
tableHeaderClassName,
sortable,
// eslint-disable-next-line unused-imports/no-unused-vars
defaultSortKey,
// eslint-disable-next-line unused-imports/no-unused-vars
defaultSortDirection = 'original',
// eslint-disable-next-line unused-imports/no-unused-vars
onSort,
handleSortClick,
sortKey,
sortDirection,
selectedKeys,
disabledKeys = [],
onSelect,
isLoading,
idName = 'id',
emptyText,
disabled,
customLoader,
borderRadius,
stickyHeader,
} = props;
// this is a fallback and honestly probably doesn't need the
// memo'ing
const mappedKeys = React.useMemo(
() => rowKeys ?? entries.map((e) => get(e, idName)),
[entries, idName, rowKeys],
);
const tableRef = React.useRef(null);
const {width} = useWindowSize();
const [tableWidth, setTableWidth] = React.useState();
React.useEffect(() => {
if (tableRef.current) {
setTableWidth(tableRef.current.offsetWidth);
}
}, [width]);
/**
* this function is also used to decide weather to show checkbox in header or not. so it's value is undefined in case selectedKeys is not there.
*/
const handleHeaderCheckboxClick = selectedKeys
? ({checked}: {value: string, checked: boolean}) => {
let selectedRowIds = [];
if (selectedKeys) {
if (checked === true) {
selectedRowIds = entries.map((singleRowObj) =>
get(singleRowObj, idName),
);
}
onSelect?.(selectedRowIds);
}
}
: undefined;
return (
<div
className={classify(css.tableContainer, classNames?.wrapper)}
data-id="table-wrap"
ref={tableRef}
style={{
'--border-radius': borderRadius,
'--table-width': tableWidth ? `${tableWidth}px` : sizeFluid,
}}
>
<BasicTable
data-id="basic-table"
className={classify(
className,
{
[css.fullHeightTable]:
isLoading || (!entries.length && !!emptyText),
},
classNames?.table,
)}
>
{showHeader && (
<DefaultTableHeader
className={classify(tableHeaderClassName, classNames?.tableHeader)}
sortable={sortable}
columns={headers}
handleSortClick={handleSortClick}
sortKey={sortKey}
sortDirection={sortDirection}
disabled={disabled}
handleCheckboxClick={handleHeaderCheckboxClick}
stickyHeader={stickyHeader}
checked={
selectedKeys == null || selectedKeys.length === 0
? 'false'
: selectedKeys.length < entries.length
? 'mixed'
: 'true'
}
/>
)}
<BasicTableBody className={classNames?.tableBody}>
{isLoading || !entries.length ? (
<EmptyRow
isLoading={isLoading}
emptyText={emptyText}
headersLength={
handleHeaderCheckboxClick ? headers.length + 1 : headers.length
}
customLoader={customLoader}
/>
) : (
mappedKeys.map((key) => {
const data = entries.find((e) => get(e, idName) === key);
if (data == null) {
return null;
}
(data: Data);
const selected =
selectedKeys && Array.isArray(selectedKeys)
? selectedKeys.includes(get(data, idName))
: undefined;
const isRowDisabled =
disabledKeys && Array.isArray(disabledKeys)
? disabledKeys.includes(get(data, idName))
: false;
return TableRow ? (
<TableRow
key={key}
data={data}
headers={headers}
// extras and rowKeys are both 'optional'
extras={extras}
sortedKeys={rowKeys ?? mappedKeys}
selected={selected}
disabled={disabled || isRowDisabled}
/>
) : (
<DefaultRow
key={key}
data={data}
extras={extras}
headers={headers}
selected={selected}
onSelect={
selectedKeys != null
? (_v) => onSelect?.(xor(selectedKeys ?? [], [key]))
: undefined
}
disabled={disabled || isRowDisabled}
classNames={{
tableRow: classNames?.tableRow,
checkbox: classNames?.checkbox,
}}
/>
);
})
)}
</BasicTableBody>
</BasicTable>
</div>
);
}