3x-ui/frontend/src/components/Sparkline.css

31 lines
586 B
CSS
Raw Normal View History

refactor(frontend): port nodes to react+ts Step 4 of the planned vue->react migration. The nodes entry brings in the largest shared-infrastructure batch so far — every authenticated react page from here on can lean on these. New shared pieces (live alongside their .vue counterparts during coexistence): * hooks/useMediaQuery.ts — useState + resize listener * hooks/useWebSocket.ts — wraps WebSocketClient, subscribes on mount and unsubscribes on unmount. The underlying client is a single module-level instance so multiple components on the same page share one socket. * hooks/useNodes.ts — node list state + CRUD + probe/test, including the totals memo (online/offline/avgLatency) used by the summary card. applyNodesEvent is the entry point for the heartbeat-pushed list. * components/CustomStatistic.tsx — thin Statistic wrapper, prefix + suffix slots become props. * components/Sparkline.tsx — the SVG line chart with measured-width axis scaling, gradient fill, tooltip overlay, and per-instance gradient id from React.useId. ResizeObserver lifecycle is in useEffect; the math is unchanged. Pages: * NodesPage — wires hooks + WebSocket together, renders summary card + NodeList, hosts the form modal. Uses Modal.useModal() for the delete confirm so the dialog inherits ConfigProvider theming. * NodeList — desktop renders a Table with expandable history rows; mobile flips to a vertical card list whose actions live in a bottom-right Dropdown. The IP-blur eye toggle persists across both. * NodeFormModal — controlled form (useState object, single setForm per change). The reset-on-open effect computes the next state once and applies it with eslint-disable to satisfy the new react-hooks/set-state-in-effect rule on a legitimate pattern. * NodeHistoryPanel — polls /panel/api/nodes/history/{id}/{metric}/ {bucket} every 15s, renders cpu+mem sparklines side-by-side.
2026-05-21 19:34:46 +00:00
.sparkline-svg {
display: block;
width: 100%;
}
.sparkline-svg .cpu-grid-y-text,
.sparkline-svg .cpu-grid-x-text {
fill: rgba(0, 0, 0, 0.65);
}
.sparkline-svg .cpu-grid-text {
fill: rgba(0, 0, 0, 0.88);
}
body.dark .sparkline-svg .cpu-grid-y-text,
body.dark .sparkline-svg .cpu-grid-x-text {
fill: rgba(255, 255, 255, 0.85);
}
body.dark .sparkline-svg .cpu-grid-text {
fill: rgba(255, 255, 255, 0.95);
}
body.dark .sparkline-svg .cpu-grid-line {
stroke: rgba(255, 255, 255, 0.12);
}
body.dark .sparkline-svg .cpu-grid-h-line {
stroke: rgba(255, 255, 255, 0.35);
}