mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-27 06:49:07 +01:00
add general settings
This commit is contained in:
@@ -4,9 +4,7 @@ import { ButtonHTMLAttributes, forwardRef } from "react";
|
||||
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>;
|
||||
|
||||
export interface ButtonProps
|
||||
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
ButtonVariants {
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonVariants {
|
||||
disabled?: boolean;
|
||||
stopPropagation?: boolean;
|
||||
}
|
||||
@@ -26,7 +24,7 @@ export const buttonVariants = cva(
|
||||
"dark:focus:ring-zinc-800/50 dark:bg-nb-gray dark:text-gray-400 dark:border-gray-700/30 dark:hover:text-white dark:hover:bg-zinc-800/50",
|
||||
],
|
||||
primary: [
|
||||
"dark:focus:ring-netbird-600/50 dark:ring-offset-neutral-950/50 enabled:dark:bg-netbird disabled:dark:bg-nb-gray-910 dark:text-gray-100 enabled:dark:hover:text-white enabled:dark:hover:bg-netbird-500/80",
|
||||
"dark:focus:ring-netbird-600/50 dark:ring-offset-neutral-950/50 enabled:dark:bg-netbird disabled:dark:bg-nb-gray-900 dark:text-gray-100 enabled:dark:hover:text-white enabled:dark:hover:bg-netbird-500/80",
|
||||
"enabled:bg-netbird enabled:text-white enabled:focus:ring-netbird-400/50 enabled:hover:bg-netbird-500",
|
||||
],
|
||||
secondary: [
|
||||
@@ -88,9 +86,9 @@ export const buttonVariants = cva(
|
||||
size: {
|
||||
xs: "text-xs py-2 px-4",
|
||||
xs2: "text-[0.78rem] py-2 px-4",
|
||||
sm: "text-sm py-2.5 px-4",
|
||||
md: "text-md py-2.5 px-4",
|
||||
lg: "text-lg py-2.5 px-4",
|
||||
sm: "text-sm py-[9px] px-4",
|
||||
md: "text-md py-[9px] px-4",
|
||||
lg: "text-lg py-[9px] px-4",
|
||||
},
|
||||
rounded: {
|
||||
true: "rounded-md",
|
||||
@@ -105,47 +103,45 @@ export const buttonVariants = cva(
|
||||
},
|
||||
);
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
function Button(
|
||||
{
|
||||
variant = "default",
|
||||
rounded = true,
|
||||
border = 1,
|
||||
size = "md",
|
||||
stopPropagation = true,
|
||||
type = "button",
|
||||
children,
|
||||
className,
|
||||
onClick,
|
||||
disabled,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
className={classNames(
|
||||
buttonVariants({
|
||||
variant,
|
||||
rounded,
|
||||
border: border ? 1 : 0,
|
||||
size,
|
||||
}),
|
||||
className,
|
||||
)}
|
||||
onClick={(e) => {
|
||||
if (stopPropagation) e.stopPropagation();
|
||||
onClick?.(e);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
||||
{
|
||||
variant = "default",
|
||||
rounded = true,
|
||||
border = 1,
|
||||
size = "md",
|
||||
stopPropagation = true,
|
||||
type = "button",
|
||||
children,
|
||||
className,
|
||||
onClick,
|
||||
disabled,
|
||||
...props
|
||||
},
|
||||
);
|
||||
ref,
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
className={classNames(
|
||||
buttonVariants({
|
||||
variant,
|
||||
rounded,
|
||||
border: border ? 1 : 0,
|
||||
size,
|
||||
}),
|
||||
className,
|
||||
)}
|
||||
onClick={(e) => {
|
||||
if (stopPropagation) e.stopPropagation();
|
||||
onClick?.(e);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
export default Button;
|
||||
|
||||
@@ -50,7 +50,7 @@ export default function FancyToggleSwitch({
|
||||
role={"switch"}
|
||||
aria-checked={value}
|
||||
className={cn(
|
||||
"cursor-pointer transition-all duration-300 relative z-[1]",
|
||||
"cursor-default transition-all duration-300 relative z-[1]",
|
||||
"inline-block text-left w-full",
|
||||
disabled && "opacity-50 pointer-events-none",
|
||||
className,
|
||||
|
||||
@@ -10,7 +10,7 @@ type Props = {
|
||||
export const HelpText = ({ children, margin = true, className }: Props) => (
|
||||
<span
|
||||
className={cn(
|
||||
"text-[.8rem] dark:text-nb-gray-300 block font-light tracking-wide",
|
||||
"text-[.81rem] dark:text-nb-gray-300 block font-light tracking-wide",
|
||||
margin && "mb-2",
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cva, VariantProps } from "class-variance-authority";
|
||||
import { AlertCircle, Eye, EyeOff } from "lucide-react";
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
import {
|
||||
forwardRef,
|
||||
InputHTMLAttributes,
|
||||
@@ -93,9 +93,9 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
const suffix = passwordToggle || customSuffix;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-col w-full min-w-0">
|
||||
{label && <Label htmlFor={inputId}>{label}</Label>}
|
||||
<div className={cn("flex relative h-[42px]", maxWidthClass)}>
|
||||
<div className={cn("flex relative h-[40px] w-full", maxWidthClass)}>
|
||||
{customPrefix && (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -104,7 +104,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
? "error"
|
||||
: "default",
|
||||
}),
|
||||
"flex h-[42px] w-auto rounded-l-md bg-white px-3 py-2 text-sm",
|
||||
"flex h-[40px] w-auto rounded-l-md bg-white px-3 py-2 text-sm",
|
||||
"border items-center whitespace-nowrap",
|
||||
props.disabled && "opacity-40",
|
||||
prefixClassName,
|
||||
@@ -134,7 +134,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
inputVariants({
|
||||
variant: error ? "error" : variant,
|
||||
}),
|
||||
"flex h-[42px] w-full rounded-md bg-white px-3 py-2 text-sm",
|
||||
"flex h-[40px] w-full rounded-md bg-white px-3 py-2 text-sm",
|
||||
"file:bg-transparent file:text-sm file:font-medium file:border-0",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
||||
"disabled:cursor-not-allowed disabled:opacity-40",
|
||||
@@ -161,7 +161,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
</div>
|
||||
{error && (
|
||||
<span className="text-xs text-red-500 mt-2 inline-flex items-center gap-1">
|
||||
<AlertCircle size={13} />
|
||||
{error}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ComponentPropsWithoutRef, forwardRef, Ref } from "react";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium tracking-wider leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-1.5 inline-block dark:text-nb-gray-200 flex items-center gap-2",
|
||||
"text-sm font-medium tracking-wider leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-1.5 inline-block dark:text-nb-gray-100 flex items-center gap-2",
|
||||
);
|
||||
|
||||
type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||
import { motion } from "framer-motion";
|
||||
import { ReactNode } from "react";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { useSwitchItemGroup } from "@/components/SwitchItemGroup";
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const SwitchItem = ({ value, children }: Props) => {
|
||||
const { value: activeValue, layoutId } = useSwitchItemGroup();
|
||||
const active = activeValue === value;
|
||||
|
||||
return (
|
||||
<RadioGroup.Item
|
||||
value={value}
|
||||
className={cn(
|
||||
"relative inline-flex items-center justify-center gap-1 rounded-md px-3.5 py-2 text-xs font-semibold",
|
||||
"outline-none cursor-default",
|
||||
active
|
||||
? "text-nb-gray-100"
|
||||
: "text-nb-gray-400 hover:text-nb-gray-200 active:text-nb-gray-100",
|
||||
)}
|
||||
>
|
||||
{active && (
|
||||
<motion.span
|
||||
layoutId={layoutId}
|
||||
className={"absolute inset-0 rounded-md bg-nb-gray-800"}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 35 }}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={"relative inline-flex items-center justify-center gap-1"}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
</RadioGroup.Item>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||
import { createContext, ReactNode, useContext, useId } from "react";
|
||||
import { cn } from "@/lib/cn";
|
||||
|
||||
type SwitchItemGroupContextValue = {
|
||||
value: string;
|
||||
layoutId: string;
|
||||
};
|
||||
|
||||
const SwitchItemGroupContext = createContext<SwitchItemGroupContextValue | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
export const useSwitchItemGroup = () => {
|
||||
const ctx = useContext(SwitchItemGroupContext);
|
||||
if (!ctx) {
|
||||
throw new Error("SwitchItem must be used inside a SwitchItemGroup");
|
||||
}
|
||||
return ctx;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const SwitchItemGroup = ({ value, onChange, children, className }: Props) => {
|
||||
const layoutId = useId();
|
||||
|
||||
return (
|
||||
<SwitchItemGroupContext.Provider value={{ value, layoutId }}>
|
||||
<RadioGroup.Root
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
className={cn(
|
||||
"flex shrink-0 rounded-lg border border-nb-gray-850 bg-nb-gray-920 p-1",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</RadioGroup.Root>
|
||||
</SwitchItemGroupContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -16,15 +16,21 @@ const switchVariants = cva("", {
|
||||
variant: {
|
||||
default: [
|
||||
"dark:data-[state=checked]:bg-netbird dark:data-[state=unchecked]:bg-nb-gray-700",
|
||||
"dark:data-[state=checked]:hover:bg-netbird-500 dark:data-[state=unchecked]:hover:bg-nb-gray-600",
|
||||
"data-[state=checked]:bg-neutral-900 data-[state=unchecked]:bg-neutral-200",
|
||||
"data-[state=checked]:hover:bg-neutral-800 data-[state=unchecked]:hover:bg-neutral-300",
|
||||
],
|
||||
"red-green": [
|
||||
"dark:data-[state=checked]:bg-red-600 dark:data-[state=unchecked]:bg-nb-gray-700",
|
||||
"dark:data-[state=checked]:hover:bg-red-500 dark:data-[state=unchecked]:hover:bg-nb-gray-600",
|
||||
"data-[state=checked]:bg-red-500 data-[state=unchecked]:bg-red-200",
|
||||
"data-[state=checked]:hover:bg-red-400 data-[state=unchecked]:hover:bg-red-300",
|
||||
],
|
||||
red: [
|
||||
"dark:data-[state=checked]:bg-red-600 dark:data-[state=unchecked]:bg-nb-gray-700",
|
||||
"dark:data-[state=checked]:hover:bg-red-500 dark:data-[state=unchecked]:hover:bg-nb-gray-600",
|
||||
"data-[state=checked]:bg-red-500 data-[state=unchecked]:bg-red-200",
|
||||
"data-[state=checked]:hover:bg-red-400 data-[state=unchecked]:hover:bg-red-300",
|
||||
],
|
||||
},
|
||||
"thumb-size": {
|
||||
@@ -45,7 +51,7 @@ const ToggleSwitch = React.forwardRef<
|
||||
) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 dark:focus-visible:ring-neutral-300 dark:focus-visible:ring-offset-neutral-950",
|
||||
"peer inline-flex shrink-0 cursor-default items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50 dark:focus-visible:ring-neutral-300 dark:focus-visible:ring-offset-neutral-950",
|
||||
className,
|
||||
switchVariants({ size, variant }),
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user