@spaced-out/ui-design-system
Version:
Sense UI components library
241 lines (223 loc) • 6.47 kB
Flow
// @flow strict
import * as React from 'react';
import type {ColorTypes} from '../../types/typography';
import classify from '../../utils/classify';
import type {ClassNameComponent} from '../../utils/makeClassNameComponent';
import {makeClassNameComponent} from '../../utils/makeClassNameComponent';
import {Checkbox} from '../Checkbox';
import {Icon} from '../Icon';
import {SubTitleExtraSmall, TEXT_COLORS} from '../Text';
import {BasicRow} from './DefaultRow';
import type {SortDirection} from './hooks';
import type {GenericObject} from './Table';
import css from './Table.module.css';
export type GenericHeaderItem<T: GenericObject, U: GenericObject> = {
label: React.Node,
key: $Keys<T>,
className?: string,
filterIcon?: React.Node,
filtered?: boolean,
subtext?: string,
sortable?: boolean,
headerIconClassName?: string,
sticky?: boolean,
render?: React.ComponentType<{
data: T,
extras?: U,
className?: string,
selected?: boolean,
disabled?: boolean,
}>,
};
export type GenericHeaderItems<T, U> = GenericHeaderItem<T, U>[];
export const BasicHeadCell: ClassNameComponent<'th'> = makeClassNameComponent(
css.defaultHeaderCell,
'th',
);
export const BasicTableHead: ClassNameComponent<'thead'> =
makeClassNameComponent(css.defaultTableHead, 'thead');
export type TableHeaderProps<T, U> = {
className?: string,
tableHeaderClassName?: string,
sortable?: boolean,
columns: GenericHeaderItems<T, U>,
handleSortClick?: (sortKey: $Keys<T>) => mixed,
sortKey?: $Keys<T>,
sortDirection?: SortDirection,
checked?: 'true' | 'false' | 'mixed',
handleCheckboxClick?: ({value: string, checked: boolean}) => mixed,
disabled?: boolean,
stickyHeader?: boolean,
};
const SortIcon = ({
sortDirection,
color,
className,
}: {
sortDirection: SortDirection,
className: string,
color: ColorTypes,
}) => {
if (sortDirection === 'original') {
return (
<Icon
color={color}
name="caret-down"
size="small"
type="solid"
className={className}
/>
);
} else if (sortDirection === 'asc') {
return (
<Icon
color={color}
name="arrow-up"
size="small"
type="regular"
className={className}
/>
);
} else if (sortDirection === 'desc') {
return (
<Icon
color={color}
name="arrow-down"
size="small"
type="regular"
className={className}
/>
);
}
};
export function DefaultTableHeader<T: GenericObject, U: GenericObject>(
props: TableHeaderProps<T, U>,
): React.Node {
const {
className,
sortable = false,
columns,
handleSortClick,
sortKey,
sortDirection = 'original',
handleCheckboxClick,
checked,
disabled,
stickyHeader,
} = props;
const tableHeaderCells = () => (
<>
{columns.map((columnData, index) => {
const {
key,
label,
subtext,
filterIcon,
filtered,
className,
sticky,
sortable: columnSortable = false,
} = columnData;
let headerClassName;
const filterable = Boolean(filterIcon);
if ((sortable && columnSortable) || filterable) {
headerClassName = classify(
css.defaultHeaderCellSortable,
{
[css.filtered]: filtered,
[css.stickyHeaderCell]: sticky,
},
css[sortDirection],
className,
);
} else {
headerClassName = classify(className, {
[css.stickyHeaderCell]: sticky,
});
}
const headCellClickHandler = () => {
if (sortable && columnSortable && handleSortClick) {
handleSortClick(key);
}
};
let columnSortDirection = 'original';
if (sortKey === key) {
columnSortDirection = sortDirection;
}
return (
<BasicHeadCell
className={classify(
{
[css.selectedHeader]:
sortKey === key && columnSortDirection !== 'original',
},
headerClassName,
)}
// eslint-disable-next-line react/no-array-index-key
key={index}
scope="col"
onClick={headCellClickHandler}
>
<div className={classify(css.labelContents)}>
<div className={css.labelContainer}>
<SubTitleExtraSmall
color={TEXT_COLORS.secondary}
className={classify({
[css.selectedHeader]:
sortKey === key && columnSortDirection !== 'original',
})}
>
{label}
</SubTitleExtraSmall>
<span className={css.headerSubtext}>{subtext && subtext}</span>
</div>
{(sortable || filterIcon != null) && (
<div className={css.headerIconContainer}>
{columnSortable && (
<SortIcon
color={TEXT_COLORS.secondary}
className={classify(css.sortArrow, {
[css.selectedSortArrow]: sortKey === key,
})}
sortDirection={columnSortDirection}
/>
)}
{filterIcon != null && (
<div className={css.filterIcon}>{filterIcon}</div>
)}
</div>
)}
</div>
</BasicHeadCell>
);
})}
</>
);
return (
<BasicTableHead
className={classify(
css.tableHeaderSortable,
{[css.stickyHeader]: stickyHeader},
className,
)}
>
<BasicRow className={css.defaultHeaderRow}>
{handleCheckboxClick && (
<BasicHeadCell scope="col">
<div className={css.checkbox}>
<Checkbox
value="all"
checked={checked === 'true' ? true : false}
indeterminate={checked === 'mixed'}
onChange={handleCheckboxClick}
disabled={disabled}
ariaLabel="Select all rows"
/>
</div>
</BasicHeadCell>
)}
{tableHeaderCells()}
</BasicRow>
</BasicTableHead>
);
}