mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-27 23:09:08 +01:00
[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>
This commit is contained in:
co-authored by
Alisdair MacLeod
mlsmaycon
Eduard Gert
Viktor Liu
Diego Noguês
Diego Noguês
Bethuel Mmbaga
Zoltan Papp
Ashley Mensah
parent
edce11b34d
commit
f53155562f
@@ -0,0 +1,145 @@
|
||||
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 };
|
||||
Reference in New Issue
Block a user