fix: simplify sorting UI by removing popover and integrating sort controls directly

This commit is contained in:
hamster1963
2026-06-01 15:07:55 +08:00
parent eac9d6a245
commit f323f4248b
+56 -140
View File
@@ -3,7 +3,6 @@ import {
ArrowsUpDownIcon, ArrowsUpDownIcon,
ArrowUpIcon, ArrowUpIcon,
ChartBarSquareIcon, ChartBarSquareIcon,
CheckIcon,
MapIcon, MapIcon,
ViewColumnsIcon, ViewColumnsIcon,
} from "@heroicons/react/20/solid"; } from "@heroicons/react/20/solid";
@@ -17,12 +16,7 @@ import ServerCard from "@/components/ServerCard";
import ServerCardInline from "@/components/ServerCardInline"; import ServerCardInline from "@/components/ServerCardInline";
import ServerOverview from "@/components/ServerOverview"; import ServerOverview from "@/components/ServerOverview";
import { ServiceTracker } from "@/components/ServiceTracker"; import { ServiceTracker } from "@/components/ServiceTracker";
import { import { SORT_TYPES } from "@/context/sort-context";
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { SORT_ORDERS, SORT_TYPES } from "@/context/sort-context";
import { useSort } from "@/hooks/use-sort"; import { useSort } from "@/hooks/use-sort";
import { useStatus } from "@/hooks/use-status"; import { useStatus } from "@/hooks/use-status";
import { useWebSocketContext } from "@/hooks/use-websocket-context"; import { useWebSocketContext } from "@/hooks/use-websocket-context";
@@ -43,7 +37,6 @@ export default function Servers() {
const [showMap, setShowMap] = useState<string>("0"); const [showMap, setShowMap] = useState<string>("0");
const [inline, setInline] = useState<string>("0"); const [inline, setInline] = useState<string>("0");
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [settingsOpen, setSettingsOpen] = useState<boolean>(false);
const [currentGroup, setCurrentGroup] = useState<string>("All"); const [currentGroup, setCurrentGroup] = useState<string>("All");
const customBackgroundImage = const customBackgroundImage =
@@ -51,12 +44,6 @@ export default function Servers() {
? window.CustomBackgroundImage ? window.CustomBackgroundImage
: undefined; : undefined;
const currentSortTypeLabel =
sortType === "default"
? "Default"
: sortType.charAt(0).toUpperCase() + sortType.slice(1);
const _currentSortOrderLabel = sortOrder === "asc" ? "Asc" : "Desc";
const restoreScrollPosition = useCallback(() => { const restoreScrollPosition = useCallback(() => {
const savedPosition = sessionStorage.getItem("scrollPosition"); const savedPosition = sessionStorage.getItem("scrollPosition");
if (savedPosition && containerRef.current) { if (savedPosition && containerRef.current) {
@@ -363,133 +350,62 @@ export default function Servers() {
setCurrentTab={handleTagChange} setCurrentTab={handleTagChange}
/> />
</section> </section>
<Popover onOpenChange={setSettingsOpen}> <div
<PopoverTrigger asChild> className={cn(
<button "flex h-8 items-center rounded-full border border-stone-200 bg-white text-sm text-stone-600 shadow-xs transition-all dark:border-stone-700 dark:bg-stone-800 dark:text-stone-300 dark:shadow-none shrink-0",
aria-label="Open sort settings" {
className={cn( "dark:border-stone-600/80 dark:bg-stone-800/80 bg-white/75":
"flex h-8 items-center gap-1.5 rounded-full border border-stone-200 bg-white px-3 text-sm text-stone-600 shadow-xs transition-all dark:border-stone-700 dark:bg-stone-800 dark:text-stone-300 dark:shadow-none", customBackgroundImage,
{ },
"shadow-inner": settingsOpen, {
}, "border-blue-400/90 text-blue-600 dark:border-blue-400/70 dark:text-blue-400":
{ sortType !== "default",
"dark:border-stone-600/80 dark:bg-stone-800/80 bg-white/75": },
customBackgroundImage, )}
}, >
{ <button
"border-blue-400/90 text-blue-600 dark:border-blue-400/70 dark:text-blue-400": aria-label="Toggle sort direction"
sortType !== "default", onClick={() => setSortOrder(sortOrder === "asc" ? "desc" : "asc")}
}, disabled={sortType === "default"}
)} className="flex h-full cursor-pointer items-center gap-1.5 px-3 disabled:cursor-not-allowed disabled:opacity-40"
>
{sortOrder === "asc" && sortType !== "default" ? (
<ArrowUpIcon className="size-3.5 shrink-0" />
) : sortOrder === "desc" && sortType !== "default" ? (
<ArrowDownIcon className="size-3.5 shrink-0" />
) : (
<ArrowsUpDownIcon className="size-3.5 shrink-0" />
)}
<span className="font-medium text-stone-900 dark:text-stone-100">
Sort
</span>
<span className="text-stone-300 dark:text-stone-600">|</span>
<span className="font-medium whitespace-nowrap">
{sortType === "default" ? "System" : currentSortTypeLabel}
</span>
</button>
</PopoverTrigger>
<PopoverContent
align="end"
className="w-72 overflow-hidden rounded-2xl p-0 shadow-xl"
> >
<div className="border-b border-stone-200 bg-linear-to-br from-blue-50 to-white p-3 dark:border-stone-700 dark:from-stone-800 dark:to-stone-900"> {sortOrder === "asc" && sortType !== "default" ? (
<div className="flex items-start justify-between gap-3"> <ArrowUpIcon className="size-3.5 shrink-0" />
<div> ) : sortOrder === "desc" && sortType !== "default" ? (
<p className="text-xs font-semibold text-stone-800 dark:text-stone-100"> <ArrowDownIcon className="size-3.5 shrink-0" />
Sort Servers ) : (
</p> <ArrowsUpDownIcon className="size-3.5 shrink-0" />
<p className="text-[10px] text-stone-500 dark:text-stone-400"> )}
Choose metric and direction <span className="font-medium text-stone-900 dark:text-stone-100">
</p> Sort
</div> </span>
<button </button>
onClick={() => { <span className="text-stone-300 dark:text-stone-600 mb-0.5">|</span>
setSortType("default"); <span className="relative ml-2 mr-3.25 inline-flex items-center">
setSortOrder("desc"); <span
}} className="pointer-events-none select-none opacity-0 text-sm font-medium whitespace-nowrap"
className="rounded-full border border-stone-300 px-2 py-1 text-[10px] font-medium text-stone-600 transition-colors hover:bg-stone-100 dark:border-stone-600 dark:text-stone-300 dark:hover:bg-stone-700" aria-hidden
> >
Reset {sortType.charAt(0).toUpperCase() + sortType.slice(1)}
</button> </span>
</div> <select
</div> aria-label="Sort metric"
<div className="space-y-3 p-3"> value={sortType}
<div className="space-y-2"> onChange={(e) => {
<p className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground"> const val = e.target.value as typeof sortType;
Metric setSortType(val);
</p> if (val === "default") setSortOrder("desc");
<div className="flex flex-wrap gap-1.5"> }}
{SORT_TYPES.map((type) => { className="absolute inset-0 cursor-pointer appearance-none bg-transparent text-sm font-medium outline-none"
const isActive = sortType === type; >
const label = {SORT_TYPES.map((type) => (
type === "default" <option key={type} value={type}>
? "Default" {type.charAt(0).toUpperCase() + type.slice(1)}
: type.charAt(0).toUpperCase() + type.slice(1); </option>
))}
return ( </select>
<button </span>
key={type} </div>
onClick={() => {
setSortType(type);
if (type === "default") {
setSortOrder("desc");
}
}}
className={cn(
"inline-flex items-center gap-1 rounded-full border px-2 py-1 text-[10px] font-medium transition-all",
isActive
? "border-blue-500 bg-blue-500 text-white shadow-sm"
: "border-stone-200 bg-stone-50 text-stone-600 hover:border-stone-300 hover:bg-stone-100 dark:border-stone-700 dark:bg-stone-800 dark:text-stone-300 dark:hover:bg-stone-700",
)}
>
{isActive && <CheckIcon className="size-3" />}
{label}
</button>
);
})}
</div>
</div>
<div className="space-y-2">
<p className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
Direction
</p>
<div className="grid grid-cols-2 gap-1.5 rounded-full bg-stone-100 p-1 dark:bg-stone-800">
{SORT_ORDERS.map((order) => (
<button
key={order}
onClick={() => setSortOrder(order)}
disabled={sortType === "default"}
className={cn(
"flex items-center justify-center gap-1 rounded-lg px-2 py-1.5 text-[10px] font-semibold transition-all",
sortOrder === order && sortType !== "default"
? "bg-white text-blue-600 shadow-sm dark:bg-stone-900 dark:text-blue-300"
: "text-stone-500 hover:text-stone-800 dark:text-stone-400 dark:hover:text-stone-100",
"disabled:cursor-not-allowed disabled:opacity-40",
)}
>
{order === "asc" ? (
<ArrowUpIcon className="size-3" />
) : (
<ArrowDownIcon className="size-3" />
)}
{order === "asc" ? "Ascending" : "Descending"}
</button>
))}
</div>
</div>
</div>
</PopoverContent>
</Popover>
</div> </div>
{showMap === "1" && ( {showMap === "1" && (
<GlobalMap <GlobalMap