mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 11:39:57 +00:00
update exit node tab
This commit is contained in:
@@ -22,17 +22,32 @@ export const ExitNodes = () => {
|
||||
searchRef.current?.focus();
|
||||
}, []);
|
||||
|
||||
// Initial order: active-first, then by id. After that, positions are sticky
|
||||
// — toggling a row doesn't move it. Mirrors the networks-list behavior so
|
||||
// the optimistic radio flip paints in place instead of the row jumping to
|
||||
// the top.
|
||||
const orderRef = useRef<string[]>([]);
|
||||
const ordered = useMemo(() => {
|
||||
const byId = new Map(exitNodes.map((n) => [n.id, n]));
|
||||
const kept = orderRef.current.filter((id) => byId.has(id));
|
||||
const known = new Set(kept);
|
||||
const fresh = exitNodes
|
||||
.filter((n) => !known.has(n.id))
|
||||
.sort((a, b) => {
|
||||
if (a.selected !== b.selected) return a.selected ? -1 : 1;
|
||||
return a.id.localeCompare(b.id);
|
||||
})
|
||||
.map((n) => n.id);
|
||||
const next = [...kept, ...fresh];
|
||||
orderRef.current = next;
|
||||
return next.map((id) => byId.get(id)!);
|
||||
}, [exitNodes]);
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const q = search.trim().toLowerCase();
|
||||
const matches = exitNodes.filter((r) => {
|
||||
if (!q) return true;
|
||||
return r.id.toLowerCase().includes(q);
|
||||
});
|
||||
return matches.sort((a, b) => {
|
||||
if (a.selected !== b.selected) return a.selected ? -1 : 1;
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
}, [exitNodes, search]);
|
||||
if (!q) return ordered;
|
||||
return ordered.filter((r) => r.id.toLowerCase().includes(q));
|
||||
}, [ordered, search]);
|
||||
|
||||
if (isConnected && exitNodes.length === 0) {
|
||||
return (
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ExitNodesList = ({ data, onToggle }: Props) => {
|
||||
onValueChange={handleChange}
|
||||
className={"flex flex-col"}
|
||||
>
|
||||
<Row value={NONE_VALUE} label={t("exitNodes.none")} />
|
||||
<Row value={NONE_VALUE} label={t("exitNodes.none")} first />
|
||||
{data.map((n) => (
|
||||
<Row key={n.id} value={n.id} label={n.id} />
|
||||
))}
|
||||
@@ -41,17 +41,26 @@ export const ExitNodesList = ({ data, onToggle }: Props) => {
|
||||
type RowProps = {
|
||||
value: string;
|
||||
label: string;
|
||||
first?: boolean;
|
||||
};
|
||||
|
||||
const Row = ({ value, label }: RowProps) => (
|
||||
const Row = ({ value, label, first }: RowProps) => (
|
||||
<RadioGroup.Item
|
||||
value={value}
|
||||
className={cn(
|
||||
"group flex items-center gap-3 px-7 py-3 min-w-0 text-left outline-none",
|
||||
"cursor-pointer wails-no-draggable",
|
||||
"hover:bg-nb-gray-900/40",
|
||||
"group flex items-center gap-2.5 pl-6 pr-8 py-3 min-w-0 w-full",
|
||||
first && "mt-2",
|
||||
"hover:bg-nb-gray-900/40 transition-colors",
|
||||
"wails-no-draggable cursor-pointer outline-none text-left",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={
|
||||
"min-w-0 flex-1 text-[0.81rem] font-medium text-nb-gray-100 truncate"
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
"h-4 w-4 shrink-0 rounded-full border",
|
||||
@@ -64,12 +73,5 @@ const Row = ({ value, label }: RowProps) => (
|
||||
className={"h-2 w-2 rounded-full bg-white"}
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
"text-[0.81rem] font-medium text-nb-gray-100 truncate min-w-0 flex-1"
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</RadioGroup.Item>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user