198 lines
6.8 KiB
TypeScript
198 lines
6.8 KiB
TypeScript
import {
|
|
Button,
|
|
Separator,
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from "@repo/shadcn-ui/components";
|
|
import { cn } from "@repo/shadcn-ui/lib/utils";
|
|
import type { Table } from "@tanstack/react-table";
|
|
import {
|
|
ArrowDownIcon,
|
|
ArrowUpIcon,
|
|
CopyPlusIcon,
|
|
PlusIcon,
|
|
ScanIcon,
|
|
TrashIcon,
|
|
} from "lucide-react";
|
|
import React from "react";
|
|
|
|
import { useTranslation } from "../../locales/i18n.ts";
|
|
|
|
import type { DataTableMeta } from "./data-table.tsx";
|
|
import { DataTableViewOptions } from "./data-table-view-options.tsx";
|
|
|
|
interface DataTableToolbarProps<TData> {
|
|
table: Table<TData>;
|
|
showViewOptions?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export function DataTableToolbar<TData>({
|
|
table,
|
|
showViewOptions = true,
|
|
className,
|
|
}: DataTableToolbarProps<TData>) {
|
|
const { t } = useTranslation();
|
|
const meta = table.options.meta as DataTableMeta<TData> | undefined;
|
|
|
|
const readOnly = meta?.readOnly ?? false;
|
|
|
|
// Modelos y conteos
|
|
const allRows = table.getFilteredRowModel().rows;
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
const totalCount = allRows.length;
|
|
const selectedCount = selectedRows.length;
|
|
const hasSelection = selectedCount > 0;
|
|
|
|
// Índices seleccionados (memoizado)
|
|
const selectedIndexes = React.useMemo(() => selectedRows.map((r) => r.index), [selectedRows]);
|
|
|
|
const handleAdd = React.useCallback(() => {
|
|
if (!readOnly) meta?.tableOps?.onAdd?.(table);
|
|
}, [meta, table, readOnly]);
|
|
|
|
const handleDuplicateSelected = React.useCallback(() => {
|
|
if (!readOnly) meta?.bulkOps?.duplicateSelected?.(selectedIndexes, table);
|
|
}, [meta, selectedIndexes, table, readOnly]);
|
|
|
|
const handleMoveSelectedUp = React.useCallback(() => {
|
|
if (!readOnly) meta?.bulkOps?.moveSelectedUp?.(selectedIndexes, table);
|
|
}, [meta, selectedIndexes, table, readOnly]);
|
|
|
|
const handleMoveSelectedDown = React.useCallback(() => {
|
|
if (!readOnly) meta?.bulkOps?.moveSelectedDown?.(selectedIndexes, table);
|
|
}, [meta, selectedIndexes, table, readOnly]);
|
|
|
|
const handleRemoveSelected = React.useCallback(() => {
|
|
if (!readOnly) meta?.bulkOps?.removeSelected?.(selectedIndexes, table);
|
|
}, [meta, selectedIndexes, table, readOnly]);
|
|
|
|
const handleClearSelection = React.useCallback(() => {
|
|
table.resetRowSelection();
|
|
}, [table]);
|
|
|
|
// Render principal
|
|
return (
|
|
<div className={cn("flex items-center justify-between gap-2 py-4 bg-transparent", className)}>
|
|
{/* IZQUIERDA: acciones + contador */}
|
|
<div className="flex flex-1 items-center gap-3 flex-wrap">
|
|
{/* Botón añadir */}
|
|
{!readOnly && meta?.tableOps?.onAdd && (
|
|
<Button
|
|
aria-label={t("components.datatable.actions.add")}
|
|
className="cursor-pointer"
|
|
onClick={handleAdd}
|
|
size="sm"
|
|
type="button"
|
|
variant={"outline"}
|
|
>
|
|
<PlusIcon aria-hidden="true" className="size-4 mr-1" />
|
|
<span>{t("components.datatable.actions.add")}</span>
|
|
</Button>
|
|
)}
|
|
|
|
{/* Acciones sobre selección */}
|
|
{hasSelection && (
|
|
<>
|
|
<Separator className="h-5 mx-1" orientation="vertical" />
|
|
|
|
{!readOnly && meta?.bulkOps?.duplicateSelected && (
|
|
<Button
|
|
aria-label={t("components.datatable.actions.duplicate")}
|
|
className="cursor-pointer"
|
|
onClick={handleDuplicateSelected}
|
|
size="sm"
|
|
type="button"
|
|
variant="outline"
|
|
>
|
|
<CopyPlusIcon aria-hidden="true" className="size-4 mr-1" />
|
|
<span>{t("components.datatable.actions.duplicate")}</span>
|
|
</Button>
|
|
)}
|
|
|
|
{!readOnly && meta?.bulkOps?.moveSelectedUp && (
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<Button
|
|
aria-label={t("components.datatable.actions.move_up")}
|
|
onClick={handleMoveSelectedUp}
|
|
size="sm"
|
|
type="button"
|
|
variant="outline"
|
|
>
|
|
<ArrowUpIcon aria-hidden="true" className="size-4" />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>{t("components.datatable.actions.move_up")}</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
|
|
{!readOnly && meta?.bulkOps?.moveSelectedDown && (
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<Button
|
|
aria-label={t("components.datatable.actions.move_down")}
|
|
onClick={handleMoveSelectedDown}
|
|
size="sm"
|
|
type="button"
|
|
variant="outline"
|
|
>
|
|
<ArrowDownIcon aria-hidden="true" className="size-4" />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>{t("components.datatable.actions.move_down")}</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
|
|
{!readOnly && meta?.bulkOps?.removeSelected && (
|
|
<>
|
|
<Separator className="h-5 mx-1 w-[1px] bg-red-500/70" orientation="vertical" />
|
|
<Button
|
|
aria-label={t("components.datatable.actions.remove")}
|
|
onClick={handleRemoveSelected}
|
|
size="sm"
|
|
type="button"
|
|
variant="destructive"
|
|
>
|
|
<TrashIcon aria-hidden="true" className="size-4 mr-1" />
|
|
<span>{t("components.datatable.actions.remove")}</span>
|
|
</Button>
|
|
</>
|
|
)}
|
|
|
|
<Separator className="h-6 mx-1 bg-muted/50" orientation="vertical" />
|
|
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<Button onClick={handleClearSelection} size="sm" type="button" variant="outline">
|
|
<ScanIcon aria-hidden="true" className="size-4 mr-1" />
|
|
<span>{t("components.datatable.actions.clear_selection")}</span>
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>{t("components.datatable.actions.clear_selection")}</TooltipContent>
|
|
</Tooltip>
|
|
</>
|
|
)}
|
|
|
|
{/* Contador de selección */}
|
|
<div aria-live="polite" className="text-sm text-muted-foreground ml-2">
|
|
{hasSelection
|
|
? t("components.datatable.selection_summary", {
|
|
count: selectedCount,
|
|
total: totalCount,
|
|
})
|
|
: t("components.datatable.selection_none", { total: totalCount })}
|
|
</div>
|
|
</div>
|
|
|
|
{/* DERECHA: opciones de vista */}
|
|
<div className="flex items-center gap-2">
|
|
{showViewOptions && !readOnly && <DataTableViewOptions table={table} />}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const MemoizedDataTableToolbar = React.memo(DataTableToolbar) as typeof DataTableToolbar;
|