|
8 | 8 | import Timestamp from '$lib/components/Timestamp.svelte'; |
9 | 9 | import TopVaultsOptIn from './TopVaultsOptIn.svelte'; |
10 | 10 | import ChainCell from './ChainCell.svelte'; |
11 | | - import VaultCell from './VaultCell.svelte'; |
12 | | - import MultiValCell from './MultiValCell.svelte'; |
13 | 11 | import FeesCell from './FeesCell.svelte'; |
14 | 12 | import DepositEventsCell from './DepositEventsCell.svelte'; |
15 | 13 | import RiskCell from './RiskCell.svelte'; |
|
22 | 20 |
|
23 | 21 | const { topVaults, chain }: Props = $props(); |
24 | 22 |
|
25 | | - const formatReturn = (v: number | null) => formatPercent(v, 2); |
26 | | - const formatTvl = (v: number | null) => formatDollar(v, 2); |
| 23 | + const formatReturn = (v: MaybeNumber) => formatPercent(v, 2); |
| 24 | + const formatTvl = (v: MaybeNumber) => formatDollar(v, 2); |
27 | 25 |
|
28 | 26 | let filterValue = $state(''); |
29 | 27 | </script> |
30 | 28 |
|
| 29 | +{#snippet multiVal<T = MaybeNumber>(values: [T, T], formatter: Formatter<T>)} |
| 30 | + <div class="multiline multival"> |
| 31 | + <span class="primary">{formatter(values[0])}</span> |
| 32 | + <span class="secondary">{formatter(values[1])}</span> |
| 33 | + </div> |
| 34 | +{/snippet} |
| 35 | + |
31 | 36 | <div class="top-vaults"> |
32 | 37 | <TopVaultsOptIn /> |
33 | 38 |
|
|
74 | 79 | <ChainCell {chain} label={chain?.name ?? `Chain ${vault.chain_id}`} /> |
75 | 80 | </td> |
76 | 81 | <td class="vault"> |
77 | | - <VaultCell name={vault.name} protocol={vault.protocol} /> |
| 82 | + <div class="multiline"> |
| 83 | + <strong>{vault.name}</strong> |
| 84 | + {#if vault.protocol} |
| 85 | + <span class="secondary">{vault.protocol}</span> |
| 86 | + {/if} |
| 87 | + </div> |
78 | 88 | </td> |
79 | | - <td class="one_month_return_ann"> |
80 | | - <MultiValCell values={[vault.one_month_cagr_net, vault.one_month_cagr]} formatter={formatReturn} /> |
| 89 | + <td class="one_month_return_ann right"> |
| 90 | + {@render multiVal([vault.one_month_cagr_net, vault.one_month_cagr], formatReturn)} |
81 | 91 | </td> |
82 | | - <td class="three_months_return_ann"> |
83 | | - <MultiValCell |
84 | | - values={[vault.three_months_cagr_net, vault.three_months_cagr]} |
85 | | - formatter={formatReturn} |
86 | | - /> |
| 92 | + <td class="three_months_return_ann right"> |
| 93 | + {@render multiVal([vault.three_months_cagr_net, vault.three_months_cagr], formatReturn)} |
87 | 94 | </td> |
88 | | - <td class="lifetime_return_ann"> |
89 | | - <MultiValCell values={[vault.cagr_net, vault.cagr]} formatter={formatReturn} /> |
| 95 | + <td class="lifetime_return_ann right"> |
| 96 | + {@render multiVal([vault.cagr_net, vault.cagr], formatReturn)} |
90 | 97 | </td> |
91 | | - <td class="lifetime_return_abs"> |
92 | | - <MultiValCell values={[vault.lifetime_return_net, vault.lifetime_return]} formatter={formatReturn} /> |
| 98 | + <td class="lifetime_return_abs right"> |
| 99 | + {@render multiVal([vault.lifetime_return_net, vault.lifetime_return], formatReturn)} |
93 | 100 | </td> |
94 | | - <td class="three_months_sharpe"> |
| 101 | + <td class="three_months_sharpe right"> |
95 | 102 | {formatNumber(vault.three_months_sharpe, 1)} |
96 | 103 | </td> |
97 | | - <td class="three_months_volatility"> |
| 104 | + <td class="three_months_volatility right"> |
98 | 105 | {formatPercent(vault.three_months_volatility, 1)} |
99 | 106 | </td> |
100 | | - <td class="denomination"> |
| 107 | + <td class="denomination center"> |
101 | 108 | {formatValue(vault.denomination)} |
102 | 109 | </td> |
103 | | - <td class="tvl"> |
104 | | - <MultiValCell values={[vault.current_nav, vault.peak_nav]} formatter={formatTvl} /> |
| 110 | + <td class="tvl right"> |
| 111 | + {@render multiVal([vault.current_nav, vault.peak_nav], formatTvl)} |
105 | 112 | </td> |
106 | | - <td class="age"> |
| 113 | + <td class="age right"> |
107 | 114 | {formatNumber(vault.years, 1)} |
108 | 115 | </td> |
109 | | - <td class="fees"> |
| 116 | + <td class="fees right"> |
110 | 117 | <FeesCell mgmt_fee={vault.mgmt_fee} perf_fee={vault.perf_fee} /> |
111 | 118 | </td> |
112 | | - <td class="event_count"> |
| 119 | + <td class="event_count right"> |
113 | 120 | <DepositEventsCell value={vault.event_count} /> |
114 | 121 | </td> |
115 | 122 | <td class="risk"> |
|
127 | 134 | {/each} |
128 | 135 | </tbody> |
129 | 136 | </table> |
130 | | - |
131 | | - <!-- <DataTable class="top-vaults-table" {tableViewModel} /> --> |
132 | 137 | </div> |
133 | 138 | {/if} |
134 | 139 | </div> |
|
175 | 180 | } |
176 | 181 | } |
177 | 182 |
|
178 | | - :global(.top-vaults-table) { |
| 183 | + .top-vaults-table { |
179 | 184 | position: relative; |
180 | 185 | width: 100%; |
181 | 186 | border-collapse: collapse; |
|
194 | 199 | font-size: 14px; |
195 | 200 | } |
196 | 201 |
|
197 | | - :global(:is(td, th)) { |
| 202 | + :is(td, th) { |
198 | 203 | vertical-align: top; |
199 | 204 | } |
200 | 205 |
|
201 | | - :global(th) { |
| 206 | + th { |
202 | 207 | position: sticky; |
203 | 208 | top: 0px; |
204 | 209 | z-index: 1; |
|
214 | 219 | text-align: left; |
215 | 220 | } |
216 | 221 |
|
217 | | - :global(th.sorted) { |
| 222 | + th.sorted { |
218 | 223 | padding-right: 1.125rem; |
219 | 224 |
|
220 | | - :global(svg) { |
| 225 | + svg { |
221 | 226 | position: absolute; |
222 | 227 | top: 0.5rem; |
223 | 228 | right: 0.25rem; |
224 | 229 | } |
225 | 230 | } |
226 | 231 |
|
227 | 232 | /* no background on index column */ |
228 | | - :global(th.index) { |
| 233 | + th.index { |
229 | 234 | background: var(--c-body); |
230 | 235 | } |
231 | 236 |
|
232 | 237 | /* custom alignment for chain sort indicator (no header label) */ |
233 | | - :global(th.chain.sorted svg) { |
| 238 | + th.chain.sorted svg { |
234 | 239 | right: 0.5rem; |
235 | 240 | } |
236 | 241 |
|
237 | | - :global(td.chain) { |
| 242 | + td.chain { |
238 | 243 | width: 1.875rem; |
239 | 244 | } |
240 | 245 |
|
241 | | - :global(td.vault) { |
| 246 | + td.vault { |
242 | 247 | min-width: 12rem; |
243 | 248 | } |
244 | 249 |
|
245 | 250 | /* flip the sort indicator on columns that use inverted sort */ |
246 | | - :global(:is(th.chain, th.vault, th.denomination, th.fees, th.risk) svg) { |
| 251 | + :is(th.chain, th.vault, th.denomination, th.fees, th.risk) svg { |
247 | 252 | rotate: 180deg; |
248 | 253 | } |
249 | 254 |
|
250 | | - :global(td) { |
| 255 | + td { |
251 | 256 | border-block: 1px solid var(--c-text-ultra-light); |
252 | 257 | padding: 0.25em 0.5em; |
253 | 258 |
|
|
262 | 267 | &:nth-child(odd) { |
263 | 268 | background-color: var(--c-col-b); |
264 | 269 | } |
| 270 | + |
| 271 | + &.right { |
| 272 | + text-align: right; |
| 273 | + } |
| 274 | + |
| 275 | + &.center { |
| 276 | + text-align: center; |
| 277 | + } |
265 | 278 | } |
266 | 279 |
|
267 | 280 | /* reverse column colors if chain col is present */ |
268 | | - :global(:where(tr:has(.chain)) td) { |
| 281 | + :where(tr:has(.chain)) td { |
269 | 282 | &:nth-child(odd) { |
270 | 283 | background-color: var(--c-col-a); |
271 | 284 | } |
|
275 | 288 | } |
276 | 289 | } |
277 | 290 |
|
278 | | - :global(td:has(.tooltip)) { |
| 291 | + td:global(:has(.tooltip)) { |
279 | 292 | position: relative; |
280 | 293 | } |
281 | 294 |
|
282 | 295 | :global(.multiline) { |
283 | 296 | display: grid; |
284 | | - gap: 0.125rem; |
| 297 | + gap: 0.5rem; |
| 298 | + } |
| 299 | + |
| 300 | + :global(.secondary) { |
| 301 | + opacity: 0.7; |
| 302 | + } |
| 303 | + |
| 304 | + .multival { |
| 305 | + > ::before { |
| 306 | + content: '('; |
| 307 | + } |
| 308 | + |
| 309 | + > ::after { |
| 310 | + content: ')'; |
| 311 | + } |
| 312 | + |
| 313 | + .primary { |
| 314 | + font-weight: 600; |
| 315 | + |
| 316 | + &::before, |
| 317 | + &::after { |
| 318 | + visibility: hidden; |
| 319 | + } |
| 320 | + } |
285 | 321 | } |
286 | 322 |
|
287 | | - :global(td.index) { |
| 323 | + td.index { |
288 | 324 | text-align: center; |
289 | 325 | vertical-align: middle; |
290 | 326 | background-color: var(--c-col-b); |
|
295 | 331 | } |
296 | 332 | } |
297 | 333 |
|
298 | | - :global(td.chain) { |
| 334 | + td.chain { |
299 | 335 | vertical-align: middle; |
300 | 336 | text-align: center; |
301 | 337 | padding-right: 0.25rem; |
|
311 | 347 | font: var(--f-ui-xs-roman); |
312 | 348 | letter-spacing: var(--ls-ui-xs, normal); |
313 | 349 |
|
314 | | - :global(a):not(:hover) { |
| 350 | + :global(a:not(:hover)) { |
315 | 351 | text-decoration: none; |
316 | 352 | } |
317 | 353 | } |
318 | | -
|
319 | | - :global( |
320 | | - :is( |
321 | | - td.one_month_return_ann, |
322 | | - td.lifetime_return_abs, |
323 | | - td.lifetime_return_ann, |
324 | | - td.three_months_return_ann, |
325 | | - td.three_months_sharpe, |
326 | | - td.three_months_volatility, |
327 | | - td.tvl, |
328 | | - td.age, |
329 | | - td.fees, |
330 | | - td.event_count |
331 | | - ) |
332 | | - ) { |
333 | | - text-align: right; |
334 | | - } |
335 | | -
|
336 | | - :global(td.denomination) { |
337 | | - text-align: center; |
338 | | - } |
339 | 354 | } |
340 | 355 | } |
341 | 356 | </style> |
0 commit comments