Skip to content

Commit 98db1e7

Browse files
committed
feat(new tool): Countries/ISO 3166 Searcher
Browser countries and iso 3166 codes (and more) FIx CorentinTh#983
1 parent 1c35ac3 commit 98db1e7

File tree

7 files changed

+175
-3
lines changed

7 files changed

+175
-3
lines changed

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ declare module '@vue/runtime-core' {
107107
Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
108108
Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
109109
Ipv6UlaGenerator: typeof import('./src/tools/ipv6-ula-generator/ipv6-ula-generator.vue')['default']
110+
Iso3166Searcher: typeof import('./src/tools/iso-3166-searcher/iso-3166-searcher.vue')['default']
110111
JsonDiff: typeof import('./src/tools/json-diff/json-diff.vue')['default']
111112
JsonMinify: typeof import('./src/tools/json-minify/json-minify.vue')['default']
112113
JsonToCsv: typeof import('./src/tools/json-to-csv/json-to-csv.vue')['default']
@@ -129,6 +130,7 @@ declare module '@vue/runtime-core' {
129130
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
130131
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
131132
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
133+
NA: typeof import('naive-ui')['NA']
132134
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
133135
NCheckbox: typeof import('naive-ui')['NCheckbox']
134136
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"change-case": "^4.1.2",
5555
"colord": "^2.9.3",
5656
"composerize-ts": "^0.6.2",
57+
"countries-db": "^1.2.0",
5758
"country-code-lookup": "^0.1.0",
5859
"cron-validator": "^1.3.1",
5960
"cronstrue": "^2.26.0",
@@ -68,6 +69,7 @@
6869
"highlight.js": "^11.7.0",
6970
"iarna-toml-esm": "^3.0.5",
7071
"ibantools": "^4.3.3",
72+
"iso-639-1": "^3.1.3",
7173
"js-base64": "^3.7.6",
7274
"json5": "^2.2.3",
7375
"jwt-decode": "^3.1.2",

pnpm-lock.yaml

Lines changed: 18 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { tool as jsonToXml } from './json-to-xml';
1212
import { tool as regexTester } from './regex-tester';
1313
import { tool as regexMemo } from './regex-memo';
1414
import { tool as markdownToHtml } from './markdown-to-html';
15+
import { tool as iso3166Searcher } from './iso-3166-searcher';
1516
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
1617
import { tool as numeronymGenerator } from './numeronym-generator';
1718
import { tool as macAddressGenerator } from './mac-address-generator';
@@ -184,6 +185,7 @@ export const toolsByCategory: ToolCategory[] = [
184185
textDiff,
185186
numeronymGenerator,
186187
asciiTextDrawer,
188+
iso3166Searcher,
187189
],
188190
},
189191
{
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
declare module 'countries-db'{
2+
export interface CountryInfo {
3+
id: string
4+
name: string
5+
officialName: string
6+
emoji: string
7+
emojiUnicode: string
8+
iso2: string
9+
iso3: string
10+
isoNumeric: string
11+
geonameId: number
12+
continentId: string
13+
population: number
14+
elevation: number
15+
areaSqKm: number
16+
coordinates: {
17+
latitude: number
18+
longitude: number
19+
},
20+
timezones: Array<string>
21+
domain: string
22+
currencyCode: string
23+
currencyName: string
24+
postalCodeFormat: string
25+
postalCodeRegex: string
26+
phoneCode: string
27+
neighborCountryIds: Array<string>
28+
languages: Array<string>
29+
locales: Array<string>
30+
}
31+
32+
export function getAllCountries(): {[id:string]: CountryInfo};
33+
export function getCountry(id: string, property: string): string | Array<string> | number;
34+
export function getCountry(id: string): CountryInfo;
35+
}

src/tools/iso-3166-searcher/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Flag } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'ISO 3166 Country Codes Searcher',
6+
path: '/iso-3166-searcher',
7+
description: 'Allow searching for Country Code (ISO 3166) and info',
8+
keywords: ['iso', 'iso2', 'iso3', 'phone', 'currency', 'timezones', 'domain', 'lang', 'iso3166', 'country', 'ccltd', 'searcher'],
9+
component: () => import('./iso-3166-searcher.vue'),
10+
icon: Flag,
11+
createdAt: new Date('2024-04-20'),
12+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
&gt; 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

Comments
 (0)