changed font style, added journey summary, changed card ui
This commit is contained in:
307
src/lib/world-map/StatsPanel.svelte
Normal file
307
src/lib/world-map/StatsPanel.svelte
Normal file
@@ -0,0 +1,307 @@
|
||||
<script>
|
||||
import { CONTINENTS, getContinent, continentTotals } from './continents.js';
|
||||
import { getSelected, getTotalCount } from '../layout/selection.svelte.js';
|
||||
|
||||
let collapsed = $state(false);
|
||||
|
||||
const continentColors = {
|
||||
'Europe': '#3b82f6',
|
||||
'Asia': '#ef4444',
|
||||
'Africa': '#f97316',
|
||||
'N. America': '#22c55e',
|
||||
'S. America': '#eab308',
|
||||
'Oceania': '#a855f7'
|
||||
};
|
||||
|
||||
let counts = $derived.by(() => {
|
||||
const c = {};
|
||||
for (const cont of CONTINENTS) c[cont] = 0;
|
||||
for (const id of getSelected()) {
|
||||
const cont = getContinent(id);
|
||||
if (cont) c[cont]++;
|
||||
}
|
||||
return c;
|
||||
});
|
||||
|
||||
let total = $derived(getSelected().size);
|
||||
let grandTotal = $derived(Object.values(continentTotals).reduce((a, b) => a + b, 0));
|
||||
let pct = $derived(grandTotal > 0 ? Math.round(total / grandTotal * 100) : 0);
|
||||
|
||||
let segments = $derived.by(() => {
|
||||
if (total === 0) return [];
|
||||
const segs = [];
|
||||
let deg = 0;
|
||||
for (const cont of CONTINENTS) {
|
||||
const angle = Math.min(counts[cont] / total * 360, 359.99);
|
||||
if (angle > 0) {
|
||||
const startDeg = deg;
|
||||
const endDeg = deg + angle;
|
||||
const midDeg = (startDeg + endDeg) / 2;
|
||||
const rad = (midDeg - 90) * Math.PI / 180;
|
||||
const sr = (startDeg - 90) * Math.PI / 180;
|
||||
const er = (endDeg - 90) * Math.PI / 180;
|
||||
const cx = 90, cy = 90, outerR = 65, innerR = 30;
|
||||
const x1 = cx + outerR * Math.cos(sr);
|
||||
const y1 = cy + outerR * Math.sin(sr);
|
||||
const x2 = cx + outerR * Math.cos(er);
|
||||
const y2 = cy + outerR * Math.sin(er);
|
||||
const x3 = cx + innerR * Math.cos(er);
|
||||
const y3 = cy + innerR * Math.sin(er);
|
||||
const x4 = cx + innerR * Math.cos(sr);
|
||||
const y4 = cy + innerR * Math.sin(sr);
|
||||
const largeArc = angle > 180 ? 1 : 0;
|
||||
const path = `M ${x1} ${y1} A ${outerR} ${outerR} 0 ${largeArc} 1 ${x2} ${y2} L ${x3} ${y3} A ${innerR} ${innerR} 0 ${largeArc} 0 ${x4} ${y4} Z`;
|
||||
const lx = cx + 82 * Math.cos(rad);
|
||||
const ly = cy + 82 * Math.sin(rad);
|
||||
segs.push({ cont, color: continentColors[cont], path, lx, ly, angle });
|
||||
deg += angle;
|
||||
}
|
||||
}
|
||||
return segs;
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="panel" class:collapsed>
|
||||
<button class="collapse-btn" onclick={() => collapsed = !collapsed} data-tip={collapsed ? 'see statistics' : 'close statistics'}>
|
||||
{collapsed ? '◀' : '▶'}
|
||||
</button>
|
||||
|
||||
{#if !collapsed}
|
||||
<div class="panel-content">
|
||||
<h2 class="headline">your statistics</h2>
|
||||
|
||||
<span class="bar-label">visited countries</span>
|
||||
<div class="total-bar-wrap">
|
||||
<div class="total-bar-bg">
|
||||
<div class="total-bar-fill" style="width: {pct}%"></div>
|
||||
</div>
|
||||
<span class="total-bar-text">{total} / {grandTotal}</span>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<span class="bar-label">by continent</span>
|
||||
{#each CONTINENTS as continent}
|
||||
{@const contTotal = continentTotals[continent]}
|
||||
<div class="row">
|
||||
<span class="dot" style="background: {continentColors[continent]}"></span>
|
||||
<span class="label">{continent}</span>
|
||||
<span class="value">{counts[continent]}<span class="total">/{contTotal}</span></span>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<div class="donut-wrap">
|
||||
{#if segments.length > 0}
|
||||
<svg viewBox="0 0 180 180" class="donut-svg">
|
||||
{#each segments as seg}
|
||||
<g class="seg-group">
|
||||
<path d={seg.path} fill={seg.color} />
|
||||
<text x={seg.lx} y={seg.ly} text-anchor="middle" dominant-baseline="middle" class="donut-label" style="font-size: {seg.angle < 20 ? 12 : 15}px">{seg.cont}</text>
|
||||
</g>
|
||||
{/each}
|
||||
<circle cx="90" cy="90" r="30" fill="#f8fafc" />
|
||||
</svg>
|
||||
{:else}
|
||||
<svg viewBox="0 0 180 180" class="donut-svg">
|
||||
<circle cx="90" cy="90" r="65" fill="#e2e8f0" />
|
||||
<circle cx="90" cy="90" r="30" fill="#f8fafc" />
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="disclaimer">Contains all UN countries, Kosovo, Hong Kong and Taiwan</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.panel {
|
||||
flex: 0 0 min(360px, 25vw);
|
||||
background: var(--bg-raised);
|
||||
border-left: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-family: var(--sans);
|
||||
transition: flex-basis 0.25s ease;
|
||||
}
|
||||
|
||||
.panel.collapsed {
|
||||
flex: 0 0 28px;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
flex: 1;
|
||||
padding: 24px 28px;
|
||||
overflow-y: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
flex: 0 0 auto;
|
||||
align-self: flex-start;
|
||||
background: var(--accent-bg);
|
||||
border: none;
|
||||
border-radius: 0 8px 8px 0;
|
||||
padding: 14px 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
color: var(--accent);
|
||||
transition: background 0.15s ease, padding 0.15s ease;
|
||||
margin-top: 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.collapse-btn:hover {
|
||||
background: var(--lavender-bg);
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.collapse-btn::after {
|
||||
content: attr(data-tip);
|
||||
position: absolute;
|
||||
right: calc(100% + 8px);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: var(--text-h);
|
||||
color: var(--bg-raised);
|
||||
font-family: var(--sans);
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.collapse-btn:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-family: var(--heading);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--accent);
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.bar-label {
|
||||
font-family: var(--sans);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--text-sub);
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.total-bar-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.total-bar-bg {
|
||||
flex: 1;
|
||||
height: 18px;
|
||||
background: var(--accent-bg);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.total-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--accent-dark), var(--lavender));
|
||||
border-radius: 10px;
|
||||
transition: width 0.3s ease;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.total-bar-text {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 400;
|
||||
color: var(--text-h);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
flex: 1;
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 300;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 400;
|
||||
color: var(--text-h);
|
||||
}
|
||||
|
||||
.total {
|
||||
font-weight: 400;
|
||||
color: var(--text-sub);
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.donut-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.donut-svg {
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
filter: drop-shadow(0 2px 8px rgba(99,102,241,0.15));
|
||||
}
|
||||
|
||||
.donut-label {
|
||||
fill: var(--text-h);
|
||||
font-family: var(--sans);
|
||||
font-weight: 300;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.seg-group:hover .donut-label {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.disclaimer {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--text-sub);
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
159
src/lib/world-map/WorldMap.svelte
Normal file
159
src/lib/world-map/WorldMap.svelte
Normal file
@@ -0,0 +1,159 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import * as d3 from 'd3';
|
||||
import { feature } from 'topojson-client';
|
||||
import worldData from 'world-atlas/countries-50m.json';
|
||||
import { getSelected, toggle, setTotalCount } from '../layout/selection.svelte.js';
|
||||
|
||||
const TERRITORY_PARENT = {
|
||||
'016': '840', '060': '826', '086': '826', '092': '826', '136': '826',
|
||||
'184': '554', '234': '208', '238': '826', '239': '826', '248': '246',
|
||||
'258': '250', '260': '250', '304': '208', '316': '840', '334': '036',
|
||||
'446': '156', '500': '826', '531': '528', '533': '528', '534': '528',
|
||||
'540': '250', '570': '554', '574': '036', '580': '840', '612': '826',
|
||||
'630': '840', '652': '250', '654': '826', '660': '826', '663': '250',
|
||||
'666': '250', '796': '826', '831': '826', '832': '826', '833': '826',
|
||||
'850': '840', '876': '250',
|
||||
};
|
||||
|
||||
function effId(d) {
|
||||
return TERRITORY_PARENT[d.id] || d.id;
|
||||
}
|
||||
|
||||
let frameEl;
|
||||
|
||||
function fitProjection(proj, w, h) {
|
||||
proj.fitSize([w, h], { type: 'Sphere' });
|
||||
const s = proj.scale() * 1.5;
|
||||
proj.scale(s).translate([w / 2, h * 0.70]);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const width = frameEl.clientWidth;
|
||||
const height = frameEl.clientHeight;
|
||||
|
||||
const projection = d3.geoMercator();
|
||||
fitProjection(projection, width, height);
|
||||
|
||||
const path = d3.geoPath().projection(projection);
|
||||
|
||||
const countries = feature(worldData, worldData.objects.countries)
|
||||
.features.filter(f => (f.id || f.properties.name === 'Kosovo') && f.id !== '010');
|
||||
|
||||
countries.forEach(f => { if (!f.id) f.id = 'XK'; });
|
||||
|
||||
const sovereignIds = new Set(countries.map(f => effId(f)));
|
||||
setTotalCount(sovereignIds.size);
|
||||
|
||||
const svg = d3.select(frameEl).append('svg').attr('width', width).attr('height', height);
|
||||
const g = svg.append('g');
|
||||
|
||||
const tooltip = d3.select(frameEl).append('div').attr('class', 'tooltip').style('display', 'none');
|
||||
|
||||
function updateFill(sel) {
|
||||
sel.attr('fill', d => getSelected().has(effId(d)) ? '#22c55e' : '#ffffff');
|
||||
g.selectAll('.micro-state').attr('fill', d => getSelected().has(effId(d)) ? '#22c55e' : '#ffffff');
|
||||
}
|
||||
|
||||
function attachEvents(sel) {
|
||||
sel
|
||||
.on('click', (event, d) => {
|
||||
toggle(effId(d));
|
||||
updateFill(d3.select(event.currentTarget));
|
||||
})
|
||||
.on('mouseenter', (event, d) => {
|
||||
d3.select(event.currentTarget).attr('fill', getSelected().has(effId(d)) ? '#16a34a' : '#f0f6fa');
|
||||
tooltip.style('display', 'block').text(d.properties.name);
|
||||
})
|
||||
.on('mousemove', (event) => {
|
||||
const [x, y] = d3.pointer(event, frameEl);
|
||||
tooltip.style('left', (x + 10) + 'px').style('top', (y - 28) + 'px');
|
||||
})
|
||||
.on('mouseleave', (event, d) => {
|
||||
d3.select(event.currentTarget).attr('fill', getSelected().has(effId(d)) ? '#22c55e' : '#ffffff');
|
||||
tooltip.style('display', 'none');
|
||||
});
|
||||
}
|
||||
|
||||
const paths = g.selectAll('path').data(countries).join('path')
|
||||
.attr('d', path).attr('fill', '#ffffff').attr('stroke', '#d4d4d4').attr('stroke-width', 0.5);
|
||||
attachEvents(paths);
|
||||
|
||||
function renderMicrostates() {
|
||||
g.selectAll('.micro-state').remove();
|
||||
const threshold = Math.max(4, 16 / d3.zoomTransform(svg.node()).k);
|
||||
paths.each(function (d) {
|
||||
if (effId(d) !== d.id) return;
|
||||
const { width, height } = this.getBBox();
|
||||
if (width < threshold && height < threshold) {
|
||||
const [cx, cy] = path.centroid(d);
|
||||
const c = g.append('circle').attr('class', 'micro-state').datum(d)
|
||||
.attr('cx', cx).attr('cy', cy).attr('r', 2)
|
||||
.attr('fill', getSelected().has(effId(d)) ? '#22c55e' : '#ffffff')
|
||||
.attr('stroke', '#94a3b8').attr('stroke-width', 0.5);
|
||||
attachEvents(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderMicrostates();
|
||||
|
||||
const zoom = d3.zoom().scaleExtent([1, 32]).on('zoom', (event) => {
|
||||
g.attr('transform', event.transform);
|
||||
renderMicrostates();
|
||||
});
|
||||
|
||||
svg.call(zoom);
|
||||
svg.on('dblclick.zoom', null);
|
||||
svg.on('dblclick', (event) => {
|
||||
const [x, y] = d3.pointer(event);
|
||||
svg.transition().duration(300).call(zoom.scaleBy, 2, [x, y]);
|
||||
});
|
||||
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
svg.attr('width', width).attr('height', height);
|
||||
fitProjection(projection, width, height);
|
||||
const countryPaths = g.selectAll('path');
|
||||
countryPaths.attr('d', path);
|
||||
updateFill(countryPaths);
|
||||
renderMicrostates();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(frameEl);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
svg.remove();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={frameEl} class="map-frame"></div>
|
||||
|
||||
<style>
|
||||
.map-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: #a4c8e0;
|
||||
}
|
||||
|
||||
.map-frame :global(svg) { display: block; cursor: grab; }
|
||||
.map-frame :global(svg:active) { cursor: grabbing; }
|
||||
.map-frame :global(svg path) { cursor: pointer; }
|
||||
|
||||
.map-frame :global(.tooltip) {
|
||||
position: absolute;
|
||||
padding: 4px 10px;
|
||||
background: #1f2937;
|
||||
color: #fff;
|
||||
font: 14px/1.4 sans-serif;
|
||||
border-radius: 4px;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
212
src/lib/world-map/continents.js
Normal file
212
src/lib/world-map/continents.js
Normal file
@@ -0,0 +1,212 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user