Added CopyableTableCell

This commit is contained in:
Matt Aitken
2025-12-20 16:55:09 +00:00
parent 92439f8424
commit 84673c5e2d
2 changed files with 78 additions and 1 deletions
@@ -1,9 +1,11 @@
import { ChevronRightIcon } from "@heroicons/react/24/solid";
import { Link } from "@remix-run/react";
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
import React, { type ReactNode, forwardRef, useState, useContext, createContext } from "react";
import { useCopy } from "~/hooks/useCopy";
import { cn } from "~/utils/cn";
import { Popover, PopoverContent, PopoverVerticalEllipseTrigger } from "./Popover";
import { InfoIconTooltip } from "./Tooltip";
import { InfoIconTooltip, SimpleTooltip } from "./Tooltip";
const variants = {
bright: {
@@ -269,6 +271,53 @@ export const TableCell = forwardRef<HTMLTableCellElement, TableCellProps>(
}
);
type CopyableTableCellProps = TableCellProps & {
value: string;
};
export const CopyableTableCell = forwardRef<HTMLTableCellElement, CopyableTableCellProps>(
({ value, children, className, ...props }, ref) => {
const { copy, copied } = useCopy(value);
return (
<TableCell ref={ref} className={cn("group/copyable-cell", className)} {...props}>
<div className="relative flex items-center">
{children}
<span
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
copy();
}}
className="absolute right-0 top-1/2 z-10 hidden -translate-y-1/2 cursor-pointer group-hover/copyable-cell:flex"
>
<SimpleTooltip
button={
<span
className={cn(
"flex size-6 items-center justify-center rounded border border-charcoal-650 bg-charcoal-750",
copied
? "text-green-500"
: "text-text-dimmed hover:border-charcoal-600 hover:bg-charcoal-700 hover:text-text-bright"
)}
>
{copied ? (
<ClipboardCheckIcon className="size-3.5" />
) : (
<ClipboardIcon className="size-3.5" />
)}
</span>
}
content={copied ? "Copied!" : "Copy"}
disableHoverableContent
/>
</span>
</div>
</TableCell>
);
}
);
export const TableCellChevron = forwardRef<
HTMLTableCellElement,
{
@@ -2,6 +2,7 @@ import React from "react";
import { Header1, Header2 } from "~/components/primitives/Headers";
import { Paragraph } from "~/components/primitives/Paragraph";
import {
CopyableTableCell,
Table,
TableBody,
TableCell,
@@ -60,6 +61,33 @@ export default function Story() {
</TableBody>
</Table>
</div>
<div className="flex flex-col gap-2">
<Header1>Copyable cells</Header1>
<Paragraph>
Hover over the first column to see the copy button. Click to copy the cell value.
</Paragraph>
<Table>
<TableHeader className="bg-background-bright">
<TableRow>
<TableHeaderCell>ID (copyable)</TableHeaderCell>
<TableHeaderCell>Name</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{Array.from({ length: 5 }, (_, index) => {
const id = `run_${crypto.randomUUID().slice(0, 8)}`;
return (
<TableRow key={index}>
<CopyableTableCell value={id}>{id}</CopyableTableCell>
<TableCell>Task {index + 1}</TableCell>
<TableCell>Completed</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</div>
);
}