This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { ArrowPathIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon } from '@heroicons/react/24/solid';
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronUpIcon,
|
||||
} from '@heroicons/react/24/solid';
|
||||
import { ArrowsUpDownIcon } from '@heroicons/react/24/outline';
|
||||
import { Button } from './Button';
|
||||
import { Dropdown } from './Dropdown';
|
||||
@@ -46,7 +52,7 @@ export function Table<T>({
|
||||
className = '',
|
||||
sorting = null,
|
||||
onSortChange,
|
||||
pagination
|
||||
pagination,
|
||||
}: Readonly<TableProps<T>>) {
|
||||
const canGoPrev = pagination != null && pagination.page > 1;
|
||||
const canGoNext = pagination != null && pagination.page < pagination.totalPages;
|
||||
@@ -58,15 +64,19 @@ export function Table<T>({
|
||||
<thead className="table-head">
|
||||
<tr>
|
||||
{headers.map((header) => {
|
||||
const canSort = header.sortable === true
|
||||
&& typeof onSortChange === 'function'
|
||||
&& typeof header.sortField === 'string'
|
||||
&& header.sortField.length > 0;
|
||||
const canSort =
|
||||
header.sortable === true &&
|
||||
typeof onSortChange === 'function' &&
|
||||
typeof header.sortField === 'string' &&
|
||||
header.sortField.length > 0;
|
||||
const isActiveSort = canSort && sorting?.field === header.sortField;
|
||||
const sortDirection = isActiveSort ? sorting?.direction : null;
|
||||
|
||||
return (
|
||||
<th key={header.id} className={`table-head-cell ${header.headerClassName ?? ''}`.trim()}>
|
||||
<th
|
||||
key={header.id}
|
||||
className={`table-head-cell ${header.headerClassName ?? ''}`.trim()}
|
||||
>
|
||||
{canSort ? (
|
||||
<button
|
||||
type="button"
|
||||
@@ -75,16 +85,16 @@ export function Table<T>({
|
||||
aria-label={`Sort by ${header.label}`}
|
||||
>
|
||||
<span>{header.label}</span>
|
||||
<span className="table-sort-icon" aria-hidden="true" data-sort-state={sortDirection ?? 'none'}>
|
||||
{sortDirection === 'asc' ? (
|
||||
<ChevronUpIcon className="h-4 w-4" />
|
||||
) : null}
|
||||
<span
|
||||
className="table-sort-icon"
|
||||
aria-hidden="true"
|
||||
data-sort-state={sortDirection ?? 'none'}
|
||||
>
|
||||
{sortDirection === 'asc' ? <ChevronUpIcon className="h-4 w-4" /> : null}
|
||||
{sortDirection === 'desc' ? (
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
) : null}
|
||||
{sortDirection == null ? (
|
||||
<ArrowsUpDownIcon className="h-4 w-4" />
|
||||
) : null}
|
||||
{sortDirection == null ? <ArrowsUpDownIcon className="h-4 w-4" /> : null}
|
||||
</span>
|
||||
</button>
|
||||
) : (
|
||||
@@ -99,7 +109,11 @@ export function Table<T>({
|
||||
{isLoading ? (
|
||||
<tr className="table-body-row">
|
||||
<td colSpan={headers.length} className="px-4 py-6 text-center">
|
||||
<Label as="span" variant="body2" className="inline-flex items-center justify-center ui-loading">
|
||||
<Label
|
||||
as="span"
|
||||
variant="body2"
|
||||
className="inline-flex items-center justify-center ui-loading"
|
||||
>
|
||||
<ArrowPathIcon className="h-5 w-5 animate-spin" aria-hidden="true" />
|
||||
</Label>
|
||||
</td>
|
||||
@@ -114,28 +128,31 @@ export function Table<T>({
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{!isLoading && data.map((row, index) => (
|
||||
<tr key={rowKey(row, index)} className="table-body-row">
|
||||
{headers.map((header) => {
|
||||
const content = typeof header.value === 'function'
|
||||
? (header.value as (item: T) => ReactNode)(row)
|
||||
: header.value;
|
||||
return (
|
||||
<td key={`${header.id}-${index}`} className={`table-cell-secondary ${header.cellClassName ?? ''}`.trim()}>
|
||||
{content}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
{!isLoading &&
|
||||
data.map((row, index) => (
|
||||
<tr key={rowKey(row, index)} className="table-body-row">
|
||||
{headers.map((header) => {
|
||||
const content =
|
||||
typeof header.value === 'function'
|
||||
? (header.value as (item: T) => ReactNode)(row)
|
||||
: header.value;
|
||||
return (
|
||||
<td
|
||||
key={`${header.id}-${index}`}
|
||||
className={`table-cell-secondary ${header.cellClassName ?? ''}`.trim()}
|
||||
>
|
||||
{content}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{pagination ? (
|
||||
<div className="flex flex-col gap-3 border-t border-zinc-500/20 px-4 py-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Label variant="body2">
|
||||
{pagination.total} results
|
||||
</Label>
|
||||
<Label variant="body2">{pagination.total} results</Label>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{pagination.onPageSizeChange ? (
|
||||
<Dropdown
|
||||
@@ -143,7 +160,7 @@ export function Table<T>({
|
||||
value={String(pagination.pageSize)}
|
||||
choices={[5, 10, 20, 50, 100].map((size) => ({
|
||||
id: String(size),
|
||||
label: String(size)
|
||||
label: String(size),
|
||||
}))}
|
||||
size="sm"
|
||||
layout="inline"
|
||||
|
||||
Reference in New Issue
Block a user