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": "5.101.0",
|
||||||
"@tanstack/react-query-devtools": "5.101.0",
|
"@tanstack/react-query-devtools": "5.101.0",
|
||||||
"@tanstack/react-table": "8.21.3",
|
"@tanstack/react-table": "8.21.3",
|
||||||
"@tanstack/react-virtual": "^3.14.5",
|
|
||||||
"@types/d3-geo": "3.1.0",
|
"@types/d3-geo": "3.1.0",
|
||||||
"@types/luxon": "3.7.1",
|
"@types/luxon": "3.7.1",
|
||||||
"class-variance-authority": "0.7.1",
|
"class-variance-authority": "0.7.1",
|
||||||
|
|||||||
Generated
-20
@@ -59,9 +59,6 @@ importers:
|
|||||||
'@tanstack/react-table':
|
'@tanstack/react-table':
|
||||||
specifier: 8.21.3
|
specifier: 8.21.3
|
||||||
version: 8.21.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
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':
|
'@types/d3-geo':
|
||||||
specifier: 3.1.0
|
specifier: 3.1.0
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
@@ -1120,19 +1117,10 @@ packages:
|
|||||||
react: '>=16.8'
|
react: '>=16.8'
|
||||||
react-dom: '>=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':
|
'@tanstack/table-core@8.21.3':
|
||||||
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
|
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
'@tanstack/virtual-core@3.17.3':
|
|
||||||
resolution: {integrity: sha512-8Np/TFELpI0ySuJoVmjvOrQYXH/8sTX0Biv9szhFhY39xOdAAY+smrMxjxOum/ux3eM8MUJQsEJ0/R0UpvC8dw==}
|
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -3031,16 +3019,8 @@ snapshots:
|
|||||||
react: 19.2.7
|
react: 19.2.7
|
||||||
react-dom: 19.2.7(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/table-core@8.21.3': {}
|
||||||
|
|
||||||
'@tanstack/virtual-core@3.17.3': {}
|
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.29.7
|
'@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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
+21
-4
@@ -13,9 +13,10 @@ import { useTranslation } from "react-i18next";
|
|||||||
import GlobalMap from "@/components/GlobalMap";
|
import GlobalMap from "@/components/GlobalMap";
|
||||||
import GroupSwitch from "@/components/GroupSwitch";
|
import GroupSwitch from "@/components/GroupSwitch";
|
||||||
import { Loader } from "@/components/loading/Loader";
|
import { Loader } from "@/components/loading/Loader";
|
||||||
|
import ServerCard from "@/components/ServerCard";
|
||||||
|
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 VirtualServerList from "@/components/VirtualServerList";
|
|
||||||
import { SORT_TYPES } from "@/context/sort-context";
|
import { 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";
|
||||||
@@ -580,11 +581,27 @@ export default function Servers({
|
|||||||
{!hasServers ? (
|
{!hasServers ? (
|
||||||
<ServerEmptyState />
|
<ServerEmptyState />
|
||||||
) : filteredServers.length > 0 ? (
|
) : filteredServers.length > 0 ? (
|
||||||
<VirtualServerList
|
inline === "1" ? (
|
||||||
inline={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}
|
now={nezhaWsData.now}
|
||||||
servers={filteredServers}
|
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 />
|
<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) =>
|
const servers = Array.from({ length: 2000 }, (_, index) =>
|
||||||
createServer({ id: index + 1, name: `server-${index + 1}` }),
|
createServer({ id: index + 1, name: `server-${index + 1}` }),
|
||||||
);
|
);
|
||||||
@@ -437,8 +437,7 @@ describe("Servers page", () => {
|
|||||||
expect(screen.getByTestId("server-overview")).toHaveTextContent(
|
expect(screen.getByTestId("server-overview")).toHaveTextContent(
|
||||||
"2000:2000:0",
|
"2000:2000:0",
|
||||||
);
|
);
|
||||||
expect(screen.getAllByTestId("server-card").length).toBeLessThan(2000);
|
expect(screen.getAllByTestId("server-card")).toHaveLength(2000);
|
||||||
expect(screen.getAllByTestId("server-card").length).toBeGreaterThan(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("toggles map and service tracker controls when service data exists", async () => {
|
it("toggles map and service tracker controls when service data exists", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user