File size: 2,087 Bytes
f1dd159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-react";
import type { ComponentProps, ReactNode } from "react";
import { useTranslation } from "react-i18next";

import { TableHead } from "@/components/ui/table";
import { cn } from "@/shared/lib/cn";
import type { SortOrder } from "@/shared/lib/table-sort";

type SortableTableHeadProps = Omit<ComponentProps<typeof TableHead>, "children" | "onChange"> & {
  children: ReactNode;
  field: string;
  sortBy: string;
  sortOrder: SortOrder;
  initialOrder?: SortOrder;
  align?: "left" | "center" | "right";
  onSort: (field: string, initialOrder: SortOrder) => void;
};

export function SortableTableHead({ children, field, sortBy, sortOrder, initialOrder = "asc", align = "left", className, onSort, ...props }: SortableTableHeadProps) {
  const { t } = useTranslation();
  const active = sortBy === field;
  const nextOrder = active && sortOrder === "asc" ? "desc" : active ? "asc" : initialOrder;
  const Icon = active ? sortOrder === "asc" ? ArrowUp : ArrowDown : ArrowUpDown;

  return (
    <TableHead
      aria-sort={active ? sortOrder === "asc" ? "ascending" : "descending" : "none"}
      className={className}
      {...props}
    >
      <button
        type="button"
        className={cn(
          "group/sort -mx-1 inline-flex h-8 max-w-full items-center gap-1 px-1 text-left transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
          align === "center" && "w-full justify-center text-center",
          align === "right" && "ml-auto flex w-full justify-end text-right",
        )}
        aria-label={t(nextOrder === "asc" ? "common.sortAscending" : "common.sortDescending", { column: typeof children === "string" ? children : "" })}
        onClick={() => onSort(field, initialOrder)}
      >
        <span className="truncate">{children}</span>
        <Icon className={cn("size-3 shrink-0", active ? "text-foreground" : "text-muted-foreground/45 group-hover/sort:text-muted-foreground")} aria-hidden />
      </button>
    </TableHead>
  );
}