From 65e16f3502ee48a7887b80805640b2b5e9bde39f Mon Sep 17 00:00:00 2001 From: Tomas Horsky Date: Tue, 9 Jun 2026 15:56:01 +0900 Subject: [PATCH] add side stats panel --- jsconfig.json | 2 +- src/App.svelte | 26 +++- src/lib/WorldMap.svelte | 144 ------------------ src/lib/{ => layout}/Footer.svelte | 9 +- src/lib/{ => layout}/Layout.svelte | 0 src/lib/{ => layout}/TopBar.svelte | 11 +- src/lib/layout/selection.svelte.js | 28 ++++ src/lib/world-map/StatsPanel.svelte | 204 ++++++++++++++++++++++++++ src/lib/world-map/WorldMap.svelte | 219 ++++++++++++++++++++++++++++ src/lib/world-map/continents.js | 213 +++++++++++++++++++++++++++ 10 files changed, 701 insertions(+), 155 deletions(-) delete mode 100644 src/lib/WorldMap.svelte rename src/lib/{ => layout}/Footer.svelte (57%) rename src/lib/{ => layout}/Layout.svelte (100%) rename src/lib/{ => layout}/TopBar.svelte (90%) create mode 100644 src/lib/layout/selection.svelte.js create mode 100644 src/lib/world-map/StatsPanel.svelte create mode 100644 src/lib/world-map/WorldMap.svelte create mode 100644 src/lib/world-map/continents.js diff --git a/jsconfig.json b/jsconfig.json index c7a0b10..4551534 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -23,7 +23,7 @@ * Typecheck JS in `.svelte` and `.js` files by default. * Disable this if you'd like to use dynamic types. */ - "checkJs": true + "checkJs": false }, /** * Use global.d.ts instead of compilerOptions.types diff --git a/src/App.svelte b/src/App.svelte index c09a368..5287ced 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,6 +1,7 @@ - -
-
-
- - diff --git a/src/lib/Footer.svelte b/src/lib/layout/Footer.svelte similarity index 57% rename from src/lib/Footer.svelte rename to src/lib/layout/Footer.svelte index dcbd363..bee400e 100644 --- a/src/lib/Footer.svelte +++ b/src/lib/layout/Footer.svelte @@ -9,8 +9,11 @@ align-items: center; justify-content: flex-end; padding-right: 24px; - background: #dce8f0; - font: 14px/1.5 sans-serif; - color: #555; + background: #334155; + font: 15px/1.6 sans-serif; + color: #cbd5e1; + position: relative; + z-index: 10; + box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1); } diff --git a/src/lib/Layout.svelte b/src/lib/layout/Layout.svelte similarity index 100% rename from src/lib/Layout.svelte rename to src/lib/layout/Layout.svelte diff --git a/src/lib/TopBar.svelte b/src/lib/layout/TopBar.svelte similarity index 90% rename from src/lib/TopBar.svelte rename to src/lib/layout/TopBar.svelte index 529a52e..f700cb7 100644 --- a/src/lib/TopBar.svelte +++ b/src/lib/layout/TopBar.svelte @@ -30,8 +30,11 @@ display: flex; align-items: center; padding: 0 24px; - background: #dce8f0; + background: #1e2937; gap: 16px; + position: relative; + z-index: 10; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); } .left { @@ -50,7 +53,7 @@ .app-name { font: 700 20px/1.2 sans-serif; - color: #1f2937; + color: #f1f5f9; white-space: nowrap; } @@ -63,7 +66,7 @@ .segmented { position: relative; display: flex; - background: rgba(0, 0, 0, 0.08); + background: rgba(255, 255, 255, 0.1); border-radius: 999px; padding: 4px; width: 300px; @@ -90,7 +93,7 @@ background: none; cursor: pointer; font: 500 16px/1.4 sans-serif; - color: #555; + color: #cbd5e1; } .right { diff --git a/src/lib/layout/selection.svelte.js b/src/lib/layout/selection.svelte.js new file mode 100644 index 0000000..c6a0d7f --- /dev/null +++ b/src/lib/layout/selection.svelte.js @@ -0,0 +1,28 @@ +let selected = $state(new Set()); +let totalCountries = $state(0); + +export function toggle(id) { + const next = new Set(selected); + if (next.has(id)) { + next.delete(id); + } else { + next.add(id); + } + selected = next; +} + +export function clearAll() { + selected = new Set(); +} + +export function getSelected() { + return selected; +} + +export function setTotalCount(n) { + totalCountries = n; +} + +export function getTotalCount() { + return totalCountries; +} diff --git a/src/lib/world-map/StatsPanel.svelte b/src/lib/world-map/StatsPanel.svelte new file mode 100644 index 0000000..f96e17d --- /dev/null +++ b/src/lib/world-map/StatsPanel.svelte @@ -0,0 +1,204 @@ + + +
+

your statistics

+ + visited countries +
+
+
+
+ {total} / {grandTotal} +
+ +
+ + by continent + {#each CONTINENTS as continent} + {@const contTotal = continentTotals[continent]} +
+ + {continent} + {counts[continent]}/{contTotal} +
+ {/each} + +
+
+
+
+
+ +
+ +
Contains all UN countries, Kosovo, Hong Kong and Taiwan
+
+ + diff --git a/src/lib/world-map/WorldMap.svelte b/src/lib/world-map/WorldMap.svelte new file mode 100644 index 0000000..5cdfb52 --- /dev/null +++ b/src/lib/world-map/WorldMap.svelte @@ -0,0 +1,219 @@ + + +
+ + diff --git a/src/lib/world-map/continents.js b/src/lib/world-map/continents.js new file mode 100644 index 0000000..2eabc73 --- /dev/null +++ b/src/lib/world-map/continents.js @@ -0,0 +1,213 @@ +export const CONTINENTS = ['Europe', 'Asia', 'Africa', 'N. America', 'S. America', 'Oceania']; + +const map = { + '004': 'Asia', // Afghanistan + '008': 'Europe', // Albania + '012': 'Africa', // Algeria + '020': 'Europe', // Andorra + '024': 'Africa', // Angola + '028': 'N. America', // Antigua and Barb. + '031': 'Asia', // Azerbaijan + '032': 'S. America', // Argentina + '036': 'Oceania', // Australia + '040': 'Europe', // Austria + '044': 'N. America', // Bahamas + '048': 'Asia', // Bahrain + '050': 'Asia', // Bangladesh + '051': 'Asia', // Armenia + '052': 'N. America', // Barbados + '056': 'Europe', // Belgium + '064': 'Asia', // Bhutan + '068': 'S. America', // Bolivia + '070': 'Europe', // Bosnia and Herz. + '072': 'Africa', // Botswana + '076': 'S. America', // Brazil + '084': 'N. America', // Belize + '090': 'Oceania', // Solomon Is. + '096': 'Asia', // Brunei + '100': 'Europe', // Bulgaria + '104': 'Asia', // Myanmar + '108': 'Africa', // Burundi + '112': 'Europe', // Belarus + '116': 'Asia', // Cambodia + '120': 'Africa', // Cameroon + '124': 'N. America', // Canada + '132': 'Africa', // Cabo Verde + '140': 'Africa', // Central African Rep. + '144': 'Asia', // Sri Lanka + '148': 'Africa', // Chad + '152': 'S. America', // Chile + '156': 'Asia', // China + '158': 'Asia', // Taiwan + '170': 'S. America', // Colombia + '174': 'Africa', // Comoros + '178': 'Africa', // Congo + '180': 'Africa', // Dem. Rep. Congo + '188': 'N. America', // Costa Rica + '191': 'Europe', // Croatia + '192': 'N. America', // Cuba + '196': 'Asia', // Cyprus + '203': 'Europe', // Czechia + '204': 'Africa', // Benin + '208': 'Europe', // Denmark + '212': 'N. America', // Dominica + '214': 'N. America', // Dominican Rep. + '218': 'S. America', // Ecuador + '222': 'N. America', // El Salvador + '226': 'Africa', // Eq. Guinea + '231': 'Africa', // Ethiopia + '232': 'Africa', // Eritrea + '233': 'Europe', // Estonia + '242': 'Oceania', // Fiji + '246': 'Europe', // Finland + '250': 'Europe', // France + '262': 'Africa', // Djibouti + '266': 'Africa', // Gabon + '268': 'Asia', // Georgia + '270': 'Africa', // Gambia + '275': 'Asia', // Palestine + '276': 'Europe', // Germany + '288': 'Africa', // Ghana + '296': 'Oceania', // Kiribati + '300': 'Europe', // Greece + '308': 'N. America', // Grenada + '320': 'N. America', // Guatemala + '324': 'Africa', // Guinea + '328': 'S. America', // Guyana + '332': 'N. America', // Haiti + '336': 'Europe', // Vatican + '340': 'N. America', // Honduras + '344': 'Asia', // Hong Kong + '348': 'Europe', // Hungary + '352': 'Europe', // Iceland + '356': 'Asia', // India + '360': 'Asia', // Indonesia + '364': 'Asia', // Iran + '368': 'Asia', // Iraq + '372': 'Europe', // Ireland + '376': 'Asia', // Israel + '380': 'Europe', // Italy + '384': 'Africa', // Cote d'Ivoire + '388': 'N. America', // Jamaica + '392': 'Asia', // Japan + '398': 'Asia', // Kazakhstan + '400': 'Asia', // Jordan + '404': 'Africa', // Kenya + '408': 'Asia', // North Korea + '410': 'Asia', // South Korea + '414': 'Asia', // Kuwait + '417': 'Asia', // Kyrgyzstan + '418': 'Asia', // Laos + '422': 'Asia', // Lebanon + '426': 'Africa', // Lesotho + '428': 'Europe', // Latvia + '430': 'Africa', // Liberia + '434': 'Africa', // Libya + '438': 'Europe', // Liechtenstein + '440': 'Europe', // Lithuania + '442': 'Europe', // Luxembourg + '450': 'Africa', // Madagascar + '454': 'Africa', // Malawi + '458': 'Asia', // Malaysia + '462': 'Asia', // Maldives + '466': 'Africa', // Mali + '470': 'Europe', // Malta + '478': 'Africa', // Mauritania + '480': 'Africa', // Mauritius + '484': 'N. America', // Mexico + '492': 'Europe', // Monaco + '496': 'Asia', // Mongolia + '498': 'Europe', // Moldova + '499': 'Europe', // Montenegro + '504': 'Africa', // Morocco + '508': 'Africa', // Mozambique + '512': 'Asia', // Oman + '516': 'Africa', // Namibia + '520': 'Oceania', // Nauru + '524': 'Asia', // Nepal + '528': 'Europe', // Netherlands + '548': 'Oceania', // Vanuatu + '554': 'Oceania', // New Zealand + '558': 'N. America', // Nicaragua + '562': 'Africa', // Niger + '566': 'Africa', // Nigeria + '578': 'Europe', // Norway + '583': 'Oceania', // Micronesia + '584': 'Oceania', // Marshall Is. + '585': 'Oceania', // Palau + '586': 'Asia', // Pakistan + '591': 'N. America', // Panama + '598': 'Oceania', // Papua New Guinea + '600': 'S. America', // Paraguay + '604': 'S. America', // Peru + '608': 'Asia', // Philippines + '616': 'Europe', // Poland + '620': 'Europe', // Portugal + '624': 'Africa', // Guinea-Bissau + '626': 'Asia', // Timor-Leste + '634': 'Asia', // Qatar + '642': 'Europe', // Romania + '643': 'Europe', // Russia + '646': 'Africa', // Rwanda + '659': 'N. America', // St. Kitts and Nevis + '662': 'N. America', // Saint Lucia + '670': 'N. America', // St. Vin. and Gren. + '674': 'Europe', // San Marino + '678': 'Africa', // Sao Tome and Principe + '682': 'Asia', // Saudi Arabia + '686': 'Africa', // Senegal + '688': 'Europe', // Serbia + '690': 'Africa', // Seychelles + '694': 'Africa', // Sierra Leone + '702': 'Asia', // Singapore + '703': 'Europe', // Slovakia + '704': 'Asia', // Vietnam + '705': 'Europe', // Slovenia + '706': 'Africa', // Somalia + '710': 'Africa', // South Africa + '716': 'Africa', // Zimbabwe + '724': 'Europe', // Spain + '728': 'Africa', // S. Sudan + '729': 'Africa', // Sudan + '732': 'Africa', // W. Sahara + '740': 'S. America', // Suriname + '748': 'Africa', // eSwatini + '752': 'Europe', // Sweden + '756': 'Europe', // Switzerland + '760': 'Asia', // Syria + '762': 'Asia', // Tajikistan + '764': 'Asia', // Thailand + '768': 'Africa', // Togo + '776': 'Oceania', // Tonga + '780': 'N. America', // Trinidad and Tobago + '784': 'Asia', // United Arab Emirates + '788': 'Africa', // Tunisia + '792': 'Asia', // Turkey + '795': 'Asia', // Turkmenistan + '800': 'Africa', // Uganda + '804': 'Europe', // Ukraine + '807': 'Europe', // Macedonia + '818': 'Africa', // Egypt + '826': 'Europe', // United Kingdom + '834': 'Africa', // Tanzania + '840': 'N. America', // United States of America + '854': 'Africa', // Burkina Faso + '858': 'S. America', // Uruguay + '860': 'Asia', // Uzbekistan + '862': 'S. America', // Venezuela + '882': 'Oceania', // Samoa + '887': 'Asia', // Yemen + '894': 'Africa', // Zambia + 'XK': 'Europe', // Kosovo +}; + +export const continentTotals = CONTINENTS.reduce((acc, c) => (acc[c] = 0, acc), {}); +for (const id of Object.keys(map)) { + const cont = map[id]; + if (cont) continentTotals[cont]++; +} + +export function getContinent(id) { + return map[id] ?? null; +} +