mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-08 23:14:43 -04:00
101 lines
3.4 KiB
Vue
101 lines
3.4 KiB
Vue
<script setup lang="ts">
|
|
import { loadNounsData } from '~/src/data.ts';
|
|
import { capitalise } from '~/src/helpers.ts';
|
|
import type { Numerus, NounWord } from '~/src/nouns.ts';
|
|
|
|
const props = withDefaults(defineProps<{
|
|
word: NounWord;
|
|
numerus?: Numerus;
|
|
singularOptions?: string[];
|
|
open?: boolean;
|
|
condense?: boolean;
|
|
tooltip?: boolean;
|
|
}>(), {
|
|
numerus: 'singular',
|
|
});
|
|
|
|
const nounsData = await loadNounsData();
|
|
|
|
const declensionByCase = computed((): Record<string, string[]> | undefined => {
|
|
if (props.word.declension === undefined) {
|
|
return undefined;
|
|
}
|
|
if (typeof props.word.declension === 'string') {
|
|
return nounsData.declensions?.[props.word.declension]?.[props.numerus];
|
|
}
|
|
return props.word.declension[props.numerus];
|
|
});
|
|
|
|
const nounConvention = computed(() => {
|
|
if (props.word.convention === undefined) {
|
|
return undefined;
|
|
}
|
|
return { ...nounsData.conventions?.[props.word.convention], key: props.word.convention };
|
|
});
|
|
|
|
const articles = computed(() => {
|
|
if (nounConvention.value === undefined) {
|
|
return {};
|
|
}
|
|
return Object.fromEntries(Object.entries(nounsData.classExample?.[props.numerus] ?? {})
|
|
.map(([caseAbbreviation, article]) => {
|
|
const resolvedArticle = article.replace(/\{([^}]+)}/, (_match, morpheme) => {
|
|
const value = nounConvention.value?.morphemes?.[morpheme];
|
|
if (value === undefined) {
|
|
return '';
|
|
}
|
|
return typeof value === 'string' ? value : value.spelling;
|
|
});
|
|
return [caseAbbreviation, resolvedArticle];
|
|
}));
|
|
});
|
|
|
|
const firstDeclension = computed(() => {
|
|
if (nounsData.cases === undefined) {
|
|
return props.word.spelling;
|
|
}
|
|
const caseAbbreviation = Object.keys(nounsData.cases)[0];
|
|
const ending = declensionByCase.value?.[caseAbbreviation][0] ?? '';
|
|
return `${articles.value[caseAbbreviation] ?? ''}${props.word.spelling}${ending}`;
|
|
});
|
|
|
|
const visible = ref(props.open);
|
|
</script>
|
|
|
|
<template>
|
|
<span class="position-relative">
|
|
<template v-if="declensionByCase">
|
|
<a v-if="!open" href="#" :class="tooltip && visible ? 'fw-bold' : ''" @click.prevent="visible = !visible"><Spelling :text="firstDeclension" /></a>
|
|
<ul v-if="visible" :class="['list-unstyled', 'small', open ? '' : 'm-2 p-3 pe-5 border bg-light', tooltip ? 'tooltip' : '']">
|
|
<li
|
|
v-for="(endings, caseAbbreviation) of declensionByCase"
|
|
:key="caseAbbreviation"
|
|
class="text-nowrap"
|
|
>
|
|
<strong>
|
|
{{ capitalise(caseAbbreviation) }}
|
|
<small v-if="!condense">({{ nounsData.cases?.[caseAbbreviation] }})</small>
|
|
</strong>
|
|
{{ endings.map(ending => `${articles?.[caseAbbreviation] ?? ''}${word.spelling}${ending}`).join(' / ') }}
|
|
</li>
|
|
<li v-if="tooltip" class="close"><a href="#" @click.prevent="visible = false"><Icon v="times" /></a></li>
|
|
</ul>
|
|
</template>
|
|
<Spelling v-else :text="firstDeclension" />
|
|
</span>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
ul.tooltip {
|
|
position: absolute;
|
|
top: 1rem;
|
|
left: 0;
|
|
z-index: 999;
|
|
li.close {
|
|
position: absolute;
|
|
top: 1rem;
|
|
right: 1rem;
|
|
}
|
|
}
|
|
</style>
|