PronounsPage/components/nouns/NounsDeclension.vue
2025-07-27 23:43:57 +02:00

95 lines
2.8 KiB
Vue

<script setup lang="ts">
import type { NounDeclension } from '~/src/classes.ts';
import { loadNounDeclensionTemplates, loadNounsData } from '~/src/data.ts';
const props = withDefaults(defineProps<{
word: string;
plural?: boolean;
singularOptions?: string[];
template?: NounDeclension | null;
open?: boolean;
condense?: boolean;
tooltip?: boolean;
}>(), {
plural: false,
});
const [nounsData, nounDeclensionTemplates] = await Promise.all([loadNounsData(), loadNounDeclensionTemplates()]);
const visible = ref(props.open);
const declensionTemplate = computed(() => {
return props.template ?? findTemplate();
});
const findTemplate = (): NounDeclension | null => {
let longestMatch = 0;
let templates: NounDeclension[] = [];
for (const t of nounDeclensionTemplates) {
const matchLength = t.matches(props.word, props.plural);
if (matchLength === 0) {
continue;
}
if (matchLength > longestMatch) {
longestMatch = matchLength;
templates = [t];
} else if (matchLength === longestMatch) {
templates.push(t);
}
}
if (!templates.length) {
return null;
} else if (templates.length === 1) {
return templates[0];
} else if (props.plural && props.singularOptions) {
for (const t of templates) {
for (const s of props.singularOptions) {
if (t.matches(s)) {
return t;
}
}
}
}
return templates[0];
};
</script>
<template>
<span class="position-relative">
<template v-if="declensionTemplate">
<a v-if="!open" href="#" :class="tooltip && visible ? 'fw-bold' : ''" @click.prevent="visible = !visible"><Spelling :text="word" /></a>
<ul v-if="visible" :class="['list-unstyled', 'small', open ? '' : 'm-2 p-3 pe-5 border bg-light', tooltip ? 'tooltip' : '']">
<li
v-for="(declined, caseName) in declensionTemplate.decline(word, plural)"
:key="caseName"
class="text-nowrap"
>
<strong>
{{ caseName }}
<small v-if="!condense">({{ nounsData.cases?.[caseName.toLowerCase()] }})</small>
</strong>
{{ declined.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="word" />
</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>