feat: add CopyButton and StatusBadge components with enhanced RDAP card UX

This commit introduces two new reusable components and significantly
improves the user experience across all RDAP cards:

New Components:
- CopyButton: Provides one-click copying functionality for handles,
  addresses, and other identifiers
- StatusBadge: Displays color-coded status badges with proper type
  safety

RDAP Card Enhancements:
- Replace deprecated ClipboardCopyIcon with ClipboardIcon
- Add copy buttons next to all handles, addresses, and identifiers
- Migrate status displays from PropertyList to StatusBadge components
  with color coding
- Replace PropertyList with proper DataList components for roles and
  public IDs
- Improve Events table layout and styling
- Wrap all copyable values in Code components for better visual
  distinction

Type Safety Improvements:
- Add rdapStatusColors mapping with proper Radix UI badge color types
- Update IpNetwork and AutonomousNumber schemas to use typed
  StatusEnum arrays
This commit is contained in:
2025-10-22 12:56:30 -05:00
parent 0e9336df1d
commit 335bc6aee8
11 changed files with 308 additions and 79 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
import type { FunctionComponent, ReactNode } from "react";
import React from "react";
import { useBoolean } from "usehooks-ts";
import { Link2Icon, CodeIcon, DownloadIcon, ClipboardCopyIcon } from "@radix-ui/react-icons";
import { Link2Icon, CodeIcon, DownloadIcon, ClipboardIcon } from "@radix-ui/react-icons";
import { Card, Flex, Box, IconButton, Code, ScrollArea } from "@radix-ui/themes";
type AbstractCardProps = {
@@ -75,7 +75,7 @@ const AbstractCard: FunctionComponent<AbstractCardProps> = ({
}}
aria-label="Copy JSON to clipboard"
>
<ClipboardCopyIcon width="18" height="18" />
<ClipboardIcon width="18" height="18" />
</IconButton>
<IconButton
variant="ghost"
+40
View File
@@ -0,0 +1,40 @@
import type { FunctionComponent } from "react";
import React from "react";
import { ClipboardIcon } from "@radix-ui/react-icons";
import { IconButton } from "@radix-ui/themes";
export type CopyButtonProps = {
value: string;
size?: "1" | "2" | "3";
};
const CopyButton: FunctionComponent<CopyButtonProps> = ({ value, size = "1" }) => {
const handleCopy = () => {
navigator.clipboard.writeText(value).then(
() => {
// Successfully copied to clipboard
},
(err) => {
if (err instanceof Error) {
console.error(`Failed to copy to clipboard (${err.toString()}).`);
} else {
console.error("Failed to copy to clipboard.");
}
}
);
};
return (
<IconButton
size={size}
aria-label="Copy value"
color="gray"
variant="ghost"
onClick={handleCopy}
>
<ClipboardIcon />
</IconButton>
);
};
export default CopyButton;
+36
View File
@@ -0,0 +1,36 @@
import type { FunctionComponent } from "react";
import React from "react";
import type { RdapStatusType } from "@/rdap/schemas";
import { rdapStatusColors, rdapStatusInfo } from "@/rdap/constants";
import { QuestionMarkIcon } from "@radix-ui/react-icons";
import { Badge, HoverCard, Text, Flex } from "@radix-ui/themes";
export type StatusBadgeProps = {
status: RdapStatusType;
};
const StatusBadge: FunctionComponent<StatusBadgeProps> = ({ status }) => {
return (
<HoverCard.Root>
<HoverCard.Trigger>
<Badge
color={rdapStatusColors[status]}
variant="soft"
radius="full"
size="2"
style={{ cursor: "help" }}
>
<Flex align="center" gap="1">
{status}
<QuestionMarkIcon width="12" height="12" />
</Flex>
</Badge>
</HoverCard.Trigger>
<HoverCard.Content maxWidth="400px">
<Text size="2">{rdapStatusInfo[status]}</Text>
</HoverCard.Content>
</HoverCard.Root>
);
};
export default StatusBadge;