mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-17 00:44:17 +00:00
107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { Cell, flexRender, Row } from "@tanstack/react-table";
|
|
import { motion } from "framer-motion";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface DataTableAnimatedRowProps<TData> {
|
|
row: Row<TData>;
|
|
/** Pass explicitly to break React Compiler memoization when selection changes */
|
|
isSelected?: boolean;
|
|
/** Pass explicitly to break React Compiler memoization for partial selection */
|
|
isSomeSelected?: boolean;
|
|
}
|
|
|
|
/**
|
|
* DataTableAnimatedRow renders a table row with smooth expand/collapse animations.
|
|
*
|
|
* The trick: You cannot animate <tr> height directly (tables ignore it).
|
|
* Instead, we wrap each cell's content in a motion.div and animate THAT.
|
|
*
|
|
* How it works:
|
|
* 1. The <tr> itself is not animated
|
|
* 2. Each <td> contains a motion.div wrapper
|
|
* 3. The wrapper animates height from 0 to "auto"
|
|
* 4. overflow-hidden clips content during animation
|
|
* 5. Padding is on the inner content, not the td
|
|
*/
|
|
export function DataTableAnimatedRow<TData>({
|
|
row,
|
|
isSelected,
|
|
isSomeSelected,
|
|
}: DataTableAnimatedRowProps<TData>) {
|
|
"use no memo";
|
|
void isSomeSelected;
|
|
|
|
return (
|
|
<motion.tr
|
|
initial="collapsed"
|
|
animate="open"
|
|
exit="collapsed"
|
|
variants={{
|
|
open: { opacity: 1 },
|
|
collapsed: { opacity: 0 },
|
|
}}
|
|
transition={{ duration: 0.2 }}
|
|
data-state={isSelected ? "selected" : undefined}
|
|
className={cn(
|
|
"transition-colors",
|
|
"[&>td:first-child]:rounded-l-full [&>td:last-child]:rounded-r-full",
|
|
"hover:bg-bg-neutral-tertiary",
|
|
"data-[state=selected]:bg-bg-neutral-tertiary",
|
|
)}
|
|
>
|
|
{row.getVisibleCells().map((cell: Cell<TData, unknown>, index, cells) => {
|
|
const isFirst = index === 0;
|
|
const isLast = index === cells.length - 1;
|
|
const cellDef = cell.column.columnDef.cell;
|
|
const cellContext = cell.getContext();
|
|
const cellContent =
|
|
typeof cellDef === "function"
|
|
? cellDef(cellContext)
|
|
: flexRender(cellDef, cellContext);
|
|
|
|
return (
|
|
<td key={cell.id} className="overflow-hidden p-0">
|
|
<motion.div
|
|
initial="collapsed"
|
|
animate="open"
|
|
exit="collapsed"
|
|
variants={{
|
|
open: {
|
|
height: "auto",
|
|
opacity: 1,
|
|
transition: {
|
|
height: { duration: 0.2, ease: "easeOut" },
|
|
opacity: { duration: 0.15, delay: 0.05 },
|
|
},
|
|
},
|
|
collapsed: {
|
|
height: 0,
|
|
opacity: 0,
|
|
transition: {
|
|
height: { duration: 0.2, ease: "easeIn" },
|
|
opacity: { duration: 0.1 },
|
|
},
|
|
},
|
|
}}
|
|
className="overflow-hidden"
|
|
>
|
|
<div
|
|
className={cn(
|
|
"px-1.5 py-2",
|
|
isFirst && "pl-3",
|
|
isLast && "pr-3",
|
|
)}
|
|
>
|
|
{cellContent}
|
|
</div>
|
|
</motion.div>
|
|
</td>
|
|
);
|
|
})}
|
|
</motion.tr>
|
|
);
|
|
}
|