Jimin Huang
Change settings
84b00d4
raw
history blame
2.57 kB
<!-- 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>