Skip to content

Commit b1298bb

Browse files
committed
Adds a copy button and truncation to title
1 parent 98bd0d8 commit b1298bb

File tree

3 files changed

+115
-1
lines changed

3 files changed

+115
-1
lines changed

src/components/layout/AppMenu.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logo from "/public/Tangle_white.png";
22
import { isAuthorizationRequired } from "@/components/shared/Authentication/helpers";
33
import { TopBarAuthentication } from "@/components/shared/Authentication/TopBarAuthentication";
4+
import { CopyText } from "@/components/shared/CopyText/CopyText";
45
import ImportPipeline from "@/components/shared/ImportPipeline";
56
import { InlineStack } from "@/components/ui/layout";
67
import { Link } from "@/components/ui/link";
@@ -34,7 +35,12 @@ const AppMenu = () => {
3435
className="h-8 filter cursor-pointer shrink-0"
3536
/>
3637
</Link>
37-
<span className="text-white text-md font-bold ml-22">{title}</span>
38+
39+
{title && (
40+
<CopyText className="text-white text-md font-bold truncate max-w-lg ml-22">
41+
{title}
42+
</CopyText>
43+
)}
3844
</InlineStack>
3945

4046
<InlineStack blockAlign="center">
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { type MouseEvent, useCallback, useState } from "react";
2+
3+
import { Button } from "@/components/ui/button";
4+
import { Icon } from "@/components/ui/icon";
5+
import { InlineStack } from "@/components/ui/layout";
6+
import { Text } from "@/components/ui/typography";
7+
import { cn } from "@/lib/utils";
8+
import { copyToClipboard } from "@/utils/string";
9+
10+
interface CopyTextProps {
11+
children: string;
12+
className?: string;
13+
}
14+
15+
export const CopyText = ({ children, className }: CopyTextProps) => {
16+
const [isCopied, setIsCopied] = useState(false);
17+
const [isHovered, setIsHovered] = useState(false);
18+
19+
const handleCopy = useCallback(() => {
20+
copyToClipboard(children);
21+
setIsCopied(true);
22+
}, [children]);
23+
24+
const handleButtonClick = useCallback(
25+
(e: MouseEvent) => {
26+
e.stopPropagation();
27+
handleCopy();
28+
},
29+
[handleCopy],
30+
);
31+
32+
const handleAnimationEnd = useCallback(() => {
33+
setIsCopied(false);
34+
}, []);
35+
36+
return (
37+
38+
<div
39+
className="group cursor-pointer"
40+
onClick={handleCopy}
41+
onMouseEnter={() => setIsHovered(true)}
42+
onMouseLeave={() => setIsHovered(false)}
43+
title={children}
44+
>
45+
<InlineStack gap="1" blockAlign="center" wrap="nowrap">
46+
<Text
47+
className={cn(
48+
"transition-all duration-150",
49+
className,
50+
isCopied && "scale-[1.01] text-emerald-400!",
51+
)}
52+
>
53+
{children}
54+
</Text>
55+
56+
<Button
57+
variant={null}
58+
size="icon"
59+
className={cn(
60+
"h-4 w-4 shrink-0 transition-opacity duration-200",
61+
isCopied ? "opacity-100" : "opacity-0 group-hover:opacity-100",
62+
)}
63+
onClick={handleButtonClick}
64+
>
65+
<span className="relative h-3 w-3">
66+
{isCopied ? (
67+
<span
68+
key="check"
69+
className="absolute inset-0 animate-revert-copied"
70+
onAnimationEnd={handleAnimationEnd}
71+
>
72+
<Icon name="Check" size="sm" className="text-emerald-400" />
73+
</span>
74+
) : (
75+
<Icon
76+
key="copy"
77+
name="Copy"
78+
size="sm"
79+
className={cn(
80+
"absolute inset-0 text-muted-foreground transition-all duration-200",
81+
isHovered
82+
? "rotate-0 scale-100 opacity-100"
83+
: "rotate-90 scale-0 opacity-0",
84+
)}
85+
/>
86+
)}
87+
</span>
88+
</Button>
89+
</InlineStack>
90+
</div>
91+
92+
);
93+
};

src/styles/global.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,21 @@ code {
144144
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
145145
--color-sidebar-border: var(--sidebar-border);
146146
--color-sidebar-ring: var(--sidebar-ring);
147+
148+
/* Custom animations */
149+
--animate-revert-copied: revert-copied 0.5s ease-in-out forwards;
150+
151+
@keyframes revert-copied {
152+
0%,
153+
80% {
154+
opacity: 1;
155+
transform: rotate(0deg) scale(1);
156+
}
157+
100% {
158+
opacity: 0;
159+
transform: rotate(-90deg) scale(0);
160+
}
161+
}
147162
}
148163

149164
@layer base {

0 commit comments

Comments
 (0)