mirror of
https://github.com/Buriburizaem0n/nezha-dash-v1.git
synced 2026-09-19 09:40:14 +00:00
fix: remove @tanstack/react-virtual
This commit is contained in:
@@ -42,7 +42,6 @@
|
||||
"@tanstack/react-query": "5.101.0",
|
||||
"@tanstack/react-query-devtools": "5.101.0",
|
||||
"@tanstack/react-table": "8.21.3",
|
||||
"@tanstack/react-virtual": "^3.14.5",
|
||||
"@types/d3-geo": "3.1.0",
|
||||
"@types/luxon": "3.7.1",
|
||||
"class-variance-authority": "0.7.1",
|
||||
|
||||
Generated
-20
@@ -59,9 +59,6 @@ importers:
|
||||
'@tanstack/react-table':
|
||||
specifier: 8.21.3
|
||||
version: 8.21.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.14.5
|
||||
version: 3.14.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
'@types/d3-geo':
|
||||
specifier: 3.1.0
|
||||
version: 3.1.0
|
||||
@@ -1120,19 +1117,10 @@ packages:
|
||||
react: '>=16.8'
|
||||
react-dom: '>=16.8'
|
||||
|
||||
'@tanstack/react-virtual@3.14.5':
|
||||
resolution: {integrity: sha512-4EKRXh7zBLkbKbFmG3AUVkircuHd+7OdT1pocJSepxtfBd3qnrJgJ5rtPkRYyo9fmyVb2+pI2xPy5oYvMLQy6A==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@tanstack/table-core@8.21.3':
|
||||
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/virtual-core@3.17.3':
|
||||
resolution: {integrity: sha512-8Np/TFELpI0ySuJoVmjvOrQYXH/8sTX0Biv9szhFhY39xOdAAY+smrMxjxOum/ux3eM8MUJQsEJ0/R0UpvC8dw==}
|
||||
|
||||
'@testing-library/dom@10.4.1':
|
||||
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3031,16 +3019,8 @@ snapshots:
|
||||
react: 19.2.7
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
|
||||
'@tanstack/react-virtual@3.14.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.17.3
|
||||
react: 19.2.7
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
|
||||
'@tanstack/table-core@8.21.3': {}
|
||||
|
||||
'@tanstack/virtual-core@3.17.3': {}
|
||||
|
||||
'@testing-library/dom@10.4.1':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.29.7
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
import { useWindowVirtualizer } from "@tanstack/react-virtual";
|
||||
import { useEffect, useState } from "react";
|
||||
import ServerCard from "@/components/ServerCard";
|
||||
import ServerCardInline from "@/components/ServerCardInline";
|
||||
import type { NezhaServer } from "@/types/nezha-api";
|
||||
|
||||
type VirtualServerListProps = {
|
||||
now: number;
|
||||
servers: NezhaServer[];
|
||||
inline: boolean;
|
||||
};
|
||||
|
||||
const CARD_ROW_GAP = 8;
|
||||
const CARD_COLUMNS_BREAKPOINT = 768;
|
||||
|
||||
function useCardColumnCount(enabled: boolean) {
|
||||
const [columnCount, setColumnCount] = useState(() => {
|
||||
if (!enabled || typeof window === "undefined") return 1;
|
||||
return window.innerWidth >= CARD_COLUMNS_BREAKPOINT ? 2 : 1;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
setColumnCount(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const updateColumnCount = () => {
|
||||
setColumnCount(window.innerWidth >= CARD_COLUMNS_BREAKPOINT ? 2 : 1);
|
||||
};
|
||||
|
||||
updateColumnCount();
|
||||
window.addEventListener("resize", updateColumnCount);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", updateColumnCount);
|
||||
};
|
||||
}, [enabled]);
|
||||
|
||||
return columnCount;
|
||||
}
|
||||
|
||||
export default function VirtualServerList({
|
||||
inline,
|
||||
now,
|
||||
servers,
|
||||
}: VirtualServerListProps) {
|
||||
if (inline) {
|
||||
return <VirtualInlineServerList now={now} servers={servers} />;
|
||||
}
|
||||
|
||||
return <VirtualCardServerList now={now} servers={servers} />;
|
||||
}
|
||||
|
||||
function VirtualCardServerList({
|
||||
now,
|
||||
servers,
|
||||
}: {
|
||||
now: number;
|
||||
servers: NezhaServer[];
|
||||
}) {
|
||||
const columnCount = useCardColumnCount(true);
|
||||
const rowCount = Math.ceil(servers.length / columnCount);
|
||||
|
||||
const virtualizer = useWindowVirtualizer({
|
||||
count: rowCount,
|
||||
estimateSize: () => 150,
|
||||
gap: CARD_ROW_GAP,
|
||||
overscan: 6,
|
||||
});
|
||||
|
||||
const virtualItems = virtualizer.getVirtualItems();
|
||||
|
||||
return (
|
||||
<section
|
||||
className="relative mt-6 server-card-list"
|
||||
style={{ height: virtualizer.getTotalSize() }}
|
||||
>
|
||||
<div
|
||||
className="absolute left-0 top-0 w-full"
|
||||
style={{
|
||||
transform: `translateY(${virtualItems[0]?.start ?? 0}px)`,
|
||||
}}
|
||||
>
|
||||
{virtualItems.map((virtualRow) => {
|
||||
const rowStart = virtualRow.index * columnCount;
|
||||
const row = servers.slice(rowStart, rowStart + columnCount);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="grid grid-cols-1 gap-2 md:grid-cols-2"
|
||||
data-index={virtualRow.index}
|
||||
key={virtualRow.key}
|
||||
ref={virtualizer.measureElement}
|
||||
style={{
|
||||
paddingBottom: CARD_ROW_GAP,
|
||||
}}
|
||||
>
|
||||
{row.map((serverInfo) => (
|
||||
<ServerCard
|
||||
key={serverInfo.id}
|
||||
now={now}
|
||||
serverInfo={serverInfo}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function VirtualInlineServerList({
|
||||
now,
|
||||
servers,
|
||||
}: {
|
||||
now: number;
|
||||
servers: NezhaServer[];
|
||||
}) {
|
||||
const virtualizer = useWindowVirtualizer({
|
||||
count: servers.length,
|
||||
estimateSize: () => 86,
|
||||
gap: CARD_ROW_GAP,
|
||||
overscan: 8,
|
||||
});
|
||||
|
||||
const virtualItems = virtualizer.getVirtualItems();
|
||||
|
||||
return (
|
||||
<section
|
||||
className="relative mt-6 overflow-x-scroll scrollbar-hidden server-inline-list"
|
||||
style={{ height: virtualizer.getTotalSize() }}
|
||||
>
|
||||
<div
|
||||
className="absolute left-0 top-0 px-px py-px flex w-full flex-col"
|
||||
style={{
|
||||
transform: `translateY(${virtualItems[0]?.start ?? 0}px)`,
|
||||
}}
|
||||
>
|
||||
{virtualItems.map((virtualRow) => {
|
||||
const serverInfo = servers[virtualRow.index];
|
||||
if (!serverInfo) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-index={virtualRow.index}
|
||||
key={virtualRow.key}
|
||||
ref={virtualizer.measureElement}
|
||||
style={{ paddingBottom: CARD_ROW_GAP }}
|
||||
>
|
||||
<ServerCardInline now={now} serverInfo={serverInfo} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
+23
-6
@@ -13,9 +13,10 @@ import { useTranslation } from "react-i18next";
|
||||
import GlobalMap from "@/components/GlobalMap";
|
||||
import GroupSwitch from "@/components/GroupSwitch";
|
||||
import { Loader } from "@/components/loading/Loader";
|
||||
import ServerCard from "@/components/ServerCard";
|
||||
import ServerCardInline from "@/components/ServerCardInline";
|
||||
import ServerOverview from "@/components/ServerOverview";
|
||||
import { ServiceTracker } from "@/components/ServiceTracker";
|
||||
import VirtualServerList from "@/components/VirtualServerList";
|
||||
import { SORT_TYPES } from "@/context/sort-context";
|
||||
import { useSort } from "@/hooks/use-sort";
|
||||
import { useStatus } from "@/hooks/use-status";
|
||||
@@ -580,11 +581,27 @@ export default function Servers({
|
||||
{!hasServers ? (
|
||||
<ServerEmptyState />
|
||||
) : filteredServers.length > 0 ? (
|
||||
<VirtualServerList
|
||||
inline={inline === "1"}
|
||||
now={nezhaWsData.now}
|
||||
servers={filteredServers}
|
||||
/>
|
||||
inline === "1" ? (
|
||||
<section className="flex flex-col gap-2 overflow-x-scroll p-px scrollbar-hidden mt-6 server-inline-list">
|
||||
{filteredServers.map((serverInfo) => (
|
||||
<ServerCardInline
|
||||
key={serverInfo.id}
|
||||
now={nezhaWsData.now}
|
||||
serverInfo={serverInfo}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
) : (
|
||||
<section className="grid grid-cols-1 gap-2 md:grid-cols-2 mt-6 server-card-list">
|
||||
{filteredServers.map((serverInfo) => (
|
||||
<ServerCard
|
||||
key={serverInfo.id}
|
||||
now={nezhaWsData.now}
|
||||
serverInfo={serverInfo}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
)
|
||||
) : (
|
||||
<ServerEmptyState filtered />
|
||||
)}
|
||||
|
||||
@@ -424,7 +424,7 @@ describe("Servers page", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("virtualizes large server lists instead of rendering every card", () => {
|
||||
it("renders every card in large server lists without virtualization", () => {
|
||||
const servers = Array.from({ length: 2000 }, (_, index) =>
|
||||
createServer({ id: index + 1, name: `server-${index + 1}` }),
|
||||
);
|
||||
@@ -437,8 +437,7 @@ describe("Servers page", () => {
|
||||
expect(screen.getByTestId("server-overview")).toHaveTextContent(
|
||||
"2000:2000:0",
|
||||
);
|
||||
expect(screen.getAllByTestId("server-card").length).toBeLessThan(2000);
|
||||
expect(screen.getAllByTestId("server-card").length).toBeGreaterThan(0);
|
||||
expect(screen.getAllByTestId("server-card")).toHaveLength(2000);
|
||||
});
|
||||
|
||||
it("toggles map and service tracker controls when service data exists", async () => {
|
||||
|
||||
Reference in New Issue
Block a user