mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-05 20:18:19 -04:00
132 lines
4.7 KiB
Vue
132 lines
4.7 KiB
Vue
<template>
|
|
<div id="map" :class="isDark ? 'dark' : ''"></div>
|
|
|
|
<!-- List different matching language versions if multiple locales match a single WALS code -->
|
|
<div v-if="dialog.visible" class="modal d-block-force modal-shown" @click.self="dialog.visible = false">
|
|
<div :class="['modal-dialog', 'modal-dialog-centered', 'modal-dialog-scrollable']" role="document">
|
|
<div class="modal-content shadow">
|
|
<div class="modal-body">
|
|
<div class="list-group">
|
|
<a
|
|
v-for="language in dialog.languages"
|
|
:key="language.walsCode"
|
|
:href="language.url"
|
|
target="_blank"
|
|
class="list-group-item list-group-item-action"
|
|
>
|
|
<div class="d-flex w-100 justify-content-between">
|
|
<h5 class="mb-1">{{ language.fullName }}</h5>
|
|
</div>
|
|
<p class="mb-1">{{ language.url }}</p>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { defineComponent, reactive } from 'vue';
|
|
|
|
import walsLanguages from '../assets/languages.tsv';
|
|
import locales from '../locale/locales.ts';
|
|
|
|
import useDark from '~/composables/useDark.ts';
|
|
import { clearUrl } from '~/src/helpers.ts';
|
|
|
|
const MOBILE_BREAKPOINT = 992;
|
|
|
|
const localesByWalsCode = locales.reduce((acc, language) => {
|
|
if (language.walsCode && language.published) {
|
|
acc[language.walsCode] = [...(acc[language.walsCode] || []), language];
|
|
}
|
|
return acc;
|
|
}, {});
|
|
|
|
export default defineComponent({
|
|
setup() {
|
|
useDark();
|
|
|
|
return {
|
|
dialog: reactive({
|
|
visible: false,
|
|
languages: [],
|
|
}),
|
|
};
|
|
},
|
|
async mounted() {
|
|
await this.$loadStylesheet(
|
|
'leaflet',
|
|
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css',
|
|
);
|
|
await this.$loadScript(
|
|
'leaflet',
|
|
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
|
|
);
|
|
await this.$loadScript(
|
|
'tinyworldmap',
|
|
'https://tinyworldmap.com/dist/v3/tiny-world-borders.js',
|
|
);
|
|
|
|
const pos = window.innerWidth < MOBILE_BREAKPOINT ? [45, 15] : [40, 65];
|
|
const map = window.L.map('map', { attributionControl: false }).setView(pos, 3);
|
|
|
|
window.L.control.attribution({ position: 'bottomright' })
|
|
.addAttribution('© <a href="https://doi.org/10.5281/zenodo.7385533" target="_blank" title="Dryer, Matthew S. & Haspelmath, Martin (eds.) 2013. The World Atlas of Language Structures Online. Leipzig: Max Planck Institute for Evolutionary Anthropology.">WALS</a>')
|
|
.addAttribution('<a href="https://wals.info" target="_blank">wals.info</a>')
|
|
.addTo(map);
|
|
|
|
new window.L.GridLayer.TinyWorld({ maxZoom: 19 }).addTo(map);
|
|
|
|
for (const walsLanguage of walsLanguages) {
|
|
if (!Object.hasOwn(localesByWalsCode, walsLanguage.id)) {
|
|
continue;
|
|
}
|
|
const locales = localesByWalsCode[walsLanguage.id];
|
|
|
|
const circle = window.L.circle([walsLanguage.latitude, walsLanguage.longitude], {
|
|
color: '#971064',
|
|
fillColor: '#c71585',
|
|
fillOpacity: 0.5,
|
|
radius: 300000 * Math.cos(walsLanguage.latitude * Math.PI / 180), // compensate for Mercator projection
|
|
}).addTo(map);
|
|
|
|
circle.bindTooltip(locales.map((locale) => `<strong>${locale.fullName}</strong><br/>${clearUrl(locale.url)}`).join('<br/>'));
|
|
|
|
if (locales.length === 1) {
|
|
// If only one locale matches the WALS code, open it directly on click
|
|
circle.on('click', () => {
|
|
window.open(locales[0].url);
|
|
return;
|
|
});
|
|
} else {
|
|
// If multiple locales match the WALS code, show a list of them in a modal
|
|
circle.on('click', () => {
|
|
this.dialog.languages = locales;
|
|
this.dialog.visible = true;
|
|
});
|
|
}
|
|
}
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
#map {
|
|
height: 500px;
|
|
z-index: 0;
|
|
&.dark {
|
|
.leaflet-layer,
|
|
.leaflet-control-zoom-in,
|
|
.leaflet-control-zoom-out,
|
|
.leaflet-control-attribution {
|
|
filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);
|
|
svg, img {
|
|
filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|