diff --git a/src/shared/versions.js b/src/shared/versions.js index 4bf279a..91f4547 100644 --- a/src/shared/versions.js +++ b/src/shared/versions.js @@ -1,6 +1,6 @@ export const HARBOR_VERSIONS = Object.freeze({ - macClient: '0.17.13', - gatewayClient: '0.18.13', + macClient: '0.17.14', + gatewayClient: '0.18.14', gatewayBackend: '0.18.1', }); diff --git a/src/web/components/ClientOverviewPage.jsx b/src/web/components/ClientOverviewPage.jsx index e929a92..e7018ad 100644 --- a/src/web/components/ClientOverviewPage.jsx +++ b/src/web/components/ClientOverviewPage.jsx @@ -1142,7 +1142,7 @@ export function ClientOverviewPage({ }} > Как использовать @@ -1162,9 +1162,9 @@ export function ClientOverviewPage({ }} > Устройства } @@ -1183,7 +1183,7 @@ export function ClientOverviewPage({ }} > Диагностика @@ -1201,8 +1201,8 @@ export function ClientOverviewPage({ > {gatewayDirect ? 'Локальные правила недоступны: сейчас работают правила Gateway' diff --git a/src/web/styles.css b/src/web/styles.css index 1fcbb4a..98d6456 100644 --- a/src/web/styles.css +++ b/src/web/styles.css @@ -1693,15 +1693,89 @@ p { filter: drop-shadow(0 0 7px color-mix(in oklch, var(--client-accent) 52%, transparent)); } -.client-instructions-toggle:hover svg, -.client-instructions-toggle:focus-visible svg { - animation: client-secondary-icon-motion 620ms cubic-bezier(0.16, 1, 0.3, 1); +.client-rail-info-ring, +.client-rail-device-primary, +.client-rail-device-secondary, +.client-rail-device-link, +.client-rail-diagnostics-wave, +.client-rail-rule-knob { + transform-box: fill-box; + transform-origin: center; } -@keyframes client-secondary-icon-motion { - 0%, 100% { transform: rotate(0) translateY(0); } - 34% { transform: rotate(-8deg) translateY(-1px); } - 68% { transform: rotate(5deg) translateY(0); } +.client-rail-info-ring, +.client-rail-device-link, +.client-rail-diagnostics-wave { + stroke-dasharray: 1; +} + +.client-instructions-toggle.is-open:not(.client-devices-toggle):not(.client-diagnostics-toggle) .client-rail-info-ring { + animation: client-rail-info-refill 680ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.client-devices-toggle.is-open .client-rail-device-primary { + animation: client-rail-device-left 680ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.client-devices-toggle.is-open .client-rail-device-secondary { + animation: client-rail-device-right 680ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.client-devices-toggle.is-open .client-rail-device-link { + animation: client-rail-device-link 680ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.client-diagnostics-toggle.is-open .client-rail-diagnostics-wave { + animation: client-rail-diagnostics-wave 760ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.client-local-rules-toggle.is-open .client-rail-rule-knob.is-top { + animation: client-rail-rule-top 680ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.client-local-rules-toggle.is-open .client-rail-rule-knob.is-bottom { + animation: client-rail-rule-bottom 680ms cubic-bezier(0.16, 1, 0.3, 1); +} + +@keyframes client-rail-info-refill { + from { opacity: 0.28; stroke-dashoffset: 1; } + to { opacity: 1; stroke-dashoffset: 0; } +} + +@keyframes client-rail-device-left { + 0%, 100% { transform: translate(0, 0); } + 45% { transform: translate(1.5px, -1px); } + 72% { transform: translate(0.5px, 0); } +} + +@keyframes client-rail-device-right { + 0%, 100% { transform: translate(0, 0); } + 45% { transform: translate(-1.5px, 1px); } + 72% { transform: translate(-0.5px, 0); } +} + +@keyframes client-rail-device-link { + from { opacity: 0.28; stroke-dashoffset: 1; } + to { opacity: 1; stroke-dashoffset: 0; } +} + +@keyframes client-rail-diagnostics-wave { + 0%, 100% { stroke-dashoffset: 0; transform: scaleY(1); } + 32% { stroke-dashoffset: -0.24; transform: scaleY(0.68); } + 68% { stroke-dashoffset: -0.58; transform: scaleY(1.32); } + 84% { stroke-dashoffset: -0.82; transform: scaleY(0.9); } +} + +@keyframes client-rail-rule-top { + 0%, 100% { transform: translateX(0); } + 42% { transform: translateX(3px); } + 72% { transform: translateX(-1px); } +} + +@keyframes client-rail-rule-bottom { + 0%, 100% { transform: translateX(0); } + 42% { transform: translateX(-3px); } + 72% { transform: translateX(1px); } } .client-local-rules-toggle:disabled { @@ -1735,10 +1809,6 @@ p { filter: drop-shadow(0 0 7px oklch(0.68 0.14 72 / 0.42)); } -.client-local-rules-toggle.is-open svg { - transform: rotate(90deg); -} - .client-instructions-toggle.is-open span, .client-local-rules-toggle.is-open span { opacity: 0; @@ -5021,7 +5091,9 @@ p { .client-drawer-close, .client-instructions-toggle, .client-instructions-toggle svg, + .client-instructions-toggle circle, .client-instructions-toggle path, + .client-instructions-toggle rect, .client-instructions-toggle span, .client-instruction-block, .client-instruction-reveal, @@ -5084,6 +5156,7 @@ p { .client-local-rules, .client-local-rules-toggle, .client-local-rules-toggle svg, + .client-local-rules-toggle circle, .client-local-rules-toggle span, .client-local-rule, .client-local-rule-enabled svg, diff --git a/test/web/responsive-layout-contract.test.js b/test/web/responsive-layout-contract.test.js index ccea331..716b331 100644 --- a/test/web/responsive-layout-contract.test.js +++ b/test/web/responsive-layout-contract.test.js @@ -112,8 +112,11 @@ test('secondary menus share one right rail and both drawers open from the right' assert.match(disabledRulesLabel, /opacity:\s*0/); assert.match(disabledRulesLabel, /filter:\s*blur\(5px\)/); assert.match(styles, /\.client-local-rules-toggle:disabled:hover span\s*\{[\s\S]*opacity:\s*1/); - assert.match(styles, /\.client-instructions-toggle:hover svg,[\s\S]*\.client-instructions-toggle:focus-visible svg \{[\s\S]*client-secondary-icon-motion 620ms/); - assert.match(styles, /@keyframes client-secondary-icon-motion[\s\S]*rotate\(-8deg\)[\s\S]*rotate\(5deg\)/); + assert.match(component, /client-rail-info-ring[\s\S]*client-rail-device-primary[\s\S]*client-rail-device-secondary[\s\S]*client-rail-device-link[\s\S]*client-rail-diagnostics-wave[\s\S]*client-rail-rule-knob is-top[\s\S]*client-rail-rule-knob is-bottom/); + assert.match(styles, /client-rail-info-refill[\s\S]*client-rail-device-left[\s\S]*client-rail-device-right[\s\S]*client-rail-device-link[\s\S]*client-rail-diagnostics-wave[\s\S]*client-rail-rule-top[\s\S]*client-rail-rule-bottom/); + assert.match(styles, /@keyframes client-rail-diagnostics-wave[\s\S]*scaleY\(0\.68\)[\s\S]*scaleY\(1\.32\)/); + assert.match(styles, /@keyframes client-rail-info-refill[\s\S]*stroke-dashoffset: 1[\s\S]*stroke-dashoffset: 0/); + assert.doesNotMatch(styles, /client-secondary-icon-motion|\.client-local-rules-toggle\.is-open svg\s*\{[^}]*rotate/); assert.match(rule('.client-drawer'), /inset:\s*0 0 0 auto/); assert.match(rule('.client-drawer'), /transform:\s*translateX\(104%\)/); assert.match(rule('.client-drawer'), /z-index:\s*50/); @@ -177,6 +180,9 @@ test('responsive motion has a reduced-motion fallback', () => { assert.match(reducedMotion, /\.client-power-section/); assert.match(reducedMotion, /\.client-form/); assert.match(reducedMotion, /\.harbor-brand/); + assert.match(reducedMotion, /\.client-instructions-toggle circle/); + assert.match(reducedMotion, /\.client-instructions-toggle rect/); + assert.match(reducedMotion, /\.client-local-rules-toggle circle/); assert.match(reducedMotion, /transition:\s*none/); assert.match(reducedMotion, /animation:\s*none/); });