mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-31 04:59:54 +00:00
add copy to clipboard
This commit is contained in:
81
client/ui/frontend/src/components/CopyToClipboard.tsx
Normal file
81
client/ui/frontend/src/components/CopyToClipboard.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useRef, useState, type ReactNode } from "react";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
type CopyToClipboardProps = {
|
||||
children: ReactNode;
|
||||
message?: string;
|
||||
size?: number;
|
||||
iconAlignment?: "left" | "right";
|
||||
className?: string;
|
||||
alwaysShowIcon?: boolean;
|
||||
};
|
||||
|
||||
export const CopyToClipboard = ({
|
||||
children,
|
||||
message,
|
||||
size = 10,
|
||||
iconAlignment = "right",
|
||||
className,
|
||||
alwaysShowIcon = false,
|
||||
}: CopyToClipboardProps) => {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleClick = async (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const text = message ?? wrapperRef.current?.innerText ?? "";
|
||||
if (!text) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 500);
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
"inline-flex gap-2 items-center group/copy cursor-pointer wails-no-draggable",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<span className={cn("relative truncate min-w-0")}>
|
||||
{children}
|
||||
<span
|
||||
className={
|
||||
"absolute bottom-0 left-0 right-0 border-b border-dashed border-transparent group-hover/copy:border-nb-gray-500 pointer-events-none"
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
"shrink-0 inline-flex relative top-[2px] right-[1px]",
|
||||
iconAlignment === "left" ? "order-first" : "order-last",
|
||||
)}
|
||||
>
|
||||
<Check
|
||||
size={size}
|
||||
className={cn(
|
||||
"text-nb-gray-100",
|
||||
!copied && "hidden",
|
||||
!alwaysShowIcon && !copied && "opacity-0",
|
||||
)}
|
||||
/>
|
||||
<Copy
|
||||
size={size}
|
||||
className={cn(
|
||||
"text-nb-gray-100 group-hover/copy:opacity-100",
|
||||
copied && "hidden",
|
||||
!alwaysShowIcon && "opacity-0",
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import { useStatus } from "@/modules/daemon-status/StatusContext.tsx";
|
||||
import { useProfile } from "@/modules/profile/ProfileContext.tsx";
|
||||
import { cn } from "@/lib/cn.ts";
|
||||
import { formatErrorMessage } from "@/lib/errors.ts";
|
||||
import { CopyToClipboard } from "@/components/CopyToClipboard";
|
||||
import netbirdFullLogo from "@/assets/logos/netbird-full.svg";
|
||||
|
||||
enum ConnectionState {
|
||||
@@ -323,22 +324,30 @@ export const ConnectionStatusSwitch = () => {
|
||||
>
|
||||
{t(STATUS_KEY[connState])}
|
||||
</h1>
|
||||
<p
|
||||
<CopyToClipboard
|
||||
message={fqdn}
|
||||
className={cn(
|
||||
"font-mono text-xs leading-tight min-h-[1em] text-nb-gray-300 mt-2 transition-opacity duration-300 wails-no-draggable",
|
||||
showLocal && fqdn ? "opacity-100" : "opacity-0",
|
||||
"min-h-[1em] transition-opacity duration-300",
|
||||
"relative left-2.5",
|
||||
showLocal && fqdn ? "opacity-100" : "opacity-0 pointer-events-none",
|
||||
)}
|
||||
>
|
||||
{fqdn || " "}
|
||||
</p>
|
||||
<p
|
||||
<span className={"font-mono text-xs leading-tight text-nb-gray-300"}>
|
||||
{fqdn || " "}
|
||||
</span>
|
||||
</CopyToClipboard>
|
||||
<CopyToClipboard
|
||||
message={ip}
|
||||
className={cn(
|
||||
"font-mono text-xs leading-tight min-h-[1em] text-nb-gray-300 mt-0.5 transition-opacity duration-300 wails-no-draggable",
|
||||
showLocal && ip ? "opacity-100" : "opacity-0",
|
||||
"min-h-[1em] transition-opacity duration-300 ",
|
||||
"relative left-2.5",
|
||||
showLocal && ip ? "opacity-100" : "opacity-0 pointer-events-none",
|
||||
)}
|
||||
>
|
||||
{ip || " "}
|
||||
</p>
|
||||
<span className={"font-mono text-xs leading-tight text-nb-gray-300"}>
|
||||
{ip || " "}
|
||||
</span>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { PeerStatus } from "@bindings/services/models.js";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { CopyToClipboard } from "@/components/CopyToClipboard";
|
||||
|
||||
const dotClass = (connStatus: string): string => {
|
||||
switch (connStatus) {
|
||||
@@ -17,40 +18,29 @@ export const PeersList = ({ data }: { data: PeerStatus[] }) => {
|
||||
const { t } = useTranslation();
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<div className={"py-12 text-center text-sm text-nb-gray-400"}>
|
||||
{t("peers.empty")}
|
||||
</div>
|
||||
<div className={"py-12 text-center text-sm text-nb-gray-400"}>{t("peers.empty")}</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className={"flex flex-col"}>
|
||||
{data.map((peer) => (
|
||||
<li
|
||||
key={peer.pubKey}
|
||||
className={"flex items-center gap-3 px-7 py-3 min-w-0"}
|
||||
>
|
||||
<li key={peer.pubKey} className={"flex items-center gap-3 px-7 py-3 min-w-0"}>
|
||||
<span
|
||||
className={cn(
|
||||
"h-2 w-2 rounded-full shrink-0",
|
||||
dotClass(peer.connStatus),
|
||||
)}
|
||||
className={cn("h-2 w-2 rounded-full shrink-0", dotClass(peer.connStatus))}
|
||||
title={peer.connStatus}
|
||||
/>
|
||||
<span
|
||||
className={
|
||||
"text-[0.81rem] font-medium text-nb-gray-100 truncate"
|
||||
}
|
||||
<CopyToClipboard message={peer.fqdn} className={"min-w-0 flex-1"}>
|
||||
<span className={"text-[0.81rem] font-medium text-nb-gray-100"}>
|
||||
{peer.fqdn}
|
||||
</span>
|
||||
</CopyToClipboard>
|
||||
<CopyToClipboard
|
||||
message={peer.ip}
|
||||
className={cn("ml-auto shrink-0", "relative left-2.5")}
|
||||
>
|
||||
{peer.fqdn}
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
"ml-auto text-xs font-mono text-nb-gray-400 shrink-0"
|
||||
}
|
||||
>
|
||||
{peer.ip}
|
||||
</span>
|
||||
<span className={"text-xs font-mono text-nb-gray-400"}>{peer.ip}</span>
|
||||
</CopyToClipboard>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user