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>