Add delete strike animation for local rules
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 28s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-07-11 23:54:21 +03:00
parent 483fce55f3
commit fbbd6e40b4
4 changed files with 36 additions and 6 deletions

View File

@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({ export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.6.3', macClient: '0.6.4',
gatewayClient: '0.6.2', gatewayClient: '0.6.3',
gatewayBackend: '0.6.0', gatewayBackend: '0.6.0',
}); });

View File

@@ -387,7 +387,7 @@ function LocalRulesPanel({
const [status, statusLabel] = localRuleStatus(rule, savedRules, activeRules); const [status, statusLabel] = localRuleStatus(rule, savedRules, activeRules);
return ( return (
<div <div
className={`client-local-rule is-${status}${rule.enabled ? '' : ' is-disabled'}${rule.removing ? ' is-removing' : ''}`} className={`client-local-rule client-deletable-row is-${status}${rule.enabled ? '' : ' is-disabled'}${rule.removing ? ' is-removing' : ''}`}
key={rule._key} key={rule._key}
style={{ viewTransitionName: rule.removing ? 'none' : rule._key }} style={{ viewTransitionName: rule.removing ? 'none' : rule._key }}
inert={rule.removing ? true : undefined} inert={rule.removing ? true : undefined}

View File

@@ -955,7 +955,27 @@ p {
.client-local-rule.is-removing { .client-local-rule.is-removing {
pointer-events: none; pointer-events: none;
animation: client-local-rule-leave 360ms cubic-bezier(0.7, 0, 0.84, 0) both; animation: client-local-rule-leave 760ms cubic-bezier(0.7, 0, 0.84, 0) both;
}
.client-deletable-row::after {
content: '';
position: absolute;
left: 0;
right: 0;
top: 50%;
z-index: 6;
height: 2px;
background: oklch(0.67 0.24 27);
box-shadow: 0 0 4px oklch(0.67 0.24 27), 0 0 12px oklch(0.67 0.24 27 / 0.8);
opacity: 0;
pointer-events: none;
transform: scaleX(0);
transform-origin: left center;
}
.client-deletable-row.is-removing::after {
animation: client-delete-strike 760ms cubic-bezier(0.16, 1, 0.3, 1) both;
} }
.client-local-rule-enabled { .client-local-rule-enabled {
@@ -1271,8 +1291,14 @@ p {
} }
@keyframes client-local-rule-leave { @keyframes client-local-rule-leave {
from { opacity: 1; filter: blur(0); transform: translateX(0) scale(1); } 0%, 48% { opacity: 1; filter: blur(0); }
to { opacity: 0; filter: blur(8px); transform: translateX(18px) scale(0.97); } 100% { opacity: 0; filter: blur(8px); }
}
@keyframes client-delete-strike {
0% { opacity: 1; transform: scaleX(0); }
38%, 58% { opacity: 1; transform: scaleX(1); }
100% { opacity: 0; transform: scaleX(1); }
} }
@keyframes client-local-rules-notice { @keyframes client-local-rules-notice {
@@ -2961,6 +2987,7 @@ p {
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.client-operation-progress::before, .client-operation-progress::before,
.client-deletable-row::after,
.client-power, .client-power,
.client-power::before, .client-power::before,
.client-power::after, .client-power::after,

View File

@@ -19,6 +19,9 @@ test('rule editor add latency stays constant and dirty exits are guarded', () =>
assert.match(component, /Не сохранено/); assert.match(component, /Не сохранено/);
assert.match(component, /Ждёт перезапуска/); assert.match(component, /Ждёт перезапуска/);
assert.match(component, /Перезапустить VPN/); assert.match(component, /Перезапустить VPN/);
assert.match(component, /client-deletable-row/);
assert.match(styles, /\.client-deletable-row\.is-removing::after[\s\S]*client-delete-strike/);
assert.match(styles, /@keyframes client-delete-strike[\s\S]*scaleX\(0\)[\s\S]*scaleX\(1\)/);
assert.match(styles, /\.client-local-rule:has\(\.client-rule-type\.is-open\)[\s\S]*z-index: 5/); assert.match(styles, /\.client-local-rule:has\(\.client-rule-type\.is-open\)[\s\S]*z-index: 5/);
}); });