This commit is contained in:
Eduard Gert
2026-05-07 09:57:14 +02:00
parent 553be144b4
commit debb558aa3
22 changed files with 774 additions and 178 deletions

View File

@@ -0,0 +1,79 @@
import React from "react";
import { HelpText } from "@/components/HelpText";
import { Label } from "@/components/Label";
import { ToggleSwitch } from "@/components/ToggleSwitch";
import { cn } from "@/lib/cn";
interface Props {
value: boolean;
onChange: (value: boolean) => void;
helpText?: React.ReactNode;
label?: React.ReactNode;
children?: React.ReactNode;
disabled?: boolean;
dataCy?: string;
className?: string;
labelClassName?: string;
textWrapperClassName?: string;
}
export default function FancyToggleSwitch({
value,
onChange,
helpText,
label,
children,
disabled = false,
dataCy,
className,
labelClassName,
textWrapperClassName = "max-w-sm",
}: Readonly<Props>) {
const handleToggle = () => {
if (disabled) return;
onChange(!value);
};
const handleKeyDown = (event: React.KeyboardEvent) => {
if (disabled) return;
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
handleToggle();
}
};
return (
<div
onClick={handleToggle}
onKeyDown={handleKeyDown}
tabIndex={-1}
role={"switch"}
aria-checked={value}
className={cn(
"cursor-pointer transition-all duration-300 relative z-[1]",
"inline-block text-left w-full",
disabled && "opacity-50 pointer-events-none",
className,
)}
>
<div className={"flex justify-between gap-10"}>
<div className={cn(textWrapperClassName)}>
<Label className={labelClassName}>{label}</Label>
<HelpText margin={false}>{helpText}</HelpText>
</div>
<div className={"mt-2 pr-1"}>
<ToggleSwitch
checked={value}
onCheckedChange={onChange}
dataCy={dataCy}
/>
</div>
</div>
{children && value ? (
<div className="mt-4" onClick={(e) => e.stopPropagation()}>
{children}
</div>
) : null}
</div>
);
}

View File

@@ -9,6 +9,7 @@ type Props = HTMLMotionProps<"button"> & {
description?: string;
active?: boolean;
iconSize?: number;
iconBackground?: boolean;
};
export const NavItem = forwardRef<HTMLButtonElement, Props>(
@@ -19,6 +20,7 @@ export const NavItem = forwardRef<HTMLButtonElement, Props>(
description,
active = false,
iconSize = 15,
iconBackground = true,
className,
type = "button",
...props
@@ -40,21 +42,33 @@ export const NavItem = forwardRef<HTMLButtonElement, Props>(
)}
{...props}
>
<div
className={cn(
"h-9 w-9 rounded-md flex items-center justify-center shrink-0",
"transition-colors duration-150",
active ? "bg-nb-gray-800" : "bg-nb-gray-920",
)}
>
{iconBackground ? (
<div
className={cn(
"h-9 w-9 rounded-md flex items-center justify-center shrink-0",
"transition-colors duration-150",
active ? "bg-nb-gray-800" : "bg-nb-gray-920",
)}
>
<Icon
size={iconSize}
className={cn(
"transition-colors duration-150",
active
? "text-nb-gray-200"
: "text-nb-gray-400",
)}
/>
</div>
) : (
<Icon
size={iconSize}
className={cn(
"transition-colors duration-150",
"shrink-0 ml-2 transition-colors duration-150",
active ? "text-nb-gray-200" : "text-nb-gray-400",
)}
/>
</div>
)}
<div className={"min-w-0"}>
<h2
className={cn(

View File

@@ -1,7 +0,0 @@
export default function PlaceholderHeader() {
return (
<div
className="h-[36px] shrink-0 cursor-default wails-draggable"
/>
);
}

View File

@@ -124,9 +124,10 @@ export const ProfileSelector = ({ email = "" }: Props) => {
}
>
<div
className={
"h-7 w-7 flex items-center justify-center bg-nb-gray-900 rounded-md text-xs font-semibold"
}
className={cn(
"flex items-center justify-center bg-nb-gray-900 rounded-md text-xs font-semibold",
email ? "h-7 w-7" : "h-6 w-6",
)}
style={{ color: initialColor }}
>
{initial}

View File

@@ -0,0 +1,71 @@
"use client";
import * as SwitchPrimitives from "@radix-ui/react-switch";
import { cva, VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/cn";
type SwitchVariants = VariantProps<typeof switchVariants>;
const switchVariants = cva("", {
variants: {
size: {
default: "h-[24px] w-[44px]",
small: "h-[18px] w-[36px]",
},
variant: {
default: [
"dark:data-[state=checked]:bg-netbird dark:data-[state=unchecked]:bg-nb-gray-700",
"data-[state=checked]:bg-neutral-900 data-[state=unchecked]:bg-neutral-200",
],
"red-green": [
"dark:data-[state=checked]:bg-red-600 dark:data-[state=unchecked]:bg-nb-gray-700",
"data-[state=checked]:bg-red-500 data-[state=unchecked]:bg-red-200",
],
red: [
"dark:data-[state=checked]:bg-red-600 dark:data-[state=unchecked]:bg-nb-gray-700",
"data-[state=checked]:bg-red-500 data-[state=unchecked]:bg-red-200",
],
},
"thumb-size": {
default: "h-5 w-5 data-[state=checked]:translate-x-5",
small: "h-[14px] w-[14px] data-[state=checked]:translate-x-[17px]",
},
},
});
const ToggleSwitch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> &
SwitchVariants & { dataCy?: string }
>(
(
{ className, size = "default", variant = "default", dataCy, ...props },
ref,
) => (
<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",
className,
switchVariants({ size, variant }),
)}
{...props}
data-cy={dataCy}
onClick={(e) => {
e.stopPropagation();
props.onClick?.(e);
}}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
switchVariants({ "thumb-size": size }),
"pointer-events-none block rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=unchecked]:translate-x-0 dark:bg-white",
)}
/>
</SwitchPrimitives.Root>
),
);
ToggleSwitch.displayName = SwitchPrimitives.Root.displayName;
export { ToggleSwitch };