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 {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}
>
-