2025-10-16 17:59:13 +00:00
|
|
|
import { Button, Separator, Tooltip, TooltipContent, TooltipTrigger } from '@repo/shadcn-ui/components'
|
|
|
|
|
import { cn } from '@repo/shadcn-ui/lib/utils'
|
2025-10-16 11:18:55 +00:00
|
|
|
import { Table } from "@tanstack/react-table"
|
2025-10-16 17:59:13 +00:00
|
|
|
import { ArrowDownIcon, ArrowUpIcon, CopyPlusIcon, PlusIcon, ScanIcon, TrashIcon } from 'lucide-react'
|
|
|
|
|
import { useCallback, useMemo } from 'react'
|
|
|
|
|
import { useTranslation } from "../../locales/i18n.ts"
|
2025-10-16 11:18:55 +00:00
|
|
|
import { DataTableViewOptions } from './data-table-view-options.tsx'
|
2025-10-16 17:59:13 +00:00
|
|
|
import { DataTableMeta } from './data-table.tsx'
|
2025-10-16 11:18:55 +00:00
|
|
|
|
|
|
|
|
interface DataTableToolbarProps<TData> {
|
|
|
|
|
table: Table<TData>
|
2025-10-16 17:59:13 +00:00
|
|
|
showViewOptions?: boolean
|
2025-10-16 11:18:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function DataTableToolbar<TData>({
|
|
|
|
|
table,
|
2025-10-16 17:59:13 +00:00
|
|
|
showViewOptions = true,
|
2025-10-16 11:18:55 +00:00
|
|
|
}: DataTableToolbarProps<TData>) {
|
2025-10-16 17:59:13 +00:00
|
|
|
|
|
|
|
|
const { t } = useTranslation()
|
|
|
|
|
const meta = table.options.meta as DataTableMeta<TData>
|
|
|
|
|
| undefined
|
|
|
|
|
|
|
|
|
|
const rowSelection = table.getSelectedRowModel().rows;
|
|
|
|
|
const selectedCount = rowSelection.length;
|
|
|
|
|
const hasSelection = selectedCount > 0;
|
|
|
|
|
|
|
|
|
|
const selectedRowIndexes = useMemo(() => rowSelection.map((row) => row.index), [rowSelection]);
|
|
|
|
|
|
|
|
|
|
const handleAdd = useCallback(() => meta?.tableOps?.onAdd?.(table), [meta])
|
|
|
|
|
const handleDuplicateSelected = useCallback(
|
|
|
|
|
() => meta?.bulkOps?.duplicateSelected?.(selectedRowIndexes, table),
|
|
|
|
|
[meta, selectedRowIndexes]
|
|
|
|
|
)
|
|
|
|
|
const handleMoveSelectedUp = useCallback(
|
|
|
|
|
() => meta?.bulkOps?.moveSelectedUp?.(selectedRowIndexes, table),
|
|
|
|
|
[meta, selectedRowIndexes]
|
|
|
|
|
)
|
|
|
|
|
const handleMoveSelectedDown = useCallback(
|
|
|
|
|
() => meta?.bulkOps?.moveSelectedDown?.(selectedRowIndexes, table),
|
|
|
|
|
[meta, selectedRowIndexes]
|
|
|
|
|
)
|
|
|
|
|
const handleRemoveSelected = useCallback(
|
|
|
|
|
() => meta?.bulkOps?.removeSelected?.(selectedRowIndexes, table),
|
|
|
|
|
[meta, selectedRowIndexes]
|
|
|
|
|
)
|
2025-10-16 11:18:55 +00:00
|
|
|
|
|
|
|
|
return (
|
2025-10-16 17:59:13 +00:00
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center justify-between gap-2 py-2",
|
|
|
|
|
"border-b border-muted px-1 sm:px-2"
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{/* IZQUIERDA: acciones globales y sobre selección */}
|
|
|
|
|
<div className="flex flex-1 items-center gap-2 flex-wrap">
|
|
|
|
|
{meta?.tableOps?.onAdd && (
|
|
|
|
|
<Button
|
|
|
|
|
type='button'
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={handleAdd}
|
|
|
|
|
aria-label={t("components.datatable.actions.add")}
|
|
|
|
|
>
|
|
|
|
|
<PlusIcon className="size-4 mr-1" aria-hidden="true" />
|
|
|
|
|
<span>{t("components.datatable.actions.add")}</span>
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{hasSelection && (
|
|
|
|
|
<>
|
|
|
|
|
<Separator orientation="vertical" className="h-5 mx-1" />
|
|
|
|
|
|
|
|
|
|
{meta?.bulkOps?.duplicateSelected && (
|
|
|
|
|
<Button
|
|
|
|
|
type='button'
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={handleDuplicateSelected}
|
|
|
|
|
aria-label={t("components.datatable.actions.duplicate")}
|
|
|
|
|
>
|
|
|
|
|
<CopyPlusIcon className="size-4 mr-1" aria-hidden="true" />
|
|
|
|
|
<span>{t("components.datatable.actions.duplicate")}</span>
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
2025-10-16 11:18:55 +00:00
|
|
|
|
2025-10-16 17:59:13 +00:00
|
|
|
{meta?.bulkOps?.moveSelectedUp && (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
type='button'
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={handleMoveSelectedUp}
|
|
|
|
|
aria-label={t("components.datatable.actions.move_up")}
|
|
|
|
|
>
|
|
|
|
|
<ArrowUpIcon className="size-4" aria-hidden="true" />
|
|
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>{t("components.datatable.actions.move_up")}</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{meta?.bulkOps?.moveSelectedDown && (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
type='button'
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={handleMoveSelectedDown}
|
|
|
|
|
aria-label={t("components.datatable.actions.move_down")}
|
|
|
|
|
>
|
|
|
|
|
<ArrowDownIcon className="size-4" aria-hidden="true" />
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>{t("components.datatable.actions.move_down")}</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{meta?.bulkOps?.removeSelected && (
|
|
|
|
|
<>
|
|
|
|
|
<Separator orientation="vertical" className="h-5 mx-1 w-1 bg-red-500" />
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
type='button'
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="destructive"
|
|
|
|
|
onClick={handleRemoveSelected}
|
|
|
|
|
aria-label={t("components.datatable.actions.remove")}
|
|
|
|
|
>
|
|
|
|
|
<TrashIcon className="size-4 mr-1" aria-hidden="true" />
|
|
|
|
|
<span>{t("components.datatable.actions.remove")}</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Separator orientation="vertical" className="h-6 mx-1 bg-muted/50" />
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
size="sm"
|
|
|
|
|
variant="outline"
|
|
|
|
|
disabled={!table.getSelectedRowModel().rows.length}
|
|
|
|
|
onClick={() => table.resetRowSelection()}
|
|
|
|
|
>
|
|
|
|
|
<ScanIcon className="size-4 mr-1" aria-hidden="true" />
|
|
|
|
|
<span>Quitar selección</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent>Quita la selección</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2025-10-16 11:18:55 +00:00
|
|
|
</div>
|
2025-10-16 17:59:13 +00:00
|
|
|
|
|
|
|
|
{/* DERECHA: opciones de vista / filtros */}
|
2025-10-16 11:18:55 +00:00
|
|
|
<div className="flex items-center gap-2">
|
2025-10-16 17:59:13 +00:00
|
|
|
{showViewOptions && <DataTableViewOptions table={table} />}
|
2025-10-16 11:18:55 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|