162 lines
5.4 KiB
TypeScript
162 lines
5.4 KiB
TypeScript
import React from "react";
|
|
import { AutoTableRow } from "./auto-table-row/AutoTableRow";
|
|
|
|
export interface IColumnSort {
|
|
index: number;
|
|
isReversed: boolean;
|
|
}
|
|
|
|
export interface IAutoTableHeader {
|
|
display: string;
|
|
onClick?: () => any;
|
|
}
|
|
|
|
const instanceOfAutoTableHeader = (object: any): object is IAutoTableHeader => {
|
|
if (typeof object === "object" && object !== null) {
|
|
return "display" in object;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
export interface IAutoTableProps<T> {
|
|
/**
|
|
* The headers of the table
|
|
*/
|
|
headers: Array<IAutoTableHeader | string | number>;
|
|
|
|
/**
|
|
* Array of functions that describe how each cell of a row ought to be displayed.
|
|
*/
|
|
cells: Array<(element: T) => any>;
|
|
|
|
/**
|
|
* The array of elements to display (each element being its own row).
|
|
*/
|
|
elements: T[];
|
|
|
|
/**
|
|
* Optional callback method that is triggered when a appUser clicks on a row.
|
|
*/
|
|
onRowClick?: (elementClicked: T) => any;
|
|
|
|
/**
|
|
* Optionally show a desired column as being sorted.
|
|
* (This DOES NOT sort. Merely displays the header as such.)
|
|
*/
|
|
sortedColumn?: IColumnSort;
|
|
|
|
className?: string;
|
|
|
|
/**
|
|
* So you want to show something else instead of this row.. what is it and which 0 index row to replace.
|
|
*/
|
|
overrideRow?: (element: T) => JSX.Element;
|
|
overrideRowFn?: (element: T) => boolean;
|
|
}
|
|
|
|
export class AutoTable<T> extends React.Component<IAutoTableProps<T>> {
|
|
getHeaderText(header: IAutoTableHeader | string | number): string | number {
|
|
if (instanceOfAutoTableHeader(header)) {
|
|
return header.display;
|
|
} else {
|
|
return header;
|
|
}
|
|
}
|
|
|
|
getHeaderOnClick(
|
|
header: IAutoTableHeader | string | number
|
|
): (() => any) | undefined {
|
|
if (instanceOfAutoTableHeader(header)) {
|
|
return header.onClick;
|
|
}
|
|
}
|
|
|
|
isHeaderClickable(header: IAutoTableHeader | string | number): boolean {
|
|
return instanceOfAutoTableHeader(header);
|
|
}
|
|
|
|
shouldDisplayAscendingSort(index: number): boolean {
|
|
return (
|
|
!!this.props.sortedColumn &&
|
|
this.props.sortedColumn.index === index &&
|
|
!this.props.sortedColumn.isReversed
|
|
);
|
|
}
|
|
|
|
shouldDisplayDescendingSort(index: number): boolean {
|
|
return (
|
|
!!this.props.sortedColumn &&
|
|
this.props.sortedColumn.index === index &&
|
|
this.props.sortedColumn.isReversed
|
|
);
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<table
|
|
className={
|
|
"table table-striped tablecenterCSS tablecenterheadCSS " +
|
|
(this.props.onRowClick ? "table-hover " : "") +
|
|
this.props.className
|
|
}
|
|
data-test="auto-table"
|
|
>
|
|
<thead>
|
|
<tr>
|
|
{this.props.headers.map((header, index) => (
|
|
<th
|
|
key={"auto_table_header_" + index}
|
|
onClick={this.getHeaderOnClick(header)}
|
|
className={
|
|
"auto-table-row " +
|
|
(this.isHeaderClickable(header)
|
|
? "auto-table-hover"
|
|
: "")
|
|
}
|
|
data-test="auto-table-header"
|
|
>
|
|
{this.shouldDisplayAscendingSort(index) ? (
|
|
<i
|
|
className="fa fa-long-arrow-down"
|
|
aria-hidden="true"
|
|
data-test={"asc-sort-icon-" + index}
|
|
>
|
|
|
|
</i>
|
|
) : null}
|
|
{this.shouldDisplayDescendingSort(index) ? (
|
|
<i
|
|
className="fa fa-long-arrow-up"
|
|
aria-hidden="true"
|
|
data-test={"desc-sort-icon-" + index}
|
|
>
|
|
|
|
</i>
|
|
) : null}
|
|
{this.getHeaderText(header)}
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{this.props.elements.map((el, index) =>
|
|
this.props.overrideRow !== undefined &&
|
|
this.props.overrideRowFn !== undefined &&
|
|
this.props.overrideRowFn(el) ? (
|
|
this.props.overrideRow(el)
|
|
) : (
|
|
<AutoTableRow
|
|
key={index}
|
|
element={el}
|
|
cells={this.props.cells}
|
|
onRowClick={this.props.onRowClick}
|
|
data-test={"auto-table-row_" + index}
|
|
/>
|
|
)
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
);
|
|
}
|
|
}
|