Spaces:
Running
Running
Jimin Huang
commited on
Commit
Β·
7d8fea8
1
Parent(s):
2a89927
Change settings
Browse files- src/views/LiveView.vue +81 -28
src/views/LiveView.vue
CHANGED
|
@@ -35,6 +35,7 @@
|
|
| 35 |
class="card"
|
| 36 |
:class="{ 'card--bh': c.kind==='bh', 'card--winner': c.isWinner }"
|
| 37 |
>
|
|
|
|
| 38 |
<div class="card__header">
|
| 39 |
<div class="card__logo">
|
| 40 |
<img v-if="c.logo" :src="c.logo" alt="" />
|
|
@@ -42,27 +43,36 @@
|
|
| 42 |
<span v-if="c.isWinner" class="card__badge" aria-label="Top performer">π</span>
|
| 43 |
</div>
|
| 44 |
|
| 45 |
-
<div class="card__title"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
<div class="card__balance">{{ fmtUSD(c.balance) }}</div>
|
| 47 |
</div>
|
| 48 |
|
|
|
|
| 49 |
<div class="card__meta">
|
| 50 |
-
<div class="
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
-
<
|
| 53 |
-
<div class="pill"
|
|
|
|
| 54 |
<span v-if="mode==='usd'">{{ signedMoney(c.gapUsd) }}</span>
|
| 55 |
<span v-else>{{ signedPct(c.gapPct) }}</span>
|
| 56 |
</div>
|
| 57 |
-
</
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
</template>
|
| 62 |
</div>
|
| 63 |
|
|
|
|
| 64 |
<div class="card__footer">
|
| 65 |
-
<div class="
|
| 66 |
</div>
|
| 67 |
</div>
|
| 68 |
</div>
|
|
@@ -305,39 +315,82 @@ watchEffect(() => {
|
|
| 305 |
@media (max-width: 1200px) { .cards5 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
| 306 |
@media (max-width: 720px) { .cards5 { grid-template-columns: 1fr; } }
|
| 307 |
|
| 308 |
-
/* card */
|
| 309 |
-
.card {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
.card--bh { outline: 2px dashed rgba(15,23,42,.08); }
|
| 311 |
.card--winner { border-color: #16a34a; box-shadow: 0 0 0 3px rgba(22,163,74,.12); }
|
| 312 |
|
| 313 |
-
/* header
|
| 314 |
-
.card__header {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
|
| 316 |
/* logo */
|
| 317 |
-
.card__logo {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
.card__logo img { width: 100%; height: 100%; object-fit: contain; }
|
| 319 |
.card__logo-fallback { width: 60%; height: 60%; border-radius: 999px; background: #E5E7EB; }
|
| 320 |
.card__badge { position: absolute; right: -6px; top: -6px; font-size: 16px; filter: drop-shadow(0 1px 1px rgba(0,0,0,.15)); }
|
| 321 |
|
| 322 |
-
/* title
|
|
|
|
| 323 |
.card__title {
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
|
|
|
|
|
|
| 327 |
}
|
| 328 |
|
| 329 |
-
/*
|
| 330 |
-
.card__balance {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
-
/* meta row
|
| 333 |
-
.card__meta {
|
| 334 |
-
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
.card__footer { margin-top: -2px; }
|
|
|
|
| 337 |
|
| 338 |
/* pills */
|
| 339 |
-
.pill {
|
| 340 |
-
|
|
|
|
|
|
|
| 341 |
.pill.pos { background: #DCFCE7; color: #166534; }
|
| 342 |
-
.pill.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 343 |
</style>
|
|
|
|
| 35 |
class="card"
|
| 36 |
:class="{ 'card--bh': c.kind==='bh', 'card--winner': c.isWinner }"
|
| 37 |
>
|
| 38 |
+
<!-- Header: logo | title (trunc) | balance (no-wrap) -->
|
| 39 |
<div class="card__header">
|
| 40 |
<div class="card__logo">
|
| 41 |
<img v-if="c.logo" :src="c.logo" alt="" />
|
|
|
|
| 43 |
<span v-if="c.isWinner" class="card__badge" aria-label="Top performer">π</span>
|
| 44 |
</div>
|
| 45 |
|
| 46 |
+
<div class="card__title-wrap">
|
| 47 |
+
<div class="card__title ellipsize" :title="c.title">{{ c.title }}</div>
|
| 48 |
+
<div class="card__subtitle ellipsize" :title="c.subtitle">{{ c.subtitle }}</div>
|
| 49 |
+
</div>
|
| 50 |
+
|
| 51 |
<div class="card__balance">{{ fmtUSD(c.balance) }}</div>
|
| 52 |
</div>
|
| 53 |
|
| 54 |
+
<!-- Meta row: strategy/badge on the left, performance pill on the right -->
|
| 55 |
<div class="card__meta">
|
| 56 |
+
<div class="meta__left">
|
| 57 |
+
<div v-if="c.kind==='bh'" class="pill pill--neutral">Buy & Hold</div>
|
| 58 |
+
<div v-else class="pill pill--outline">Strategy</div>
|
| 59 |
+
</div>
|
| 60 |
|
| 61 |
+
<div class="meta__right" v-if="c.kind==='agent' && c.gapUsd != null">
|
| 62 |
+
<div class="pill"
|
| 63 |
+
:class="{ neg: c.gapUsd < 0 && !c.isWinner, pos: c.gapUsd >= 0 || c.isWinner }">
|
| 64 |
<span v-if="mode==='usd'">{{ signedMoney(c.gapUsd) }}</span>
|
| 65 |
<span v-else>{{ signedPct(c.gapPct) }}</span>
|
| 66 |
</div>
|
| 67 |
+
</div>
|
| 68 |
+
<div class="meta__right" v-else>
|
| 69 |
+
<!-- spacer to keep height consistent -->
|
| 70 |
+
</div>
|
|
|
|
| 71 |
</div>
|
| 72 |
|
| 73 |
+
<!-- Footer: EOD date -->
|
| 74 |
<div class="card__footer">
|
| 75 |
+
<div class="card__foot">EOD {{ c.date ? new Date(c.date).toLocaleDateString() : 'β' }}</div>
|
| 76 |
</div>
|
| 77 |
</div>
|
| 78 |
</div>
|
|
|
|
| 315 |
@media (max-width: 1200px) { .cards5 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
| 316 |
@media (max-width: 720px) { .cards5 { grid-template-columns: 1fr; } }
|
| 317 |
|
| 318 |
+
/* card container */
|
| 319 |
+
.card {
|
| 320 |
+
display: grid;
|
| 321 |
+
grid-template-rows: auto auto auto; /* header, meta, footer */
|
| 322 |
+
gap: 10px;
|
| 323 |
+
padding: 12px 14px;
|
| 324 |
+
border: 1px solid #EEF1F6;
|
| 325 |
+
border-radius: 14px;
|
| 326 |
+
background: #fff;
|
| 327 |
+
box-shadow: 0 1px 2px rgba(0,0,0,.04);
|
| 328 |
+
min-width: 0; /* let children truncate */
|
| 329 |
+
}
|
| 330 |
.card--bh { outline: 2px dashed rgba(15,23,42,.08); }
|
| 331 |
.card--winner { border-color: #16a34a; box-shadow: 0 0 0 3px rgba(22,163,74,.12); }
|
| 332 |
|
| 333 |
+
/* header grid: logo | title block | balance */
|
| 334 |
+
.card__header {
|
| 335 |
+
display: grid;
|
| 336 |
+
grid-template-columns: 48px minmax(0, 1fr) auto;
|
| 337 |
+
align-items: center;
|
| 338 |
+
gap: 12px;
|
| 339 |
+
}
|
| 340 |
|
| 341 |
/* logo */
|
| 342 |
+
.card__logo {
|
| 343 |
+
width: 44px; height: 44px; border-radius: 999px;
|
| 344 |
+
background: #F3F4F6; display: grid; place-items: center;
|
| 345 |
+
overflow: hidden; position: relative; flex: 0 0 44px;
|
| 346 |
+
}
|
| 347 |
.card__logo img { width: 100%; height: 100%; object-fit: contain; }
|
| 348 |
.card__logo-fallback { width: 60%; height: 60%; border-radius: 999px; background: #E5E7EB; }
|
| 349 |
.card__badge { position: absolute; right: -6px; top: -6px; font-size: 16px; filter: drop-shadow(0 1px 1px rgba(0,0,0,.15)); }
|
| 350 |
|
| 351 |
+
/* title block */
|
| 352 |
+
.card__title-wrap { min-width: 0; display: grid; gap: 2px; }
|
| 353 |
.card__title {
|
| 354 |
+
font-weight: 800; color: #0F172A; line-height: 1.1;
|
| 355 |
+
font-size: clamp(16px, 2vw, 20px); /* scale a bit with width */
|
| 356 |
+
}
|
| 357 |
+
.card__subtitle {
|
| 358 |
+
font-size: 12px; color: #5B6476; opacity: .85; line-height: 1.1;
|
| 359 |
}
|
| 360 |
|
| 361 |
+
/* balance (never wraps) */
|
| 362 |
+
.card__balance {
|
| 363 |
+
white-space: nowrap;
|
| 364 |
+
font-weight: 900; color: #0F172A;
|
| 365 |
+
font-size: clamp(18px, 2.2vw, 22px);
|
| 366 |
+
margin-left: 8px;
|
| 367 |
+
}
|
| 368 |
|
| 369 |
+
/* meta row */
|
| 370 |
+
.card__meta {
|
| 371 |
+
display: grid;
|
| 372 |
+
grid-template-columns: 1fr auto;
|
| 373 |
+
align-items: center;
|
| 374 |
+
gap: 10px;
|
| 375 |
+
}
|
| 376 |
+
.meta__left, .meta__right { min-width: 0; display: flex; align-items: center; gap: 8px; }
|
| 377 |
+
|
| 378 |
+
/* footer */
|
| 379 |
.card__footer { margin-top: -2px; }
|
| 380 |
+
.card__foot { font-size: 12px; color: #5B6476; opacity: .85; }
|
| 381 |
|
| 382 |
/* pills */
|
| 383 |
+
.pill {
|
| 384 |
+
padding: 4px 9px; border-radius: 999px; font-size: 12px; font-weight: 800;
|
| 385 |
+
line-height: 1; white-space: nowrap; background: #EEF2F7; color: #0F172A;
|
| 386 |
+
}
|
| 387 |
.pill.pos { background: #DCFCE7; color: #166534; }
|
| 388 |
+
.pill.neg { background: #FEE2E2; color: #B91C1C; }
|
| 389 |
+
.pill--neutral { background: #EEF2F7; color: #0F172A; }
|
| 390 |
+
.pill--outline {
|
| 391 |
+
background: transparent; color: #475569; border: 1px solid #E5E7EB; padding: 3px 8px;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
/* utilities */
|
| 395 |
+
.ellipsize { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
| 396 |
</style>
|