mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-29 16:04:38 -04:00
536 lines
25 KiB
Vue
536 lines
25 KiB
Vue
<template>
|
|
<Page v-if="$config.pronouns.enabled">
|
|
<section>
|
|
<Suggested />
|
|
|
|
<div
|
|
v-if="$config.pronouns.sentence && $config.pronouns.sentence.examples"
|
|
class="alert alert-info small mt-3"
|
|
>
|
|
<Icon v="lightbulb-on" />
|
|
<T>pronouns.sentence</T>
|
|
<ul class="mb-0">
|
|
<li v-for="example in $config.pronouns.sentence.examples">
|
|
<a :href="`https://${example}`" target="_blank" rel="noopener">{{ example }}</a>
|
|
</li>
|
|
</ul>
|
|
<T v-if="$te('pronouns.domainThanks')">pronouns.domainThanks</T>
|
|
</div>
|
|
|
|
<CardsBanner />
|
|
|
|
<AdPlaceholder :phkey="['content-0', 'content-mobile-0']" />
|
|
|
|
<ul class="list-group mt-4">
|
|
<template v-for="[group, groupPronouns] in pronounLibrary.split()">
|
|
<li v-if="!group.hidden" class="list-group-item">
|
|
<p class="h5">
|
|
<Spelling :text="group.name" />
|
|
</p>
|
|
<div v-if="group.description" class="small my-1">
|
|
<Icon v="info-circle" />
|
|
<LinkedText :text="group.description" />
|
|
</div>
|
|
<SimplePronounList :pronouns="groupPronouns" />
|
|
</li>
|
|
</template>
|
|
<li v-if="$config.pronouns.generator.enabled" id="generator" class="list-group-item">
|
|
<p class="h5">
|
|
<T>home.generator.header</T>
|
|
</p>
|
|
<p>
|
|
<T>home.generator.description</T>
|
|
</p>
|
|
<a v-if="!customise" href="#" class="btn btn-outline-primary w-100" @click.prevent="customise = true">
|
|
<Icon v="sliders-h-square" />
|
|
<T>home.generator.button</T>
|
|
</a>
|
|
<div v-else class="card mb-5">
|
|
<div class="card-header">
|
|
<Icon v="sliders-h-square" />
|
|
<T>home.generator.header2</T>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-title border-bottom pb-3">
|
|
<p><strong><T>home.generator.base</T><T>quotation.colon</T></strong></p>
|
|
<ul class="list-unstyled">
|
|
<template v-for="[group, groupPronouns] in pronounLibrary.split()">
|
|
<li v-if="!group.hidden">
|
|
<ul class="list-inline">
|
|
<li class="list-inline-item">
|
|
<Spelling :text="group.name" />
|
|
</li>
|
|
<template v-for="pronoun in deduplicatePronounGroup(groupPronouns)">
|
|
<li v-if="!pronoun.hidden" class="list-inline-item">
|
|
<button
|
|
:class="['btn', pronoun.name(glue) === selectedPronoun.name(glue) ? 'btn-primary' : 'btn-outline-primary', 'btn-sm', 'my-1']"
|
|
@click="selectedPronoun = pronoun.clone(true)"
|
|
>
|
|
<Spelling :text="pronoun.name(glue)" />
|
|
</button>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</div>
|
|
<div class="alert alert-primary">
|
|
<p class="h3 mb-0 text-center">
|
|
<Spelling escape :text="selectedPronoun.name(glue)" />
|
|
<template v-if="$config.pronouns.generator.description ?? true">
|
|
<br>
|
|
<input
|
|
v-model="selectedPronoun.description"
|
|
class="form-control form-input p-0 form-control-sm"
|
|
:size="selectedPronoun.description.length ? selectedPronoun.description.length + 3 : 16"
|
|
:maxlength="DESCRIPTION_MAXLENGTH"
|
|
:placeholder="$t('profile.description')"
|
|
>
|
|
</template>
|
|
</p>
|
|
</div>
|
|
<p>
|
|
<T>pronouns.examples.header</T><T>quotation.colon</T>
|
|
</p>
|
|
<template v-for="{ examples, isHonorific } in examplesByHonorific">
|
|
<ul>
|
|
<li v-for="example in examples">
|
|
<template v-for="part in clearExampleParts(example.parts(selectedPronoun))">
|
|
<input
|
|
v-if="part.variable"
|
|
v-model="selectedPronoun.morphemes[part.str]"
|
|
:class="['form-control form-input p-0', { active: selectedMorpheme === part.str }]"
|
|
:size="selectedPronoun.morphemes[part.str]?.length ?? 0"
|
|
maxlength="24"
|
|
@focus="selectedMorpheme = part.str"
|
|
@blur="selectedMorpheme = ''"
|
|
>
|
|
<span v-else><Spelling :text="part.str" /></span>
|
|
</template>
|
|
</li>
|
|
</ul>
|
|
<div v-if="$config.pronouns.plurals" class="my-3">
|
|
<div v-if="isHonorific" class="custom-control custom-switch">
|
|
<input
|
|
id="pluralHonorific"
|
|
v-model="selectedPronoun.pluralHonorific[0]"
|
|
type="checkbox"
|
|
class="custom-control-input"
|
|
>
|
|
<label class="custom-control-label" for="pluralHonorific">
|
|
<T>pronouns.plural</T>
|
|
<Icon v="level-up" />
|
|
</label>
|
|
</div>
|
|
<div v-else class="custom-control custom-switch">
|
|
<input
|
|
id="plural"
|
|
v-model="selectedPronoun.plural[0]"
|
|
type="checkbox"
|
|
class="custom-control-input"
|
|
>
|
|
<label class="custom-control-label" for="plural">
|
|
<T>pronouns.plural</T>
|
|
<Icon v="level-up" />
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<p class="small">
|
|
<T icon="info-circle">home.generator.alt</T>
|
|
</p>
|
|
<!-- TODO #136
|
|
<p class="small" v-if="$config.pronunciation.enabled && $te('home.generator.pronunciation')">
|
|
<Icon v="info-circle"/>
|
|
<T>home.generator.pronunciation</T>
|
|
</p>
|
|
-->
|
|
</div>
|
|
<div v-if="link" class="card-footer">
|
|
<LinkInput :link="link" />
|
|
<div
|
|
v-if="!usedBaseEquals && ($config.pronouns.generator.disclaimer ?? true)"
|
|
class="alert alert-warning"
|
|
>
|
|
<Icon v="exclamation-triangle" />
|
|
<T>pronouns.generated</T>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li id="multiple" class="list-group-item">
|
|
<p class="h5">
|
|
<Spelling :text="$config.pronouns.multiple.name" />
|
|
</p>
|
|
<div v-if="$config.pronouns.multiple.description" class="small my-1">
|
|
<Icon v="info-circle" />
|
|
<em><Spelling :text="$config.pronouns.multiple.description" /></em>
|
|
</div>
|
|
<SimplePronounList :pronouns="$config.pronouns.multiple.examples" class="mb-3" />
|
|
<a v-if="!customiseMultiple" href="#" class="btn btn-outline-primary w-100" @click.prevent="customiseMultiple = true">
|
|
<Icon v="sliders-h-square" />
|
|
<T>pronouns.alt.button</T>
|
|
</a>
|
|
<div v-else class="card">
|
|
<div class="card-header">
|
|
<Icon v="sliders-h-square" />
|
|
<T>pronouns.alt.button</T><T>quotation.colon</T>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="card-title">
|
|
<ul class="list-inline d-inline mb-0">
|
|
<template v-for="(pronoun, pronounName) in pronouns">
|
|
<li v-if="!pronoun.hidden" class="list-inline-item">
|
|
<button
|
|
:class="['btn', multiple.includes(pronounName) ? 'btn-primary' : 'btn-outline-primary', 'btn-sm', 'my-1']"
|
|
@click="toggleMultiple(pronounName)"
|
|
>
|
|
<Spelling :text="pronoun.name('')" />
|
|
</button>
|
|
</li>
|
|
</template>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div v-if="linkMultiple" class="card-footer">
|
|
<LinkInput :link="linkMultiple" />
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li v-if="$config.pronouns.null !== false" id="nameself" class="list-group-item">
|
|
<p class="h5">
|
|
<LinkedText :text="$config.pronouns.null.description" />
|
|
<NormativeBadge />
|
|
</p>
|
|
<div v-if="$config.pronouns.null.history" class="small my-1">
|
|
<Icon v="info-circle" />
|
|
<LinkedText :text="$config.pronouns.null.history" />
|
|
</div>
|
|
<SimplePronounList :pronouns="$config.pronouns.null.examples" class="mb-3" />
|
|
<button
|
|
v-if="!customiseNullPronouns"
|
|
type="button"
|
|
class="btn btn-outline-primary w-100"
|
|
@click.prevent="customiseNullPronouns = true"
|
|
>
|
|
<Icon v="sliders-h-square" />
|
|
<T>pronouns.null.button</T>
|
|
</button>
|
|
<div v-else class="card">
|
|
<div class="card-header">
|
|
<Icon v="sliders-h-square" />
|
|
<T>pronouns.null.button</T><T>quotation.colon</T>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-group">
|
|
<label for="nullPronounsBase"><T>pronouns.null.base</T></label>
|
|
<input
|
|
id="nullPronounsBase"
|
|
v-model="nullPronounsBase"
|
|
class="form-control"
|
|
:maxlength="NULL_PRONOUNS_MAXLENGTH"
|
|
>
|
|
</div>
|
|
</div>
|
|
<div v-if="nullPronounsLink" class="card-footer">
|
|
<LinkInput :link="nullPronounsLink" />
|
|
<div v-if="$config.pronouns.generator.disclaimer ?? true" class="alert alert-warning">
|
|
<Icon v="exclamation-triangle" />
|
|
<T>pronouns.generated</T>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li v-if="$config.pronouns.emoji !== false" class="list-group-item">
|
|
<p class="h5">
|
|
<Spelling :text="$config.pronouns.emoji.description" />
|
|
</p>
|
|
<div v-if="$config.pronouns.emoji.history" class="small my-1">
|
|
<Icon v="info-circle" />
|
|
<LinkedText :text="$config.pronouns.emoji.history" />
|
|
</div>
|
|
<SimplePronounList :pronouns="$config.pronouns.emoji.examples" class="mb-3" />
|
|
<button
|
|
v-if="!customiseEmojiPronouns"
|
|
type="button"
|
|
class="btn btn-outline-primary w-100"
|
|
@click.prevent="customiseEmojiPronouns = true"
|
|
>
|
|
<Icon v="sliders-h-square" />
|
|
<T>pronouns.emoji.button</T>
|
|
</button>
|
|
<div v-else class="card">
|
|
<div class="card-header">
|
|
<Icon v="sliders-h-square" />
|
|
<T>pronouns.emoji.button</T><T>quotation.colon</T>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-group">
|
|
<label for="emojiPronounsBase"><T>pronouns.emoji.base</T></label>
|
|
<input
|
|
id="emojiPronounsBase"
|
|
v-model="emojiPronounsBase"
|
|
class="form-control"
|
|
>
|
|
</div>
|
|
</div>
|
|
<div v-if="emojiPronounsLink" class="card-footer">
|
|
<LinkInput :link="emojiPronounsLink" />
|
|
<div v-if="$config.pronouns.generator.disclaimer ?? true" class="alert alert-warning">
|
|
<Icon v="exclamation-triangle" />
|
|
<T>pronouns.generated</T>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li v-if="$config.pronouns.mirror" id="mirror" class="list-group-item">
|
|
<p class="h5">
|
|
<nuxt-link :to="`/${$config.pronouns.mirror.route}`">
|
|
<LinkedText :text="$config.pronouns.mirror.name" />
|
|
</nuxt-link>
|
|
</p>
|
|
<div v-if="$config.pronouns.mirror.description" class="small my-1">
|
|
<Icon v="info-circle" />
|
|
<LinkedText :text="$config.pronouns.mirror.description" />
|
|
</div>
|
|
</li>
|
|
<li v-if="$config.pronouns.any" class="list-group-item">
|
|
<p class="h5">
|
|
<nuxt-link :to="`/${$config.pronouns.any}`">
|
|
<T>pronouns.any.header</T>
|
|
</nuxt-link>
|
|
</p>
|
|
<p>
|
|
<T>pronouns.any.description</T>
|
|
</p>
|
|
<ul v-if="Object.keys(pronounLibrary.byKey()).length" class="small">
|
|
<li>
|
|
<nuxt-link :to="`/${$config.pronouns.any}`">
|
|
<T>pronouns.any.short</T>
|
|
</nuxt-link>
|
|
</li>
|
|
<li v-for="(merged, key) in pronounLibrary.byKey()" :key="key">
|
|
<nuxt-link :to="`/${$config.pronouns.any}:${key}`">
|
|
<Spelling :text="merged.short($translator)" />
|
|
</nuxt-link>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<AdPlaceholder :phkey="['content-1', 'content-mobile-1']" />
|
|
|
|
<Separator icon="fist-raised" />
|
|
|
|
<Mission />
|
|
|
|
<Separator icon="heart" />
|
|
<Support />
|
|
<section>
|
|
<Share />
|
|
</section>
|
|
</Page>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import Vue from 'vue';
|
|
import { examples, pronouns, pronounLibrary } from '../src/data.ts';
|
|
import { NULL_PRONOUNS_MAXLENGTH } from '../src/buildPronoun.ts';
|
|
import { ExampleCategory, ExamplePart, Pronoun } from '../src/classes.ts';
|
|
import { isEmoji } from '../src/helpers.ts';
|
|
import Compressor from '../src/compressor.ts';
|
|
import MORPHEMES from '../data/pronouns/morphemes.ts';
|
|
import { mapState } from 'vuex';
|
|
import Suggested from '../data/pronouns/Suggested.vue';
|
|
import type { PronounLibrary, Example } from '../src/classes.ts';
|
|
|
|
interface PronounsData {
|
|
examplesByHonorific: { examples: Example[], isHonorific: boolean }[];
|
|
pronouns: Record<string, Pronoun>;
|
|
pronounLibrary: PronounLibrary;
|
|
selectedPronoun: Pronoun;
|
|
selectedMorpheme: string;
|
|
customiseMultiple: boolean;
|
|
multiple: string[];
|
|
customise: boolean;
|
|
customiseNullPronouns: boolean;
|
|
nullPronounsBase: string;
|
|
customiseEmojiPronouns: boolean;
|
|
emojiPronounsBase: string;
|
|
glue: string;
|
|
DESCRIPTION_MAXLENGTH: number;
|
|
NULL_PRONOUNS_MAXLENGTH: number;
|
|
}
|
|
|
|
export default Vue.extend({
|
|
components: { Suggested },
|
|
data(): PronounsData {
|
|
if (!this.$config.pronouns.enabled) {
|
|
throw null;
|
|
}
|
|
|
|
const exampleCategories = ExampleCategory.from(examples, this.$config);
|
|
const examplesByHonorific = [false, true].map((isHonorific) => {
|
|
const examples = exampleCategories
|
|
.filter((exampleCategory) => !exampleCategory.comprehensive)
|
|
.map((exampleCategory) => exampleCategory.examples[0])
|
|
.filter((example) => example.isHonorific === isHonorific);
|
|
return { examples, isHonorific };
|
|
}).filter(({ examples }) => examples.length > 0);
|
|
|
|
return {
|
|
examplesByHonorific,
|
|
pronouns,
|
|
pronounLibrary,
|
|
|
|
selectedPronoun: pronouns[this.$config.pronouns.default].clone(true),
|
|
selectedMorpheme: '',
|
|
|
|
customiseMultiple: false,
|
|
multiple: this.$config.pronouns.multiple ? this.$config.pronouns.multiple.examples[0].split('&') : [],
|
|
|
|
customise: this.$config.pronouns.generator.autoOpen ?? false,
|
|
|
|
customiseNullPronouns: false,
|
|
nullPronounsBase: '',
|
|
|
|
customiseEmojiPronouns: false,
|
|
emojiPronounsBase: '',
|
|
|
|
glue: ` ${this.$t('pronouns.or')} `,
|
|
|
|
DESCRIPTION_MAXLENGTH: Pronoun.DESCRIPTION_MAXLENGTH,
|
|
NULL_PRONOUNS_MAXLENGTH,
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState([
|
|
'user',
|
|
]),
|
|
usedBase(): string | null {
|
|
const name = this.selectedPronoun.name(this.glue);
|
|
for (const key in this.pronouns) {
|
|
if (this.pronouns.hasOwnProperty(key)) {
|
|
if (key === name) {
|
|
return key;
|
|
}
|
|
for (const alias of this.pronouns[key].aliases) {
|
|
if (alias === name) {
|
|
return key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
},
|
|
usedBaseEquals(): boolean {
|
|
return !!this.usedBase && this.selectedPronoun.equals(this.pronouns[this.usedBase], true);
|
|
},
|
|
longLink(): string {
|
|
const base = this.pronouns[this.selectedPronoun.morphemes[MORPHEMES[0]]!];
|
|
|
|
return base
|
|
? Compressor.compress(
|
|
this.selectedPronoun.toArray().map((x) => x.split('|')[0]),
|
|
base.toArray().map((x) => x.split('|')[0]),
|
|
).join(',')
|
|
: this.selectedPronoun.toString();
|
|
},
|
|
link(): string | null {
|
|
if (!this.selectedPronoun.pronoun()) {
|
|
return null;
|
|
}
|
|
|
|
const slashes = this.selectedPronoun.toStringSlashes(this.$translator);
|
|
|
|
let link;
|
|
if (this.usedBaseEquals) {
|
|
link = this.usedBase;
|
|
} else if (slashes) {
|
|
link = slashes;
|
|
} else {
|
|
link = this.longLink;
|
|
}
|
|
|
|
return this.addSlash(`${this.$base + (this.$config.pronouns.prefix || '')}/${link}`);
|
|
},
|
|
linkMultiple(): string | null {
|
|
if (!this.multiple.length) {
|
|
return null;
|
|
}
|
|
|
|
return this.addSlash(`${this.$base + (this.$config.pronouns.prefix || '')}/${this.multiple.join('&')}`);
|
|
},
|
|
nullPronounsLink(): string | null {
|
|
if (!this.nullPronounsBase) {
|
|
return null;
|
|
}
|
|
return `${this.$base + (this.$config.pronouns.prefix || '')}/:${this.nullPronounsBase}`;
|
|
},
|
|
emojiPronounsLink(): string | null {
|
|
if (!isEmoji(this.emojiPronounsBase)) {
|
|
return null;
|
|
}
|
|
return `${this.$base + (this.$config.pronouns.prefix || '')}/${this.emojiPronounsBase}`;
|
|
},
|
|
},
|
|
methods: {
|
|
toggleMultiple(name: string): void {
|
|
const index = this.multiple.indexOf(name);
|
|
if (index > -1) {
|
|
this.multiple.splice(index, 1);
|
|
} else {
|
|
this.multiple.push(name);
|
|
}
|
|
},
|
|
addSlash(link: string): string {
|
|
return link + (['*', '\''].includes(link.substr(link.length - 1)) ? '/' : '');
|
|
},
|
|
clearExampleParts(parts: ExamplePart[]): ExamplePart[] {
|
|
return parts.map((p) => new ExamplePart(p.variable, p.str.replace(/^'/, '')));
|
|
},
|
|
deduplicatePronounGroup(pronounGroup: Pronoun[]): Pronoun[] {
|
|
const dict: Record<string, Pronoun> = {};
|
|
for (const pronoun of pronounGroup) {
|
|
if (dict.hasOwnProperty(pronoun.name(this.glue))) {
|
|
continue;
|
|
}
|
|
dict[pronoun.name(this.glue)] = pronoun;
|
|
}
|
|
return Object.values(dict);
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "../assets/variables";
|
|
|
|
.form-input {
|
|
text-align: center;
|
|
&.active {
|
|
/*@include alert-variant(
|
|
theme-color-level('primary', $alert-bg-level),
|
|
theme-color-level('primary', $alert-border-level),
|
|
theme-color-level('primary', $alert-color-level)
|
|
);FIXME*/
|
|
}
|
|
&.form-control {
|
|
width: auto;
|
|
display: inline;
|
|
}
|
|
&[size="0"] {
|
|
width: .5rem !important;
|
|
}
|
|
}
|
|
|
|
/*@include media-breakpoint-up('md', $grid-breakpoints) {
|
|
.btn-md-lg {
|
|
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-line-height-lg, $btn-border-radius-lg);
|
|
}
|
|
}*/
|
|
</style>
|