PronounsPage/components/OpinionListInput.vue

133 lines
4.3 KiB
Vue

<template>
<ListInput v-model="v" :prototype="prototype" :group="group" :maxitems="maxitems">
<template #default="s">
<button
type="button"
:class="['btn', 'btn-outline-secondary', showOpinionSelector === s.i ? 'btn-secondary text-white border' : validate(s.val) ? 'btn-outline-danger' : '']"
@click="showOpinionSelector = showOpinionSelector === s.i ? false : s.i"
>
<Icon :v="getIcon(s.val.opinion)" />
</button>
<input v-model="s.val.value" :class="['form-control', 'mw-input', validate(s.val) ? 'border-danger' : '']" required :maxlength="maxlength" @keyup="s.update(s.val)">
<slot name="additional" :val="s.val"></slot>
<div v-if="showOpinionSelector === s.i" class="bg-light border rounded hanging shadow shadow-lg">
<ul class="list-unstyled icons-list p-1 text-center mb-0">
<li
v-for="(opinion, key) in opinions"
class="list-inline-item"
>
<button
type="button"
:class="['btn', key === s.val.opinion ? 'btn-dark' : 'btn-outline-dark', 'border-0 my-2']"
@click="s.val.opinion = key; showOpinionSelector = false"
>
<Icon :v="opinion.icon" />
</button>
</li>
</ul>
<ul v-if="customOpinions.length" class="list-unstyled icons-list p-1 text-center mb-0">
<li
v-for="opinion in customOpinions"
class="list-inline-item"
>
<button
type="button"
:class="['btn', opinion.icon === s.val.opinion ? 'btn-dark' : 'btn-outline-dark', 'border-0 my-2']"
@click="s.val.opinion = opinion.icon; showOpinionSelector = false"
>
<Icon :v="opinion.icon" />
</button>
</li>
</ul>
</div>
</template>
<template #validation="s">
<p v-if="validate(s.val)" class="small text-danger">
<Icon v="exclamation-triangle" />
<span class="ml-1">{{ $t(validate(s.val)) }}</span>
</p>
</template>
</ListInput>
</template>
<script>
import opinions from '../src/opinions.ts';
export default {
props: {
modelValue: {},
prototype: {
default: () => {
return { value: '', opinion: 'meh' };
},
type: Object,
},
group: {},
validation: {},
customOpinions: {
default: () => {
return [];
},
type: Array,
},
maxlength: { default: 32 },
maxitems: { default: null, type: Number },
},
emits: ['update:modelValue'],
data() {
return {
v: this.modelValue,
showOpinionSelector: false,
opinions,
};
},
watch: {
v() {
this.$emit('update:modelValue', this.v);
},
modelValue(v) {
this.v = v;
},
},
methods: {
validate(val) {
if (!this.getIcon(val.opinion)) {
return 'profile.opinions.validation.invalidOpinion';
}
if (!val.value) {
return null;
}
return this.validation && this.validation(val.value);
},
getIcon(opinion) {
if (Object.hasOwn(opinions, opinion)) {
return opinions[opinion].icon;
}
for (const op of this.customOpinions) {
if (op.icon === opinion) {
return opinion;
}
}
return '';
},
},
};
</script>
<style lang="scss" scoped>
@import "assets/variables";
.hanging {
position: absolute;
top: 100%;
left: 0;
width: 100%;
max-width: 300px;
z-index: 5000;
}
</style>