Spaces:
Running
Running
File size: 2,571 Bytes
84b00d4 6c34ff7 84b00d4 6c34ff7 84b00d4 a3d5125 84b00d4 6c34ff7 84b00d4 6c34ff7 84b00d4 6c34ff7 75cdf3c 84b00d4 6c34ff7 84b00d4 6c34ff7 75cdf3c 84b00d4 6c34ff7 84b00d4 6c34ff7 84b00d4 6c34ff7 7e792e2 84b00d4 7e792e2 84b00d4 7e792e2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
<!-- src/components/AssetTabsPrice.vue -->
<template>
<div class="tabs">
<button v-for="code in orderedAssets" :key="code"
class="tab" :class="code===modelValue ? 'tab--active' : ''"
@click="$emit('update:modelValue', code)">
<div class="row">
<img :src="iconFor(code)" class="icon" @error="hide($event)" alt="" />
<span class="code">{{ code }}</span>
</div>
<div class="price">{{ fmtUSD(prices[code]) }}</div>
</button>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { dataService } from '../lib/dataService'
import { pollPrices, subscribePrices, fmtUSD } from '../lib/prices'
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
const iconFor = (c:string) => new URL(`../assets/images/assets_images/${c}.png`, import.meta.url).href
const hide = (e:Event)=>((e.target as HTMLImageElement).style.display='none')
const available = computed(() => {
const rows = Array.isArray((dataService as any).tableRows) ? (dataService as any).tableRows : []
return Array.from(new Set(rows.map((r:any)=>r.asset)))
})
const preferred = ['BTC','ETH','SOL','BNB','DOGE','XRP','AAPL','MSFT','BMRN','MRNA','TSLA']
const orderedAssets = computed(() => {
const present = new Set(available.value)
const primary = preferred.filter(a => present.has(a))
const extras = [...present].filter(a => !preferred.includes(a)).sort()
const list = [...primary, ...extras]
if (list.length && !list.includes(props.modelValue || '')) emit('update:modelValue', list[0])
return list
})
const prices = ref<Record<string, number>>({})
let unsub: null | (()=>void) = null
onMounted(async () => {
unsub = subscribePrices(m => (prices.value = m))
await pollPrices(orderedAssets.value) // starts polling; cheap even if called again
})
onBeforeUnmount(()=>unsub?.())
watch(orderedAssets, (arr)=>pollPrices(arr))
</script>
<style scoped>
.tabs{ display:flex; flex-wrap:wrap; gap:8px }
.tab{
background:#fff; border:1px solid #1f2937; border-radius:9999px;
padding:8px 12px; min-width:110px; display:flex; flex-direction:column; align-items:center; gap:2px
}
.tab--active{ background:#111827; color:#fff; border-color:#111827 }
.row{ display:flex; align-items:center; gap:6px; line-height:1 }
.icon{ width:18px; height:18px; flex:0 0 18px; object-fit:contain }
.code{ font-weight:700; letter-spacing:.02em }
.price{ font-variant-numeric:tabular-nums; font-weight:800; line-height:1.05; margin-top:2px }
</style>
|