webui/src/components/common/TableHeader.tsx

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>
);
}