mirror of
https://github.com/Buriburizaem0n/nezha-dash-v1.git
synced 2026-09-19 09:40:14 +00:00
fix: simplify sorting UI by removing popover and integrating sort controls directly
This commit is contained in:
+56
-140
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user