refactor: improve icon handling with component-based approach

Replace ReactNode icon props with IconComponent type for better
type safety and consistency. Add configurable iconSize prop to
control icon dimensions. Update ShareButton to pass icon
components instead of JSX elements.
This commit is contained in:
2025-10-23 14:58:26 -05:00
parent 9350864929
commit a257675bf7
2 changed files with 28 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import type { FunctionComponent, ReactNode } from "react"; import type { FunctionComponent, ComponentType } from "react";
import { useState, useCallback, useEffect, useRef } from "react"; import { useState, useCallback, useEffect, useRef } from "react";
import { CheckIcon, ClipboardIcon } from "@radix-ui/react-icons"; import { CheckIcon, ClipboardIcon } from "@radix-ui/react-icons";
import type { IconButtonProps } from "@radix-ui/themes"; import type { IconButtonProps } from "@radix-ui/themes";
@@ -15,6 +15,9 @@ export type ButtonSize = IconButtonProps["size"];
export type ButtonVariant = IconButtonProps["variant"]; export type ButtonVariant = IconButtonProps["variant"];
export type ButtonColor = IconButtonProps["color"]; export type ButtonColor = IconButtonProps["color"];
// Type for icon components that accept width/height props (e.g., Radix UI icons)
export type IconComponent = ComponentType<{ width?: string | number; height?: string | number }>;
export type CopyButtonProps = { export type CopyButtonProps = {
/** /**
* The value to copy to clipboard when the button is clicked * The value to copy to clipboard when the button is clicked
@@ -35,9 +38,14 @@ export type CopyButtonProps = {
*/ */
color?: ButtonColor | null; color?: ButtonColor | null;
/** /**
* Optional custom icon to show when not copied (defaults to ClipboardIcon) * Optional custom icon component to show when not copied (defaults to ClipboardIcon)
*/ */
icon?: ReactNode; icon?: IconComponent;
/**
* Size for both the custom icon and checkmark icon
* @default 16
*/
iconSize?: number;
/** /**
* Tooltip text to show when not copied (defaults to "Copy to Clipboard") * Tooltip text to show when not copied (defaults to "Copy to Clipboard")
*/ */
@@ -49,7 +57,8 @@ const CopyButton: FunctionComponent<CopyButtonProps> = ({
size = "1", size = "1",
variant = "ghost", variant = "ghost",
color = "gray", color = "gray",
icon, icon: IconComp,
iconSize = 16,
tooltipText = "Copy to Clipboard", tooltipText = "Copy to Clipboard",
}) => { }) => {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
@@ -91,6 +100,9 @@ const CopyButton: FunctionComponent<CopyButtonProps> = ({
setTooltipOpen(open); setTooltipOpen(open);
}, []); }, []);
// Determine which icon component to render
const ActiveIcon = copied ? CheckIcon : (IconComp ?? ClipboardIcon);
return ( return (
<Tooltip <Tooltip
content={copied ? "Copied!" : tooltipText} content={copied ? "Copied!" : tooltipText}
@@ -104,7 +116,7 @@ const CopyButton: FunctionComponent<CopyButtonProps> = ({
aria-label={copied ? "Copied!" : tooltipText} aria-label={copied ? "Copied!" : tooltipText}
onClick={handleCopy} onClick={handleCopy}
> >
{copied ? <CheckIcon /> : (icon ?? <ClipboardIcon />)} <ActiveIcon width={iconSize} height={iconSize} />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
); );

View File

@@ -11,7 +11,8 @@ export type ShareButtonProps = Omit<CopyButtonProps, "value"> & {
const ShareButton: FunctionComponent<ShareButtonProps> = ({ const ShareButton: FunctionComponent<ShareButtonProps> = ({
url, url,
icon = <Link2Icon />, icon = Link2Icon,
iconSize = 18,
tooltipText = "Copy shareable link", tooltipText = "Copy shareable link",
...copyButtonProps ...copyButtonProps
}) => { }) => {
@@ -24,7 +25,15 @@ const ShareButton: FunctionComponent<ShareButtonProps> = ({
} }
} }
return <CopyButton {...copyButtonProps} value={url} icon={icon} tooltipText={tooltipText} />; return (
<CopyButton
{...copyButtonProps}
value={url}
icon={icon}
iconSize={iconSize}
tooltipText={tooltipText}
/>
);
}; };
export default ShareButton; export default ShareButton;