Files
netbird/proxy/web/src/components/SegmentedTabs.tsx
Pascal Fischer f53155562f [management, reverse proxy] Add reverse proxy feature (#5291)
* implement reverse proxy


---------

Co-authored-by: Alisdair MacLeod <git@alisdairmacleod.co.uk>
Co-authored-by: mlsmaycon <mlsmaycon@gmail.com>
Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
Co-authored-by: Viktor Liu <viktor@netbird.io>
Co-authored-by: Diego Noguês <diego.sure@gmail.com>
Co-authored-by: Diego Noguês <49420+diegocn@users.noreply.github.com>
Co-authored-by: Bethuel Mmbaga <bethuelmbaga12@gmail.com>
Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
Co-authored-by: Ashley Mensah <ashleyamo982@gmail.com>
2026-02-13 19:37:43 +01:00

146 lines
3.2 KiB
TypeScript

import { cn } from "@/utils/helpers";
import { useState, useMemo, useCallback } from "react";
import { TabContext, useTabContext } from "./TabContext";
type TabsProps = {
value?: string;
defaultValue?: string;
onChange?: (value: string) => void;
children:
| React.ReactNode
| ((context: { value: string; onChange: (value: string) => void }) => React.ReactNode);
};
function SegmentedTabs({ value, defaultValue, onChange, children }: Readonly<TabsProps>) {
const [internalValue, setInternalValue] = useState(defaultValue ?? "");
const currentValue = value ?? internalValue;
const handleChange = useCallback((newValue: string) => {
if (value === undefined) {
setInternalValue(newValue);
}
onChange?.(newValue);
}, [value, onChange]);
const contextValue = useMemo(
() => ({ value: currentValue, onChange: handleChange }),
[currentValue, handleChange],
);
return (
<TabContext.Provider value={contextValue}>
<div>
{typeof children === "function"
? children({ value: currentValue, onChange: handleChange })
: children}
</div>
</TabContext.Provider>
);
}
function List({
children,
className,
}: Readonly<{
children: React.ReactNode;
className?: string;
}>) {
return (
<div
role="tablist"
className={cn(
"bg-nb-gray-930/70 p-1.5 flex justify-center gap-1 border-nb-gray-900",
className
)}
>
{children}
</div>
);
}
function Trigger({
children,
value,
disabled = false,
className,
selected,
onClick,
}: Readonly<{
children: React.ReactNode;
value: string;
disabled?: boolean;
className?: string;
selected?: boolean;
onClick?: () => void;
}>) {
const context = useTabContext();
const isSelected = selected ?? value === context.value;
let stateClassName = "";
if (isSelected) {
stateClassName = "bg-nb-gray-900 text-white";
} else if (!disabled) {
stateClassName = "text-nb-gray-400 hover:bg-nb-gray-900/50";
}
const handleClick = () => {
context.onChange(value);
onClick?.();
};
return (
<button
role="tab"
type="button"
disabled={disabled}
aria-selected={isSelected}
onClick={handleClick}
className={cn(
"px-4 py-2 text-sm rounded-md w-full transition-all cursor-pointer",
disabled && "opacity-30 cursor-not-allowed",
stateClassName,
className
)}
>
<div className="flex items-center w-full justify-center gap-2">
{children}
</div>
</button>
);
}
function Content({
children,
value,
className,
visible,
}: Readonly<{
children: React.ReactNode;
value: string;
className?: string;
visible?: boolean;
}>) {
const context = useTabContext();
const isVisible = visible ?? value === context.value;
if (!isVisible) return null;
return (
<div
role="tabpanel"
className={cn(
"bg-nb-gray-930/70 px-4 pt-4 pb-5 rounded-b-md border border-t-0 border-nb-gray-900",
className
)}
>
{children}
</div>
);
}
SegmentedTabs.List = List;
SegmentedTabs.Trigger = Trigger;
SegmentedTabs.Content = Content;
export { SegmentedTabs };