PronounsPage/components/ListInput.vue

119 lines
3.6 KiB
Vue

<template>
<draggable
v-model="iVal"
tag="ul"
handle=".handle"
ghost-class="ghost"
class="list-unstyled"
:group="group"
@end="$emit('input', iVal)"
@add="$emit('input', iVal)"
>
<li v-for="(v, i) in iVal" ref="items">
<div>
<div class="input-group input-group-sm mb-1">
<button v-if="!disableSorting" :class="['btn', 'btn-light border', readonly ? '' : 'handle']" type="button" :aria-label="$t('table.sort')" :disabled="readonly">
<Icon v="bars" />
</button>
<slot :val="iVal[i]" :update="curry(update)(i)" :i="i">
<input
v-model="iVal[i]"
type="text"
class="form-control"
required
:readonly="readonly"
:maxlength="maxlength"
>
</slot>
<button
v-if="i >= minitems"
:class="['btn', readonly ? 'btn-light border' : 'btn-outline-danger']"
type="button"
:aria-label="$t('crud.remove')"
:disabled="readonly"
@click.prevent="remove(i)"
>
<Icon v="times" />
</button>
</div>
<slot name="validation" :val="iVal[i]" :i="i"></slot>
</div>
</li>
<li slot="footer">
<button
v-if="!readonly && (maxitems === null || iVal.length < maxitems)"
class="btn btn-outline-success w-100 btn-sm"
type="button"
:aria-label="$t('crud.add')"
@click.prevent="add"
>
<Icon v="plus" />
</button>
</li>
<li v-if="maxitems && iVal.length > maxitems" class="alert alert-danger">
<Icon v="exclamation-triangle" />
<T :params="{ maxlength: maxitems }" class="ml-1">crud.validation.listMaxLength</T>
</li>
</draggable>
</template>
<script>
import { curry } from '../src/helpers.ts';
import draggable from 'vuedraggable';
export default {
components: {
draggable,
},
props: {
value: {},
prototype: { default: '' },
group: {},
readonly: { type: Boolean },
maxlength: { default: 32, type: Number },
minitems: { default: 0, type: Number },
maxitems: { default: null, type: Number },
disableSorting: { type: Boolean },
},
data() {
return {
iVal: this.value,
curry,
};
},
watch: {
value() {
this.iVal = this.value;
},
},
methods: {
remove(i) {
const v = [...this.value];
v.splice(i, 1);
this.$emit('input', v);
},
add() {
// create a deep copy of the prototype
const addedItem = JSON.parse(JSON.stringify(this.prototype));
this.$emit('input', [...this.value, addedItem]);
this.$nextTick((_) => {
this.$refs.items[this.value.length - 1].querySelector('input,textarea,select').focus();
});
},
update(i, val) {
const v = [...this.value];
v[i] = val;
this.$emit('input', v);
},
},
};
</script>
<style lang="scss" scoped>
.ghost {
opacity: 0.5;
background: #c8ebfb;
}
</style>