Skip to content

Commit 8fe8f83

Browse files
committed
Adds copy to other titles
1 parent be6f694 commit 8fe8f83

File tree

4 files changed

+129
-35
lines changed

4 files changed

+129
-35
lines changed

src/components/Editor/PipelineDetails.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useEffect, useState } from "react";
33

44
import { PipelineValidationList } from "@/components/Editor/components/PipelineValidationList/PipelineValidationList";
55
import { useValidationIssueNavigation } from "@/components/Editor/hooks/useValidationIssueNavigation";
6+
import { CopyText } from "@/components/shared/CopyText/CopyText";
67
import { Button } from "@/components/ui/button";
78
import { Icon } from "@/components/ui/icon";
89
import { InlineStack } from "@/components/ui/layout";
@@ -134,9 +135,9 @@ const PipelineDetails = () => {
134135
{/* Header */}
135136
<div className="flex items-center gap-2 max-w-[90%]">
136137
<Network className="w-6 h-6 text-secondary-foreground rotate-270 min-w-6 min-h-6" />
137-
<h2 className="text-lg font-semibold" data-testid="pipeline-name">
138+
<CopyText className="text-lg font-semibold" alwaysShowButton>
138139
{componentSpec.name ?? "Unnamed Pipeline"}
139-
</h2>
140+
</CopyText>
140141
<RenamePipeline />
141142
</div>
142143

src/components/PipelineRun/RunDetails.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Frown, Videotape } from "lucide-react";
22

3+
import { CopyText } from "@/components/shared/CopyText/CopyText";
34
import { Spinner } from "@/components/ui/spinner";
45
import { useCheckComponentSpecFromPath } from "@/hooks/useCheckComponentSpecFromPath";
56
import { useUserDetails } from "@/hooks/useUserDetails";
@@ -84,9 +85,9 @@ export const RunDetails = () => {
8485
<div className="p-2 flex flex-col gap-6 h-full">
8586
<div className="flex items-center gap-2 max-w-[90%]">
8687
<Videotape className="w-6 h-6 text-gray-500" />
87-
<h2 className="text-lg font-semibold">
88+
<CopyText className="text-lg font-semibold" alwaysShowButton>
8889
{componentSpec.name ?? "Unnamed Pipeline"}
89-
</h2>
90+
</CopyText>
9091
<StatusIcon status={runStatus} tooltip />
9192
</div>
9293

src/components/shared/CopyText/CopyText.tsx

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ interface CopyTextProps {
1313
children: string;
1414
className?: string;
1515
showButton?: boolean;
16+
alwaysShowButton?: boolean;
1617
}
1718

1819
export const CopyText = ({
1920
children,
2021
className,
2122
showButton = true,
23+
alwaysShowButton = false,
2224
}: CopyTextProps) => {
2325
const [isCopied, setIsCopied] = useState(false);
2426
const [isHovered, setIsHovered] = useState(false);
@@ -68,39 +70,55 @@ export const CopyText = ({
6870
{children}
6971
</Text>
7072

71-
<Button
72-
variant="ghost"
73-
size="icon"
74-
className={cn(
75-
"h-4 w-4 shrink-0 transition-opacity duration-200",
76-
isCopied ? "opacity-100" : "opacity-0 group-hover:opacity-100",
77-
)}
78-
onClick={handleButtonClick}
79-
>
80-
<span className="relative h-3 w-3">
81-
<Icon
82-
name="Check"
83-
size="sm"
84-
className={cn(
85-
"absolute inset-0 text-emerald-400 transition-all duration-200",
86-
isCopied
87-
? "rotate-0 scale-100 opacity-100"
88-
: "-rotate-90 scale-0 opacity-0",
89-
)}
90-
/>
91-
<Icon
92-
name="Copy"
93-
size="sm"
94-
className={cn(
95-
"absolute inset-0 text-muted-foreground transition-all duration-200",
96-
isHovered && !isCopied
97-
? "rotate-0 scale-100 opacity-100"
98-
: "rotate-90 scale-0 opacity-0",
99-
)}
73+
{showButton && (
74+
<Button
75+
variant="ghost"
76+
size="icon"
77+
className={cn(
78+
"h-6 w-6 shrink-0 transition-opacity duration-200",
79+
alwaysShowButton || isCopied
80+
? "opacity-100"
81+
: "opacity-0 group-hover:opacity-100",
82+
)}
83+
onClick={handleButtonClick}
84+
>
85+
<CopyIcon
86+
isCopied={isCopied}
87+
alwaysShow={alwaysShowButton || isHovered}
10088
/>
101-
</span>
102-
</Button>
89+
</Button>
90+
)}
10391
</InlineStack>
10492
</div>
10593
);
10694
};
95+
96+
interface CopyIconProps {
97+
isCopied: boolean;
98+
alwaysShow: boolean;
99+
}
100+
101+
const CopyIcon = ({ isCopied, alwaysShow }: CopyIconProps) => (
102+
<span className="relative h-3.5 w-3.5">
103+
<Icon
104+
name="Check"
105+
size="sm"
106+
className={cn(
107+
"absolute inset-0 text-emerald-400 transition-all duration-200",
108+
isCopied
109+
? "rotate-0 scale-100 opacity-100"
110+
: "-rotate-90 scale-0 opacity-0",
111+
)}
112+
/>
113+
<Icon
114+
name="Copy"
115+
size="sm"
116+
className={cn(
117+
"absolute inset-0 text-muted-foreground transition-all duration-200",
118+
alwaysShow && !isCopied
119+
? "rotate-0 scale-100 opacity-100"
120+
: "rotate-90 scale-0 opacity-0",
121+
)}
122+
/>
123+
</span>
124+
);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import type { ReactNode, RefObject } from "react";
2+
3+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
4+
import { BlockStack, InlineStack } from "@/components/ui/layout";
5+
import { Text } from "@/components/ui/typography";
6+
import { cn } from "@/lib/utils";
7+
import type { TaskNodeDimensions } from "@/types/taskNode";
8+
9+
interface TaskNodeCardContentProps {
10+
name: string;
11+
taskId?: string;
12+
dimensions: TaskNodeDimensions;
13+
selected?: boolean;
14+
highlighted?: boolean;
15+
isSubgraphNode?: boolean;
16+
nodeRef?: RefObject<HTMLDivElement | null>;
17+
onDoubleClick?: () => void;
18+
headerActions?: ReactNode;
19+
headerIcons?: ReactNode;
20+
children: ReactNode;
21+
}
22+
23+
export const TaskNodeCardContent = ({
24+
name,
25+
taskId,
26+
dimensions,
27+
selected = false,
28+
highlighted = false,
29+
isSubgraphNode = false,
30+
nodeRef,
31+
onDoubleClick,
32+
headerActions,
33+
headerIcons,
34+
children,
35+
}: TaskNodeCardContentProps) => {
36+
return (
37+
<Card
38+
className={cn(
39+
"rounded-2xl border-gray-200 border-2 wrap-break-word p-0 drop-shadow-none gap-2",
40+
selected ? "border-gray-500" : "hover:border-slate-200",
41+
highlighted && "border-orange-500!",
42+
isSubgraphNode && "cursor-pointer",
43+
)}
44+
style={{
45+
width: dimensions.w + "px",
46+
height: "auto",
47+
}}
48+
ref={nodeRef}
49+
onDoubleClick={onDoubleClick}
50+
>
51+
<CardHeader className="border-b border-slate-200 px-2 py-2.5 flex flex-row justify-between items-start">
52+
<BlockStack>
53+
<InlineStack gap="2" blockAlign="center" wrap="nowrap">
54+
{headerIcons}
55+
<CardTitle className="wrap-break-word text-left text-xs text-slate-900">
56+
{name}
57+
</CardTitle>
58+
</InlineStack>
59+
{taskId &&
60+
taskId !== name &&
61+
!taskId.match(new RegExp(`^${name}\\s*\\d+$`)) && (
62+
<Text size="xs" tone="subdued" className="font-light">
63+
{taskId}
64+
</Text>
65+
)}
66+
</BlockStack>
67+
{headerActions}
68+
</CardHeader>
69+
<CardContent className="p-2 flex flex-col gap-2">
70+
{children}
71+
</CardContent>
72+
</Card>
73+
);
74+
};

0 commit comments

Comments
 (0)