Skip to content

Commit bd7aaa7

Browse files
committed
Adds copy tags functionailty on hover
1 parent e024181 commit bd7aaa7

3 files changed

Lines changed: 110 additions & 36 deletions

File tree

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,12 @@
1-
import { useMemo } from "react";
1+
import { Link } from "@remix-run/react";
2+
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
3+
import { useCallback, useMemo, useState } from "react";
4+
import { SimpleTooltip } from "~/components/primitives/Tooltip";
5+
import { cn } from "~/utils/cn";
26
import tagLeftPath from "./tag-left.svg";
37

48
type Tag = string | { key: string; value: string };
59

6-
export function RunTag({ tag }: { tag: string }) {
7-
const tagResult = useMemo(() => splitTag(tag), [tag]);
8-
9-
if (typeof tagResult === "string") {
10-
return (
11-
<span className="flex h-6 items-stretch">
12-
<img src={tagLeftPath} alt="" className="block h-full w-[0.5625rem]" />
13-
<span className="flex items-center rounded-r-sm border-y border-r border-charcoal-700 bg-charcoal-800 pr-1.5 text-text-dimmed">
14-
{tag}
15-
</span>
16-
</span>
17-
);
18-
} else {
19-
return (
20-
<span className="flex h-6 items-stretch">
21-
<img src={tagLeftPath} alt="" className="block h-full w-[0.5625rem]" />
22-
<span className="flex items-center border-y border-r border-charcoal-700 bg-charcoal-800 pr-1.5 text-text-dimmed">
23-
{tagResult.key}
24-
</span>
25-
<span className="flex items-center whitespace-nowrap rounded-r-sm border-y border-r border-charcoal-700 bg-charcoal-750 px-1.5 text-text-dimmed">
26-
{tagResult.value}
27-
</span>
28-
</span>
29-
);
30-
}
31-
}
32-
3310
/** Takes a string and turns it into a tag
3411
*
3512
* If the string has 12 or fewer alpha characters followed by an underscore or colon then we return an object with a key and value
@@ -46,3 +23,103 @@ function splitTag(tag: string): Tag {
4623

4724
return tag;
4825
}
26+
27+
export function RunTag({ tag, to, tooltip }: { tag: string; to?: string; tooltip?: string }) {
28+
const tagResult = useMemo(() => splitTag(tag), [tag]);
29+
const [isHovered, setIsHovered] = useState(false);
30+
31+
// Render the basic tag content
32+
const renderBasicTagContent = () => {
33+
if (typeof tagResult === "string") {
34+
return (
35+
<>
36+
<img src={tagLeftPath} alt="" className="block h-full w-[0.5625rem]" />
37+
<span className="flex items-center rounded-r-sm border-y border-r border-charcoal-700 bg-charcoal-800 pr-1.5 text-text-dimmed group-hover:rounded-r-none group-has-[[href]]:group-hover:border-charcoal-650 group-has-[[href]]:group-hover:text-charcoal-300">
38+
{tag}
39+
</span>
40+
</>
41+
);
42+
} else {
43+
return (
44+
<>
45+
<img src={tagLeftPath} alt="" className="block h-full w-[0.5625rem]" />
46+
<span className="flex items-center border-y border-r border-charcoal-700 bg-charcoal-800 pr-1.5 text-text-dimmed group-has-[[href]]:group-hover:border-charcoal-650 group-has-[[href]]:group-hover:text-charcoal-300">
47+
{tagResult.key}
48+
</span>
49+
<span className="flex items-center whitespace-nowrap rounded-r-sm border-y border-r border-charcoal-700 bg-charcoal-750 px-1.5 text-text-dimmed group-hover:rounded-r-none group-has-[[href]]:group-hover:border-charcoal-650 group-has-[[href]]:group-hover:bg-charcoal-700 group-has-[[href]]:group-hover:text-charcoal-300">
50+
{tagResult.value}
51+
</span>
52+
</>
53+
);
54+
}
55+
};
56+
57+
// The main tag content, optionally wrapped in a Link and SimpleTooltip
58+
const tagContent = to ? (
59+
<SimpleTooltip
60+
button={
61+
<Link to={to} className="group">
62+
<span className="flex h-6 items-stretch">{renderBasicTagContent()}</span>
63+
</Link>
64+
}
65+
content={tooltip || `Filter runs by ${tag}`}
66+
disableHoverableContent
67+
/>
68+
) : (
69+
<span className="flex h-6 items-stretch">{renderBasicTagContent()}</span>
70+
);
71+
72+
return (
73+
<div
74+
className="group relative inline-flex"
75+
onMouseEnter={() => setIsHovered(true)}
76+
onMouseLeave={() => setIsHovered(false)}
77+
>
78+
{tagContent}
79+
<CopyButton textToCopy={tag} isHovered={isHovered} />
80+
</div>
81+
);
82+
}
83+
84+
function CopyButton({ textToCopy, isHovered }: { textToCopy: string; isHovered: boolean }) {
85+
const [copied, setCopied] = useState(false);
86+
87+
const copy = useCallback(
88+
(e: React.MouseEvent) => {
89+
e.preventDefault();
90+
e.stopPropagation();
91+
navigator.clipboard.writeText(textToCopy);
92+
setCopied(true);
93+
setTimeout(() => {
94+
setCopied(false);
95+
}, 1500);
96+
},
97+
[textToCopy]
98+
);
99+
100+
return (
101+
<SimpleTooltip
102+
button={
103+
<button
104+
onClick={copy}
105+
onMouseDown={(e) => e.stopPropagation()}
106+
className={cn(
107+
"absolute -right-6 top-0 z-10 flex size-6 items-center justify-center rounded-r-sm border-y border-r border-charcoal-650 bg-charcoal-750",
108+
isHovered ? "opacity-100" : "opacity-0",
109+
copied
110+
? "text-green-500"
111+
: "text-text-dimmed hover:border-charcoal-600 hover:bg-charcoal-700 hover:text-text-bright"
112+
)}
113+
>
114+
{copied ? (
115+
<ClipboardCheckIcon className="size-3.5" />
116+
) : (
117+
<ClipboardIcon className="size-3.5" />
118+
)}
119+
</button>
120+
}
121+
content={copied ? "Copied!" : "Copy tag"}
122+
disableHoverableContent
123+
/>
124+
);
125+
}

apps/webapp/app/components/runs/v3/TaskRunsTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ export function TaskRunsTable({
384384
{run.delayUntil ? <DateTime date={run.delayUntil} /> : "–"}
385385
</TableCell>
386386
<TableCell to={path}>{run.ttl ?? "–"}</TableCell>
387-
<TableCell to={path} actionClassName="py-1">
387+
<TableCell to={path} actionClassName="py-1" className="pr-16">
388388
<div className="flex gap-1">
389389
{run.tags.map((tag) => <RunTag key={tag} tag={tag} />) || "–"}
390390
</div>

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -690,14 +690,11 @@ function RunBody({
690690
) : (
691691
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs">
692692
{run.tags.map((tag: string) => (
693-
<SimpleTooltip
693+
<RunTag
694694
key={tag}
695-
button={
696-
<Link to={v3RunsPath(organization, project, { tags: [tag] })}>
697-
<RunTag tag={tag} />
698-
</Link>
699-
}
700-
content={`Filter runs by ${tag}`}
695+
tag={tag}
696+
to={v3RunsPath(organization, project, { tags: [tag] })}
697+
tooltip={`Filter runs by ${tag}`}
701698
/>
702699
))}
703700
</div>

0 commit comments

Comments
 (0)