|
| 1 | +<script setup lang="ts"> |
| 2 | +import CountriesDB from 'countries-db'; |
| 3 | +import ISO6391 from 'iso-639-1'; |
| 4 | +import { useFuzzySearch } from '@/composable/fuzzySearch'; |
| 5 | +import useDebouncedRef from '@/composable/debouncedref'; |
| 6 | +
|
| 7 | +const searchQuery = useDebouncedRef('', 500); |
| 8 | +const countriesSearchData = Object.entries(CountriesDB.getAllCountries()).map(([_, info]) => { |
| 9 | + return { |
| 10 | + iso2: info.iso2, |
| 11 | + iso3: info.iso3, |
| 12 | + domain: info.domain, |
| 13 | + name: `${info.name} ${info.officialName}`, |
| 14 | + info, |
| 15 | + }; |
| 16 | +}); |
| 17 | +const { searchResult } = useFuzzySearch({ |
| 18 | + search: searchQuery, |
| 19 | + data: countriesSearchData, |
| 20 | + options: { |
| 21 | + keys: ['name', { name: 'iso3', weight: 3 }, { name: 'iso2', weight: 2 }, 'domain'], |
| 22 | + threshold: 0.3, |
| 23 | + isCaseSensitive: false, |
| 24 | + useExtendedSearch: true, |
| 25 | + }, |
| 26 | +}); |
| 27 | +
|
| 28 | +function langToName(code: string) { |
| 29 | + const name = ISO6391.getName(code); |
| 30 | + if (!name) { |
| 31 | + return code; |
| 32 | + } |
| 33 | + return `${code} (${name})`; |
| 34 | +} |
| 35 | +</script> |
| 36 | + |
| 37 | +<template> |
| 38 | + <div mx-auto max-w-2400px important:flex-1> |
| 39 | + <div flex items-center gap-3> |
| 40 | + <c-input-text |
| 41 | + v-model:value="searchQuery" |
| 42 | + placeholder="Search Countries by name, iso2, iso3..." |
| 43 | + mx-auto max-w-600px |
| 44 | + > |
| 45 | + <template #prefix> |
| 46 | + <icon-mdi-search mr-6px color-black op-70 dark:color-white /> |
| 47 | + </template> |
| 48 | + </c-input-text> |
| 49 | + </div> |
| 50 | + |
| 51 | + <div v-if="searchQuery.trim().length > 0"> |
| 52 | + <div |
| 53 | + v-if="searchResult.length === 0" |
| 54 | + mt-4 |
| 55 | + text-20px |
| 56 | + font-bold |
| 57 | + > |
| 58 | + No results |
| 59 | + </div> |
| 60 | + |
| 61 | + <div v-else> |
| 62 | + <div mt-4 text-20px font-bold> |
| 63 | + Search result |
| 64 | + </div> |
| 65 | + |
| 66 | + <n-table> |
| 67 | + <thead> |
| 68 | + <th>Iso2/Iso3</th> |
| 69 | + <th>Name and Info</th> |
| 70 | + </thead> |
| 71 | + <tbody> |
| 72 | + <tr v-for="(result, ix) in searchResult" :key="ix"> |
| 73 | + <td style="vertical-align: top"> |
| 74 | + <input-copyable :value="result.iso2" /> |
| 75 | + <input-copyable :value="result.iso3" /> |
| 76 | + </td> |
| 77 | + <td> |
| 78 | + <input-copyable label-width="150px" label="Name" label-position="left" :value="result.name" mb-1 /> |
| 79 | + <input-copyable label-width="150px" label="Official Name" label-position="left" :value="result.info.officialName" mb-1 /> |
| 80 | + <input-copyable label-width="150px" label="Domain" label-position="left" :value="result.info.domain" mb-1 /> |
| 81 | + <input-copyable label-width="150px" label="Emoji" label-position="left" :value="`${result.info.emoji} (${result.info.emojiUnicode})`" mb-1 /> |
| 82 | + <input-copyable label-width="150px" label="ISO Num" label-position="left" :value="result.info.isoNumeric" mb-1 /> |
| 83 | + <input-copyable label-width="150px" label="Continent" label-position="left" :value="result.info.continentId" mb-1 /> |
| 84 | + <input-copyable label-width="150px" label="Elevation (m)" label-position="left" :value="result.info.elevation" mb-1 /> |
| 85 | + <input-copyable label-width="150px" label="Population" label-position="left" :value="result.info.population" mb-1 /> |
| 86 | + <input-copyable label-width="150px" label="Area (km²)" label-position="left" :value="result.info.areaSqKm" mb-1 /> |
| 87 | + <input-copyable label-width="150px" label="Timezones" label-position="left" :value="result.info.timezones.join('\n')" mb-1 /> |
| 88 | + <input-copyable label-width="150px" label="Currency" label-position="left" :value="`${result.info.currencyCode} / ${result.info.currencyName}`" mb-1 /> |
| 89 | + <input-copyable label-width="150px" label="Postal Code" label-position="left" :value="`${result.info.postalCodeFormat} / ${result.info.postalCodeRegex}`" mb-1 /> |
| 90 | + <input-copyable label-width="150px" label="Phone Code" label-position="left" :value="result.info.phoneCode" mb-1 /> |
| 91 | + <input-copyable label-width="150px" label="Neighbor Countries" label-position="left" :value="result.info.neighborCountryIds.map(id => CountriesDB.getCountry(id, 'name')?.toString() || id).join(', ')" mb-1 /> |
| 92 | + <input-copyable label-width="150px" label="Languages" label-position="left" :value="result.info.languages.map(langToName).join(', ')" mb-1 /> |
| 93 | + <input-copyable label-width="150px" label="Locales" label-position="left" :value="result.info.locales.map(langToName).join(', ')" mb-1 /> |
| 94 | + <!-- //NOSONAR --><n-a :href="`https://www.openstreetmap.org/#map=5/${result.info.coordinates.latitude}/${result.info.coordinates.longitude}`" target="_blank"> |
| 95 | + > See on OpenStreetMap |
| 96 | + </n-a> |
| 97 | + </td> |
| 98 | + </tr> |
| 99 | + </tbody> |
| 100 | + </n-table> |
| 101 | + </div> |
| 102 | + </div> |
| 103 | + </div> |
| 104 | +</template> |
0 commit comments