mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-14 04:39:54 +00:00
[client/ui] Replace fyne UI with Wails (rename ui-wails to ui)
Removes the legacy fyne-based client/ui implementation and renames the Wails replacement (client/ui-wails) to take its place at client/ui. Go imports, frontend bindings, CI workflows, goreleaser configs and the windows .syso icon path are updated to follow the rename.
This commit is contained in:
42
client/ui/frontend/src/components/Button.tsx
Normal file
42
client/ui/frontend/src/components/Button.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { ButtonHTMLAttributes, forwardRef } from "react";
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
type Variant = "primary" | "secondary" | "ghost" | "danger";
|
||||
type Size = "sm" | "md";
|
||||
|
||||
const variants: Record<Variant, string> = {
|
||||
primary: "bg-netbird text-white hover:bg-netbird-500 disabled:bg-nb-gray-300",
|
||||
secondary:
|
||||
"bg-nb-gray-100 text-nb-gray-900 hover:bg-nb-gray-200 dark:bg-nb-gray-900 dark:text-nb-gray-50 dark:hover:bg-nb-gray-800",
|
||||
ghost:
|
||||
"bg-transparent text-nb-gray-700 hover:bg-nb-gray-100 dark:text-nb-gray-200 dark:hover:bg-nb-gray-900",
|
||||
danger: "bg-red-600 text-white hover:bg-red-500",
|
||||
};
|
||||
|
||||
const sizes: Record<Size, string> = {
|
||||
sm: "h-7 px-2 text-xs",
|
||||
md: "h-9 px-3 text-sm",
|
||||
};
|
||||
|
||||
interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
}
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, Props>(function Button(
|
||||
{ variant = "primary", size = "md", className, ...rest },
|
||||
ref,
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60",
|
||||
variants[variant],
|
||||
sizes[size],
|
||||
className,
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
});
|
||||
14
client/ui/frontend/src/components/Card.tsx
Normal file
14
client/ui/frontend/src/components/Card.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { HTMLAttributes } from "react";
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
export function Card({ className, ...rest }: HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-lg border border-nb-gray-200 bg-white p-4 dark:border-nb-gray-800 dark:bg-nb-gray-925",
|
||||
className,
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
33
client/ui/frontend/src/components/Input.tsx
Normal file
33
client/ui/frontend/src/components/Input.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { InputHTMLAttributes, forwardRef } from "react";
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
interface Props extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, Props>(function Input(
|
||||
{ label, className, id, ...rest },
|
||||
ref,
|
||||
) {
|
||||
const inputId = id ?? label?.toLowerCase().replace(/\s+/g, "-");
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
{label && (
|
||||
<label htmlFor={inputId} className="text-xs font-medium text-nb-gray-600 dark:text-nb-gray-300">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<input
|
||||
id={inputId}
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"h-9 rounded-md border border-nb-gray-300 bg-white px-3 text-sm",
|
||||
"focus:border-netbird focus:outline-none focus:ring-1 focus:ring-netbird",
|
||||
"dark:border-nb-gray-700 dark:bg-nb-gray-925 dark:text-nb-gray-50",
|
||||
className,
|
||||
)}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
42
client/ui/frontend/src/components/Switch.tsx
Normal file
42
client/ui/frontend/src/components/Switch.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
interface Props {
|
||||
checked: boolean;
|
||||
onChange: (checked: boolean) => void;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export function Switch({ checked, onChange, disabled, label, description }: Props) {
|
||||
return (
|
||||
<label className={cn("flex items-start gap-3", disabled && "opacity-60")}>
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
disabled={disabled}
|
||||
onClick={() => onChange(!checked)}
|
||||
className={cn(
|
||||
"mt-0.5 inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors",
|
||||
checked ? "bg-netbird" : "bg-nb-gray-300 dark:bg-nb-gray-700",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-block h-4 w-4 transform rounded-full bg-white transition-transform",
|
||||
checked ? "translate-x-4" : "translate-x-0.5",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
{(label || description) && (
|
||||
<span className="flex flex-col">
|
||||
{label && <span className="text-sm font-medium">{label}</span>}
|
||||
{description && (
|
||||
<span className="text-xs text-nb-gray-500">{description}</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
40
client/ui/frontend/src/components/Tabs.tsx
Normal file
40
client/ui/frontend/src/components/Tabs.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { ReactNode, useState } from "react";
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
interface Tab {
|
||||
value: string;
|
||||
label: string;
|
||||
content: ReactNode;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
tabs: Tab[];
|
||||
initial?: string;
|
||||
}
|
||||
|
||||
export function Tabs({ tabs, initial }: Props) {
|
||||
const [active, setActive] = useState(initial ?? tabs[0]?.value);
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex shrink-0 gap-1 border-b border-nb-gray-200 dark:border-nb-gray-800">
|
||||
{tabs.map((t) => (
|
||||
<button
|
||||
key={t.value}
|
||||
onClick={() => setActive(t.value)}
|
||||
className={cn(
|
||||
"border-b-2 px-3 py-2 text-sm font-medium transition-colors",
|
||||
active === t.value
|
||||
? "border-netbird text-netbird"
|
||||
: "border-transparent text-nb-gray-500 hover:text-nb-gray-800 dark:hover:text-nb-gray-200",
|
||||
)}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
{tabs.find((t) => t.value === active)?.content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user