116 lines
3.0 KiB
TypeScript
116 lines
3.0 KiB
TypeScript
import React, { ChangeEvent, useCallback } from "react";
|
|
import { faSortAsc, faSortDesc } from "@fortawesome/free-solid-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import Column from "./columns";
|
|
import Input, { InputType } from "./Input";
|
|
|
|
export interface TableHeaderProps<T> {
|
|
sortColumn?: Column<T>;
|
|
columns: Column<T>[];
|
|
showDelete?: boolean;
|
|
showEdit?: boolean;
|
|
showAudit?: boolean;
|
|
showSecondaryAudit?: boolean;
|
|
onSort?: (sortColumn: Column<T>) => void;
|
|
onSearch?: (name: string, value: string) => void;
|
|
}
|
|
|
|
export default function TableHeader<T>({
|
|
sortColumn,
|
|
columns,
|
|
showEdit,
|
|
showDelete,
|
|
showAudit,
|
|
showSecondaryAudit,
|
|
onSort,
|
|
onSearch,
|
|
}: TableHeaderProps<T>): JSX.Element {
|
|
const columnsMatch = useCallback((left?: Column<T>, right?: Column<T>) => {
|
|
return left?.key === right?.key;
|
|
}, []);
|
|
|
|
const raiseSort = useCallback(
|
|
(column: Column<T>) => {
|
|
let sc = sortColumn;
|
|
|
|
if (sc) {
|
|
if (columnsMatch(column, sc)) {
|
|
sc.order = sc.order === "asc" ? "desc" : "asc";
|
|
} else {
|
|
sc = column;
|
|
sc.order = "asc";
|
|
}
|
|
|
|
if (onSort) onSort(sc);
|
|
}
|
|
},
|
|
[sortColumn, onSort, columnsMatch],
|
|
);
|
|
|
|
const renderSortIcon = useCallback(
|
|
(column: Column<T>) => {
|
|
if (!sortColumn) return null;
|
|
if (!columnsMatch(column, sortColumn)) return null;
|
|
return sortColumn.order === "asc" ? (
|
|
<FontAwesomeIcon icon={faSortAsc} />
|
|
) : (
|
|
<FontAwesomeIcon icon={faSortDesc} />
|
|
);
|
|
},
|
|
[sortColumn, columnsMatch],
|
|
);
|
|
|
|
const changeSearch = useCallback(
|
|
(e: ChangeEvent<HTMLInputElement>) => {
|
|
if (onSearch) onSearch(e.target.name, e.target.value);
|
|
},
|
|
[onSearch],
|
|
);
|
|
|
|
const searchRow = onSearch ? (
|
|
<tr>
|
|
{columns.map((column) => (
|
|
<th key={column.path || column.key}>
|
|
{(column.searchable === undefined || column.searchable === true) && (
|
|
<Input
|
|
name={column.path || column.key}
|
|
label={""}
|
|
error={""}
|
|
type={InputType.text}
|
|
onChange={changeSearch}
|
|
/>
|
|
)}
|
|
</th>
|
|
))}
|
|
{showEdit && <th></th>}
|
|
{showDelete && <th></th>}
|
|
{showAudit && <th></th>}
|
|
{showAudit && showSecondaryAudit && <th></th>}
|
|
</tr>
|
|
) : (
|
|
<></>
|
|
);
|
|
|
|
return (
|
|
<thead>
|
|
<tr>
|
|
{columns.map((column) => (
|
|
<th
|
|
className="text-nowrap"
|
|
key={column.path || column.key}
|
|
scope="col"
|
|
onClick={() => raiseSort(column)}
|
|
>
|
|
{column.label} {renderSortIcon(column)}
|
|
</th>
|
|
))}
|
|
{showEdit && <th scope="col"></th>}
|
|
{showDelete && <th scope="col"></th>}
|
|
{showAudit && <th scope="col"></th>}
|
|
{showAudit && showSecondaryAudit && <th scope="col"></th>}
|
|
</tr>
|
|
{searchRow}
|
|
</thead>
|
|
);
|
|
}
|