PronounsPage/components/pronouns/PronounsDetailPage.vue

226 lines
7.5 KiB
Vue

<script setup lang="ts">
import { useNuxtApp } from 'nuxt/app';
import useConfig from '~/composables/useConfig.ts';
import useSimpleHead from '~/composables/useSimpleHead.ts';
import { ExampleCategory, SourceLibrary } from '~/src/classes.ts';
import type { Pronoun } from '~/src/classes.ts';
import { loadPronounExamples, loadPronounLibrary } from '~/src/data.ts';
const props = defineProps<{
pronoun: Pronoun;
showSources?: boolean;
}>();
const { $translator: translator } = useNuxtApp();
const config = useConfig();
const [pronounLibrary, pronounExamples] = await Promise.all([loadPronounLibrary(config), loadPronounExamples()]);
const glue = ` ${translator.translate('pronouns.or')} `;
useSimpleHead({
title: `${translator.translate('pronouns.intro')}: ${props.pronoun.name(glue)}`,
description: [
translator.translate('pronouns.examples.header', {}, false),
translator.translate('pronouns.grammarTable', {}, false),
translator.translate('sources.headerLong', {}, false),
].filter((x) => !!x).join(', '),
banner: `api/banner/${props.pronoun.canonicalName}.png`,
}, translator);
const { data: sources } = useFetch('/api/sources', { lazy: true });
const exampleCategories = ExampleCategory.from(pronounExamples, config);
const nameOptions = props.pronoun.nameOptions();
const pronounGroup = pronounLibrary.find(props.pronoun);
const comprehensive = useComprehensive();
const sourceLibrary = computed(() => {
if (sources.value === null) {
return null;
}
return new SourceLibrary(config, sources.value);
});
const groupedSources = computed(() => {
if (sourceLibrary.value === null || !props.showSources) {
return {};
}
let key = props.pronoun.canonicalName;
if (config.sources.enabled && config.sources.mergePronouns[key] !== undefined) {
key = config.sources.mergePronouns[key];
}
return sourceLibrary.value.getForPronounExtended(key);
});
const addSlash = (link: string) => {
return link + (['*', '\''].includes(link.substr(link.length - 1)) ? '/' : '');
};
const counter = ref(0);
const counterHandle = ref<number | undefined>();
const counterSpeed = ref(3000);
const setCounterInterval = () => {
if (counterHandle.value) {
clearInterval(counterHandle.value);
}
if (counterSpeed.value > 0) {
counter.value++;
counterHandle.value = setInterval((_) => counter.value++, counterSpeed.value);
}
};
watch(counterSpeed, () => setCounterInterval());
onMounted(() => setCounterInterval());
const counterPause = () => {
counterSpeed.value = 0;
};
const counterSlow = () => {
counterSpeed.value = 3000;
};
const counterFast = () => {
counterSpeed.value = 1000;
};
</script>
<template>
<h2
class="d-flex justify-content-between align-items-start align-items-md-center
flex-column flex-md-row gap-2"
>
<div>
<Icon v="tag" />
<T>pronouns.intro</T><T>quotation.colon</T>
</div>
<ComprehensiveSwitch v-model="comprehensive" />
<div v-if="nameOptions.length > 1" class="btn-group" role="group">
<button
type="button"
:class="['btn btn-sm', counterSpeed === 0 ? 'btn-primary' : 'btn-outline-primary']"
@click="counterPause"
>
<Icon v="pause" />
</button>
<button
type="button"
:class="['btn btn-sm', counterSpeed === 3000 ? 'btn-primary' : 'btn-outline-primary']"
@click="counterSlow"
>
<Icon v="play" />
</button>
<button
type="button"
:class="['btn btn-sm', counterSpeed === 1000 ? 'btn-primary' : 'btn-outline-primary']"
@click="counterFast"
>
<Icon v="forward" />
</button>
</div>
</h2>
<section>
<div class="alert alert-primary">
<h2 class="text-center mb-0">
<template v-if="nameOptions.length === 1">
<strong><Spelling escape :text="pronoun.name(glue)" /></strong><small v-if="pronoun.smallForm">/<Spelling :text="pronoun.morphemes[pronoun.smallForm] ?? ''" /></small>
</template>
<template v-else>
<template v-for="(nameOption, i) in nameOptions">
<nuxt-link :to="`/${addSlash(nameOption)}`">
<strong>
<Spelling :text="nameOption" escape />
</strong>
</nuxt-link>
<span v-if="i < nameOptions.length - 1"><Spelling :text="glue" /></span>
</template>
</template>
</h2>
<p v-if="pronoun.description" class="h6 small text-center mb-0 mt-2">
<em>
(<LinkedText
escape
noicons
:text="Array.isArray(pronoun.description)
? `${$t('pronouns.alt.header')}: ${pronoun.description.join(glue)}`
: pronoun.description"
/>)
</em>
</p>
</div>
</section>
<section>
<h2 class="h4">
<Icon v="file-signature" />
<T>pronouns.examples.header</T><T>quotation.colon</T>
</h2>
<ul>
<template v-for="exampleCategory in exampleCategories">
<ExampleCategoryListItem
v-if="!exampleCategory.comprehensive || comprehensive"
:example-category="exampleCategory"
:pronouns-choice="[pronoun]"
:counter="counter"
show-pronunciation
/>
</template>
</ul>
</section>
<CensusStat type="pronouns" :item="pronoun.name(glue)" colour="info" />
<section v-if="pronoun.history">
<template v-for="part in pronoun.history.replace(/\\@/g, '###').split('@')">
<template v-if="part === '__generator__'">
<div v-if="config.pronouns.generator?.disclaimer ?? true" class="alert alert-warning">
<Icon v="exclamation-triangle" />
<T>pronouns.generated</T>
</div>
</template>
<div v-else class="alert alert-light">
<Icon v="info-circle" />
<LinkedText :text="part.replace(/###/g, '@')" noicons />
</div>
</template>
</section>
<AdPlaceholder :phkey="['content-0', 'content-mobile-0']" />
<PronounsGrammarTables :pronoun :counter :comprehensive :pronoun-examples />
<AdPlaceholder :phkey="['content-1', 'content-mobile-1']" />
<PronounGroup
v-if="pronounGroup"
:pronoun-group="pronounGroup.group"
:pronouns="pronounGroup.groupPronouns"
/>
<Avoiding v-if="pronoun.nullPronoun" />
<section>
<Share :title="`${$t('pronouns.intro')}: ${pronoun.name(glue)}`" />
</section>
<section v-if="Object.values(groupedSources).filter(x => !!x).length">
<Literature :pronoun="pronoun" :sources="groupedSources" />
</section>
<AdPlaceholder :phkey="['content-2', 'content-mobile-2']" />
<Separator icon="info" />
<section class="mb-0">
<h2 class="h4">
<Icon v="info-circle" />
<T>home.whatisit</T>
</h2>
<T>home.about</T>
<Homepage align="center" />
</section>
</template>