chore: compact traffic cards

This commit is contained in:
3252a8
2026-05-28 21:52:38 +03:00
parent 2f192447ce
commit e998b9ddd1
3 changed files with 257 additions and 74 deletions
+1
View File
@@ -12,6 +12,7 @@ export {
ChevronRight, ChevronRight,
ChevronsUpDown, ChevronsUpDown,
Circle, Circle,
CircleQuestionMark,
CircleX, CircleX,
Coins, Coins,
Copy, Copy,
+165 -9
View File
@@ -360,6 +360,58 @@ a {
font-weight: 850; font-weight: 850;
} }
.traffic-card-compact {
display: grid;
}
.traffic-card-compact .progress {
margin-top: 8px;
}
.traffic-summary-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
gap: 8px;
min-width: 0;
line-height: 1.15;
}
.traffic-summary-left,
.traffic-summary-right {
align-items: baseline;
min-width: 0;
gap: 5px;
}
.traffic-summary-left {
display: block;
overflow: hidden;
color: var(--muted);
font-size: 11px;
text-overflow: ellipsis;
white-space: nowrap;
}
.traffic-summary-right {
display: inline-flex;
justify-content: flex-end;
color: var(--text);
font-size: 11px;
font-weight: 850;
text-align: right;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.traffic-summary-separator {
color: color-mix(in srgb, var(--muted) 58%, transparent);
}
.traffic-summary-right .traffic-summary-separator {
flex: 0 0 auto;
}
.card-heading { .card-heading {
margin: 0 0 10px; margin: 0 0 10px;
color: var(--text); color: var(--text);
@@ -412,6 +464,9 @@ a {
.traffic-card-depleted .traffic-top span, .traffic-card-depleted .traffic-top span,
.traffic-card-depleted .traffic-top strong, .traffic-card-depleted .traffic-top strong,
.traffic-card-depleted .traffic-summary-left,
.traffic-card-depleted .traffic-summary-right,
.traffic-card-depleted .traffic-summary-separator,
.traffic-card-depleted .traffic-meta, .traffic-card-depleted .traffic-meta,
.traffic-card-depleted .traffic-percent { .traffic-card-depleted .traffic-percent {
color: var(--danger-text); color: var(--danger-text);
@@ -456,6 +511,7 @@ a {
.premium-traffic-meta { .premium-traffic-meta {
align-items: flex-start; align-items: flex-start;
margin-top: 5px;
} }
.premium-server-dropdown { .premium-server-dropdown {
@@ -464,23 +520,57 @@ a {
min-width: 0; min-width: 0;
} }
.premium-server-dropdown summary { .premium-server-dropdown-inline {
display: inline-flex; display: grid;
align-items: center;
gap: 6px;
min-width: 0; min-width: 0;
min-height: 32px; }
margin: -6px -8px;
padding: 6px 8px; .premium-server-dropdown summary {
min-width: 0;
min-height: 26px;
margin: -4px -7px 0;
padding: 4px 7px;
border-radius: 999px;
cursor: pointer; cursor: pointer;
list-style: none; list-style: none;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
transition:
background 0.16s ease,
color 0.16s ease;
}
.premium-server-dropdown-inline summary {
display: grid;
max-width: 100%;
min-height: 0;
margin: 0;
padding: 0;
}
.premium-server-summary {
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
gap: 8px;
}
.premium-summary-trigger {
display: inline-flex;
align-items: center;
min-width: 0;
gap: 6px;
overflow: hidden;
white-space: nowrap;
} }
.premium-server-dropdown summary:focus { .premium-server-dropdown summary:focus {
outline: none; outline: none;
} }
.premium-server-dropdown summary:focus-visible {
background: color-mix(in srgb, var(--accent) 12%, transparent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 22%, transparent);
}
.premium-server-dropdown summary::-webkit-details-marker { .premium-server-dropdown summary::-webkit-details-marker {
display: none; display: none;
} }
@@ -491,13 +581,64 @@ a {
white-space: nowrap; white-space: nowrap;
} }
.premium-summary-copy {
display: block;
}
.premium-summary-copy {
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.premium-server-dropdown svg { .premium-server-dropdown svg {
flex: 0 0 auto; flex: 0 0 auto;
opacity: 0.72; opacity: 0.82;
}
.premium-server-help-icon {
padding: 1px;
border-radius: 999px;
color: var(--muted);
background: color-mix(in srgb, var(--muted) 10%, transparent);
transition:
color 0.16s ease,
background 0.16s ease,
transform 0.18s ease;
}
.premium-server-dropdown summary:hover .premium-server-help-icon,
.premium-server-dropdown[open] .premium-server-help-icon {
color: var(--accent);
background: color-mix(in srgb, var(--accent) 14%, transparent);
} }
.premium-server-list-dropdown { .premium-server-list-dropdown {
margin-top: 8px; display: grid;
grid-template-rows: 0fr;
margin-top: 0;
opacity: 0;
transform: translateY(-2px);
transform-origin: top;
transition:
grid-template-rows 0.22s ease,
margin-top 0.22s ease,
opacity 0.18s ease,
transform 0.22s ease;
}
.premium-server-dropdown[open] .premium-server-list-dropdown {
grid-template-rows: 1fr;
margin-top: 6px;
opacity: 1;
transform: translateY(0);
}
.premium-server-list-dropdown > div {
min-height: 0;
overflow: hidden;
} }
.premium-detail-grid { .premium-detail-grid {
@@ -2466,6 +2607,15 @@ a {
--screen-gutter: 10px; --screen-gutter: 10px;
} }
.traffic-summary-row {
gap: 6px;
}
.traffic-summary-left,
.traffic-summary-right {
font-size: 10px;
}
.period-grid, .period-grid,
.method-grid { .method-grid {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -2496,6 +2646,12 @@ a {
.motion-height-stage.motion-height-locked { .motion-height-stage.motion-height-locked {
transition: none !important; transition: none !important;
} }
.premium-server-dropdown summary,
.premium-server-help-icon,
.premium-server-list-dropdown {
transition: none !important;
}
} }
@supports not (color: color-mix(in srgb, #000 50%, #fff)) { @supports not (color: color-mix(in srgb, #000 50%, #fff)) {
+73 -47
View File
@@ -1,7 +1,7 @@
<script> <script>
import { import {
CheckCircle2, CheckCircle2,
ChevronsUpDown, CircleQuestionMark,
CircleX, CircleX,
CreditCard, CreditCard,
Database, Database,
@@ -58,6 +58,7 @@
} }
function regularTrafficCardClass(sub = subscription) { function regularTrafficCardClass(sub = subscription) {
return [ return [
"traffic-card-compact",
regularTrafficTopupBarClickable ? "traffic-card-clickable" : "", regularTrafficTopupBarClickable ? "traffic-card-clickable" : "",
regularTrafficDepleted(sub) ? "traffic-card-depleted" : "", regularTrafficDepleted(sub) ? "traffic-card-depleted" : "",
] ]
@@ -79,6 +80,11 @@
function premiumTitle(sub = subscription) { function premiumTitle(sub = subscription) {
return premiumTitleFn(sub, t); return premiumTitleFn(sub, t);
} }
function premiumTrafficMetaLabel(sub = subscription) {
return sub?.premium_is_limited
? t("wa_premium_access_limited", {}, "Доступ к premium временно ограничен")
: t("wa_premium_reset_monthly", {}, "Отдельный лимит на месяц");
}
function premiumServerLabels(sub) { function premiumServerLabels(sub) {
return premiumServerLabelsFn(sub); return premiumServerLabelsFn(sub);
} }
@@ -166,7 +172,7 @@
</Card> </Card>
{#if subscription.active} {#if subscription.active}
<Card class={regularTrafficCardClass(subscription)}> <Card compact class={regularTrafficCardClass(subscription)}>
{#if regularTrafficTopupBarClickable} {#if regularTrafficTopupBarClickable}
<button <button
class="card-click-target" class="card-click-target"
@@ -175,28 +181,34 @@
aria-label={t("wa_add_traffic")} aria-label={t("wa_add_traffic")}
></button> ></button>
{/if} {/if}
<div class="traffic-top"> <div class="traffic-summary-row">
<span>{t("wa_home_traffic_used")}</span> <span class="traffic-summary-left">
<strong>{trafficLabel(subscription)}</strong> {t("wa_home_traffic_used")}
<span class="traffic-summary-separator" aria-hidden="true">|</span>
{regularTrafficMetaLabel(subscription)}
</span>
<strong class="traffic-summary-right">
<span>{trafficLabel(subscription)}</span>
<span class="traffic-summary-separator" aria-hidden="true">|</span>
<span>{trafficPercent(subscription)}%</span>
</strong>
</div> </div>
<LinearProgress value={trafficPercent(subscription)} label={t("wa_home_traffic_used")} /> <LinearProgress value={trafficPercent(subscription)} label={t("wa_home_traffic_used")} />
<div class="traffic-meta">
<span>{regularTrafficMetaLabel(subscription)}</span>
<span class="traffic-percent">{trafficPercent(subscription)}%</span>
</div>
</Card> </Card>
{#if premiumTrafficAvailable(subscription) && subscription?.premium_unlimited_override} {#if premiumTrafficAvailable(subscription) && subscription?.premium_unlimited_override}
<Card class="premium-traffic-card"> <Card compact class="traffic-card-compact premium-traffic-card">
<div class="traffic-top">
<span>{premiumTitle(subscription)}</span>
<strong></strong>
</div>
<div class="traffic-meta premium-traffic-meta">
{#if premiumServerLabels(subscription).length} {#if premiumServerLabels(subscription).length}
<details class="premium-server-dropdown"> <details class="premium-server-dropdown premium-server-dropdown-inline">
<summary> <summary class="traffic-summary-row premium-server-summary">
<span>{t("wa_premium_unlimited", {}, "Безлимит на premium-сервера")}</span> <span class="traffic-summary-left premium-summary-trigger">
<ChevronsUpDown size={13} /> <span class="premium-summary-copy">
{premiumTitle(subscription)}
<span class="traffic-summary-separator" aria-hidden="true">|</span>
{t("wa_premium_unlimited", {}, "Безлимит на premium-сервера")}
</span>
<CircleQuestionMark class="premium-server-help-icon" size={15} />
</span>
<strong class="traffic-summary-right"></strong>
</summary> </summary>
<div class="premium-server-list premium-server-list-dropdown"> <div class="premium-server-list premium-server-list-dropdown">
<div> <div>
@@ -207,13 +219,20 @@
</div> </div>
</details> </details>
{:else} {:else}
<span>{t("wa_premium_unlimited", {}, "Безлимит на premium-сервера")}</span> <div class="traffic-summary-row">
{/if} <span class="traffic-summary-left">
{premiumTitle(subscription)}
<span class="traffic-summary-separator" aria-hidden="true">|</span>
{t("wa_premium_unlimited", {}, "Безлимит на premium-сервера")}
</span>
<strong class="traffic-summary-right"></strong>
</div> </div>
{/if}
</Card> </Card>
{:else if premiumTrafficAvailable(subscription) && Number(subscription?.premium_limit_bytes || 0) > 0} {:else if premiumTrafficAvailable(subscription) && Number(subscription?.premium_limit_bytes || 0) > 0}
<Card <Card
class={`${premiumTrafficTopupBarClickable ? "traffic-card-clickable " : ""}premium-traffic-card${subscription?.premium_is_limited ? " premium-traffic-card-limited" : ""}`} compact
class={`traffic-card-compact ${premiumTrafficTopupBarClickable ? "traffic-card-clickable " : ""}premium-traffic-card${subscription?.premium_is_limited ? " premium-traffic-card-limited" : ""}`}
> >
{#if premiumTrafficTopupBarClickable} {#if premiumTrafficTopupBarClickable}
<button <button
@@ -223,25 +242,22 @@
aria-label={t("wa_add_traffic_premium", { target: premiumTitle(subscription) })} aria-label={t("wa_add_traffic_premium", { target: premiumTitle(subscription) })}
></button> ></button>
{/if} {/if}
<div class="traffic-top">
<span>{premiumTitle(subscription)}</span>
<strong>{premiumTrafficLabel(subscription)}</strong>
</div>
<LinearProgress
class="premium-progress"
value={premiumTrafficPercent(subscription)}
label={premiumTitle(subscription)}
/>
<div class="traffic-meta premium-traffic-meta">
{#if premiumServerLabels(subscription).length} {#if premiumServerLabels(subscription).length}
<details class="premium-server-dropdown"> <details class="premium-server-dropdown premium-server-dropdown-inline">
<summary> <summary class="traffic-summary-row premium-server-summary">
<span <span class="traffic-summary-left premium-summary-trigger">
>{subscription?.premium_is_limited <span class="premium-summary-copy">
? t("wa_premium_access_limited", {}, "Доступ к premium временно ограничен") {premiumTitle(subscription)}
: t("wa_premium_reset_monthly", {}, "Отдельный лимит на месяц")}</span <span class="traffic-summary-separator" aria-hidden="true">|</span>
> {premiumTrafficMetaLabel(subscription)}
<ChevronsUpDown size={13} /> </span>
<CircleQuestionMark class="premium-server-help-icon" size={15} />
</span>
<strong class="traffic-summary-right">
<span>{premiumTrafficLabel(subscription)}</span>
<span class="traffic-summary-separator" aria-hidden="true">|</span>
<span>{premiumTrafficPercent(subscription)}%</span>
</strong>
</summary> </summary>
<div class="premium-server-list premium-server-list-dropdown"> <div class="premium-server-list premium-server-list-dropdown">
<div> <div>
@@ -252,14 +268,24 @@
</div> </div>
</details> </details>
{:else} {:else}
<span <div class="traffic-summary-row">
>{subscription?.premium_is_limited <span class="traffic-summary-left">
? t("wa_premium_access_limited", {}, "Доступ к premium временно ограничен") {premiumTitle(subscription)}
: t("wa_premium_reset_monthly", {}, "Отдельный лимит на месяц")}</span <span class="traffic-summary-separator" aria-hidden="true">|</span>
> {premiumTrafficMetaLabel(subscription)}
{/if} </span>
<span class="traffic-percent">{premiumTrafficPercent(subscription)}%</span> <strong class="traffic-summary-right">
<span>{premiumTrafficLabel(subscription)}</span>
<span class="traffic-summary-separator" aria-hidden="true">|</span>
<span>{premiumTrafficPercent(subscription)}%</span>
</strong>
</div> </div>
{/if}
<LinearProgress
class="premium-progress"
value={premiumTrafficPercent(subscription)}
label={premiumTitle(subscription)}
/>
</Card> </Card>
{/if} {/if}
{:else if trialOfferAvailable} {:else if trialOfferAvailable}