From 2151ff51f05df4d765a72393ab1a6199ab0495ba Mon Sep 17 00:00:00 2001 From: Dmitriy Petrov Date: Fri, 7 Aug 2026 22:19:06 +0300 Subject: [PATCH] Polish diagnostics animations and device alias sizing --- src/shared/versions.js | 4 +- .../ConnectivityDiagnosticsPanel.jsx | 45 ++++++++++-- src/web/components/DevicesPanel.jsx | 8 +-- src/web/styles.css | 68 +++++++++++++++---- test/web/device-inventory-contract.test.js | 3 +- test/web/responsive-layout-contract.test.js | 7 +- 6 files changed, 106 insertions(+), 29 deletions(-) diff --git a/src/shared/versions.js b/src/shared/versions.js index 22ac138..8e17553 100644 --- a/src/shared/versions.js +++ b/src/shared/versions.js @@ -1,6 +1,6 @@ export const HARBOR_VERSIONS = Object.freeze({ - macClient: '0.17.6', - gatewayClient: '0.18.6', + macClient: '0.17.8', + gatewayClient: '0.18.8', gatewayBackend: '0.18.1', }); diff --git a/src/web/components/ConnectivityDiagnosticsPanel.jsx b/src/web/components/ConnectivityDiagnosticsPanel.jsx index b3173c8..76826f9 100644 --- a/src/web/components/ConnectivityDiagnosticsPanel.jsx +++ b/src/web/components/ConnectivityDiagnosticsPanel.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { api } from '../api.js'; import { assessConnectivity, @@ -38,7 +38,7 @@ function readCustomServices() { function resultStatus(site, pending, available = true) { if (!available) return ['is-muted', '—']; - if (pending) return ['is-running', 'Проверяем']; + if (pending) return ['is-running', 'Тестируем']; if (!site) return ['is-muted', '—']; if (site.status === 'unavailable') return ['is-error', 'Нет доступа']; if (site.status === 'responded') return ['is-warning', `HTTP ${site.httpStatus}`]; @@ -47,7 +47,10 @@ function resultStatus(site, pending, available = true) { function Status({ value, route }) { const [className, label] = value; - return {label}; + return + {label} + {className === 'is-running' && } + ; } function ipResult(path, source) { @@ -60,7 +63,7 @@ function ipResult(path, source) { function IpCell({ path, source, pending, route }) { const value = ipResult(path, source); if (path?.available === false) return ; - if (pending) return ; + if (pending) return ; if (!path?.available) return ; if (!value?.address) return ; return {value.address}; @@ -111,6 +114,9 @@ export function ConnectivityDiagnosticsPanel({ isGateway, open, panelRef, closeR const [serviceName, setServiceName] = useState(''); const [serviceUrl, setServiceUrl] = useState(''); const [formError, setFormError] = useState(''); + const sheetRef = useRef(null); + const runnerRef = useRef(null); + const previousTargetRef = useRef(null); useEffect(() => { try { @@ -120,6 +126,30 @@ export function ConnectivityDiagnosticsPanel({ isGateway, open, panelRef, closeR } }, [customServices]); + useLayoutEffect(() => { + const sheet = sheetRef.current; + const runner = runnerRef.current; + if (!sheet || !runner) return; + if (!activeTarget) { + runner.classList.remove('is-visible', 'is-moving'); + previousTargetRef.current = null; + return; + } + + const row = [...sheet.querySelectorAll('[data-diagnostic-target]')] + .find((item) => item.dataset.diagnosticTarget === activeTarget); + if (!row) return; + const rowRect = row.getBoundingClientRect(); + const sheetRect = sheet.getBoundingClientRect(); + runner.style.setProperty('--diagnostics-runner-x', `${rowRect.left - sheetRect.left}px`); + runner.style.setProperty('--diagnostics-runner-y', `${rowRect.top - sheetRect.top}px`); + runner.style.width = `${rowRect.width}px`; + runner.style.height = `${rowRect.height}px`; + runner.classList.toggle('is-moving', previousTargetRef.current !== null); + runner.classList.add('is-visible'); + previousTargetRef.current = activeTarget; + }, [activeTarget]); + async function run() { setStatus('running'); setError(null); @@ -182,7 +212,8 @@ export function ConnectivityDiagnosticsPanel({ isGateway, open, panelRef, closeR aria-hidden={!open} inert={!open ? true : undefined} > -
+
+