forked from Cloud-Pipelines/pipeline-editor
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathCopyText.tsx
More file actions
121 lines (111 loc) · 3.01 KB
/
CopyText.tsx
File metadata and controls
121 lines (111 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { type MouseEvent, useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Icon } from "@/components/ui/icon";
import { InlineStack } from "@/components/ui/layout";
import { Text } from "@/components/ui/typography";
import { cn } from "@/lib/utils";
import { copyToClipboard } from "@/utils/string";
interface CopyTextProps {
children: string;
displayValue?: string;
className?: string;
alwaysShowButton?: boolean;
compact?: boolean;
size?: "xs" | "sm" | "md" | "lg" | "xl";
}
export const CopyText = ({
children,
displayValue,
className,
alwaysShowButton = false,
compact = false,
size = "md",
}: CopyTextProps) => {
const [isCopied, setIsCopied] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const handleCopy = () => {
copyToClipboard(children);
setIsCopied(true);
};
useEffect(() => {
if (isCopied) {
const timer = setTimeout(() => {
setIsCopied(false);
}, 1500);
return () => clearTimeout(timer);
}
}, [isCopied]);
const handleButtonClick = (e: MouseEvent) => {
e.stopPropagation();
handleCopy();
};
return (
<div
className="group cursor-pointer"
onClick={handleCopy}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
title={children}
>
<InlineStack gap="1" blockAlign="center" wrap="nowrap">
<Text
size={size}
className={cn(
"transition-all duration-150",
className,
isCopied && "text-emerald-400!",
)}
>
{displayValue ?? children}
</Text>
<Button
variant="ghost"
size="icon"
className={cn(
compact ? "h-2 w-2" : "h-6 w-6",
"shrink-0 transition-opacity duration-200",
alwaysShowButton || isCopied
? "opacity-100"
: "opacity-0 group-hover:opacity-100",
)}
onClick={handleButtonClick}
>
<CopyIcon
isCopied={isCopied}
alwaysShow={alwaysShowButton || isHovered}
compact={compact}
/>
</Button>
</InlineStack>
</div>
);
};
interface CopyIconProps {
isCopied: boolean;
alwaysShow: boolean;
compact?: boolean;
}
const CopyIcon = ({ isCopied, alwaysShow, compact = false }: CopyIconProps) => (
<span className={cn("relative h-3 w-3")}>
<Icon
name="Check"
size={compact ? "xs" : "sm"}
className={cn(
"absolute inset-0 text-emerald-400 transition-all duration-200",
isCopied
? "rotate-0 scale-100 opacity-100"
: "-rotate-90 scale-0 opacity-0",
)}
/>
<Icon
name="Copy"
size={compact ? "xs" : "sm"}
className={cn(
"absolute inset-0 text-muted-foreground transition-all duration-200",
alwaysShow && !isCopied
? "rotate-0 scale-100 opacity-100"
: "rotate-90 scale-0 opacity-0",
)}
/>
</span>
);