Spaces:
Running
Running
| <template> | |
| <div class="flex gap-2 flex-wrap"> | |
| <button | |
| v-for="code in orderedAssets" :key="code" | |
| class="px-3 py-1 rounded-full border flex items-center gap-2" | |
| :class="code===modelValue ? 'bg-black text-white' : 'bg-white'" | |
| @click="$emit('update:modelValue', code)" | |
| > | |
| <img :src="iconFor(code)" alt="" class="asset-icon" decoding="async" loading="lazy" @error="hide($event)" /> | |
| <span>{{ code }}</span> | |
| </button> | |
| </div> | |
| </template> | |
| <script setup> | |
| import { computed } from 'vue' | |
| import { dataService } from '../lib/dataService' | |
| const props = defineProps({ | |
| modelValue: { type: String, default: '' }, | |
| preferredOrder: { | |
| type: Array, | |
| default: () => ['BTC','ETH','SOL','BNB','DOGE','XRP','AAPL','MSFT','BMRN','MRNA','TSLA'] | |
| } | |
| }) | |
| const emit = defineEmits(['update:modelValue']) | |
| // same pattern as your AssetsFilter.vue | |
| const iconFor = (assetCode) => | |
| new URL(`../assets/images/assets_images/${assetCode}.png`, import.meta.url).href | |
| const hide = (e) => { e.target.style.display = 'none' } | |
| const available = computed(() => { | |
| const rows = Array.isArray(dataService.tableRows) ? dataService.tableRows : [] | |
| return Array.from(new Set(rows.map(r => r.asset))) | |
| }) | |
| const orderedAssets = computed(() => { | |
| const present = new Set(available.value) | |
| const primary = props.preferredOrder.filter(a => present.has(a)) | |
| const extras = [...present].filter(a => !props.preferredOrder.includes(a)).sort() | |
| const list = [...primary, ...extras] | |
| if (!list.includes(props.modelValue) && list.length) emit('update:modelValue', list[0]) | |
| return list | |
| }) | |
| </script> | |
| <style scoped> | |
| .asset-icon{ | |
| width: 18px; height: 18px; flex: 0 0 18px; | |
| object-fit: contain; display: inline-block; vertical-align: middle; | |
| } | |
| </style> | |