mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-20 05:39:07 +02:00
add peer details
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
import { ComponentType, forwardRef, ReactNode } from "react";
|
||||
import { motion, HTMLMotionProps } from "framer-motion";
|
||||
import {
|
||||
ButtonHTMLAttributes,
|
||||
ComponentType,
|
||||
forwardRef,
|
||||
ReactNode,
|
||||
} from "react";
|
||||
import { LucideProps } from "lucide-react";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
type Props = HTMLMotionProps<"button"> & {
|
||||
type Props = ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
icon?: ComponentType<LucideProps>;
|
||||
iconNode?: ReactNode;
|
||||
title: string;
|
||||
@@ -28,10 +32,9 @@ export const CardNavItem = forwardRef<HTMLButtonElement, Props>(
|
||||
ref,
|
||||
) {
|
||||
return (
|
||||
<motion.button
|
||||
<button
|
||||
ref={ref}
|
||||
type={type}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 p-1.5 rounded-lg cursor-default outline-none text-left",
|
||||
"transition-colors duration-150",
|
||||
@@ -77,7 +80,7 @@ export const CardNavItem = forwardRef<HTMLButtonElement, Props>(
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</motion.button>
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ type CopyToClipboardProps = {
|
||||
size?: number;
|
||||
iconAlignment?: "left" | "right";
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
alwaysShowIcon?: boolean;
|
||||
};
|
||||
|
||||
@@ -17,6 +18,7 @@ export const CopyToClipboard = ({
|
||||
size = 10,
|
||||
iconAlignment = "right",
|
||||
className,
|
||||
iconClassName,
|
||||
alwaysShowIcon = false,
|
||||
}: CopyToClipboardProps) => {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
@@ -57,6 +59,7 @@ export const CopyToClipboard = ({
|
||||
className={cn(
|
||||
"shrink-0 inline-flex relative top-[2px] right-[1px]",
|
||||
iconAlignment === "left" ? "order-first" : "order-last",
|
||||
iconClassName,
|
||||
)}
|
||||
>
|
||||
<Check
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { ComponentType } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Browser } from "@wailsio/runtime";
|
||||
import { ExternalLinkIcon, LucideProps } from "lucide-react";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { SquareIcon } from "./SquareIcon";
|
||||
|
||||
type Props = {
|
||||
icon: ComponentType<LucideProps>;
|
||||
title: string;
|
||||
description?: string;
|
||||
learnMoreUrl?: string;
|
||||
learnMoreTopic?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const openUrl = (url: string) => {
|
||||
void Browser.OpenURL(url).catch(() => window.open(url, "_blank"));
|
||||
};
|
||||
|
||||
export const EmptyState = ({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
learnMoreUrl,
|
||||
learnMoreTopic,
|
||||
className,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={cn("py-12 text-center", className)}>
|
||||
<div
|
||||
className={
|
||||
"flex flex-col items-center justify-center max-w-sm mx-auto"
|
||||
}
|
||||
>
|
||||
<SquareIcon icon={icon} className={"mb-3"} />
|
||||
<p className={"text-base font-semibold text-nb-gray-200 mb-1"}>
|
||||
{title}
|
||||
</p>
|
||||
{description && (
|
||||
<p className={"text-sm text-nb-gray-350"}>{description}</p>
|
||||
)}
|
||||
{learnMoreUrl && learnMoreTopic && (
|
||||
<p className={"text-sm text-nb-gray-350"}>
|
||||
{t("common.learnMoreAbout")}{" "}
|
||||
<a
|
||||
href={learnMoreUrl}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
openUrl(learnMoreUrl);
|
||||
}}
|
||||
className={cn(
|
||||
"text-netbird hover:underline underline-offset-4",
|
||||
"cursor-pointer wails-no-draggable",
|
||||
"inline-flex items-center gap-1",
|
||||
)}
|
||||
>
|
||||
{learnMoreTopic}
|
||||
<ExternalLinkIcon
|
||||
size={12}
|
||||
className={"shrink-0"}
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
||||
import * as React from "react";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
const HoverCard = HoverCardPrimitive.Root;
|
||||
const HoverCardTrigger = HoverCardPrimitive.Trigger;
|
||||
const HoverCardPortal = HoverCardPrimitive.Portal;
|
||||
|
||||
const HoverCardContent = React.forwardRef<
|
||||
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||
>(({ className, align = "start", sideOffset = 6, ...props }, ref) => (
|
||||
<HoverCardPrimitive.Portal>
|
||||
<HoverCardPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-80 rounded-lg border border-nb-gray-900 bg-nb-gray-935",
|
||||
"p-3 text-nb-gray-200 shadow-lg outline-none",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
||||
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
||||
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</HoverCardPrimitive.Portal>
|
||||
));
|
||||
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
|
||||
|
||||
export { HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger };
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentType } from "react";
|
||||
import { FunnelXIcon, LucideProps } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { EmptyState } from "./EmptyState";
|
||||
|
||||
type Props = {
|
||||
icon?: ComponentType<LucideProps>;
|
||||
title?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export const NoResults = ({
|
||||
icon = FunnelXIcon,
|
||||
title,
|
||||
description,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<EmptyState
|
||||
icon={icon}
|
||||
title={title ?? t("common.noResults.title")}
|
||||
description={description ?? t("common.noResults.description")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { UnplugIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { EmptyState } from "./EmptyState";
|
||||
|
||||
export const NotConnectedState = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
"h-full min-h-[260px] flex items-center justify-center px-6"
|
||||
}
|
||||
>
|
||||
<EmptyState
|
||||
icon={UnplugIcon}
|
||||
title={t("notConnected.title")}
|
||||
description={t("notConnected.description")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,15 +1,24 @@
|
||||
import { forwardRef, InputHTMLAttributes } from "react";
|
||||
import { forwardRef, InputHTMLAttributes, ReactNode } from "react";
|
||||
import { SearchIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
type Props = InputHTMLAttributes<HTMLInputElement> & {
|
||||
iconSize?: number;
|
||||
shortcut?: ReactNode;
|
||||
};
|
||||
|
||||
export const SearchInput = forwardRef<HTMLInputElement, Props>(
|
||||
function SearchInput({ iconSize = 16, className, ...props }, ref) {
|
||||
function SearchInput(
|
||||
{ iconSize = 16, className, disabled, shortcut, ...props },
|
||||
ref,
|
||||
) {
|
||||
return (
|
||||
<div className={"flex items-center gap-2 px-1 h-10"}>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-1 h-10",
|
||||
disabled && "opacity-50",
|
||||
)}
|
||||
>
|
||||
<SearchIcon
|
||||
size={iconSize}
|
||||
className={"text-nb-gray-300 shrink-0"}
|
||||
@@ -17,13 +26,29 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(
|
||||
<input
|
||||
ref={ref}
|
||||
type={"text"}
|
||||
disabled={disabled}
|
||||
{...props}
|
||||
className={cn(
|
||||
"w-full bg-transparent text-sm text-nb-gray-200 placeholder:text-nb-gray-400",
|
||||
"outline-none border-none",
|
||||
disabled && "cursor-not-allowed",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
{shortcut && (
|
||||
<span
|
||||
className={cn(
|
||||
"shrink-0 select-none",
|
||||
"inline-flex items-center justify-center",
|
||||
"h-5 min-w-[20px] px-1.5 rounded",
|
||||
"border border-nb-gray-850 bg-nb-gray-920",
|
||||
"text-[10px] font-medium text-nb-gray-400",
|
||||
"wails-no-draggable",
|
||||
)}
|
||||
>
|
||||
{shortcut}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@ type SquareIconProps = {
|
||||
export const SquareIcon = ({ icon: Icon, iconSize = 20, className }: SquareIconProps) => (
|
||||
<div
|
||||
className={cn(
|
||||
"h-11 w-11 rounded-xl flex items-center justify-center bg-nb-gray-920 border border-nb-gray-900 text-white",
|
||||
"h-11 w-11 rounded-lg flex items-center justify-center bg-nb-gray-920 border border-nb-gray-900 text-white",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { cn } from "@/lib/cn";
|
||||
type SwitchItemGroupContextValue = {
|
||||
value: string;
|
||||
layoutId: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
const SwitchItemGroupContext = createContext<SwitchItemGroupContextValue | null>(null);
|
||||
@@ -22,18 +23,27 @@ type Props = {
|
||||
onChange: (value: string) => void;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SwitchItemGroup = ({ value, onChange, children, className }: Props) => {
|
||||
export const SwitchItemGroup = ({
|
||||
value,
|
||||
onChange,
|
||||
children,
|
||||
className,
|
||||
disabled = false,
|
||||
}: Props) => {
|
||||
const layoutId = useId();
|
||||
|
||||
return (
|
||||
<SwitchItemGroupContext.Provider value={{ value, layoutId }}>
|
||||
<SwitchItemGroupContext.Provider value={{ value, layoutId, disabled }}>
|
||||
<RadioGroup.Root
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
"flex shrink-0 rounded-lg border border-nb-gray-850 bg-nb-gray-910 p-1 overflow-hidden",
|
||||
disabled && "opacity-50 pointer-events-none",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user