PronounsPage/components/ButtonList.vue
Valentyne Stigloher b25afefc49 (fmt)
2024-10-29 10:56:32 +01:00

131 lines
4.2 KiB
Vue

<template>
<div>
<draggable
v-model="iVal"
tag="ul"
ghost-class="ghost"
:group="ulid"
class="list-inline border rounded drop-empty px-3 py-2"
:item-key="(element: string) => element"
@end="$emit('update:modelValue', iVal)"
>
<template #item="{ element }">
<li v-if="options[element]" :key="element" class="list-inline-item py-1">
<a
href="#"
class="badge bg-light text-dark border p-2"
@click.prevent="$emit('update:modelValue', iVal.filter(v => v !== element))"
>
<slot :v="element" :desc="options[element]">
{{ element }}
</slot>
<span class="text-danger">
<Icon v="times" />
</span>
</a>
</li>
</template>
</draggable>
<div class="input-group py-1">
<input v-model="search" class="form-control" :placeholder="$t('crud.search')">
<button v-if="search" type="button" class="btn btn-light btn-sm border text-danger" @click.prevent="search = ''">
<Icon v="times" />
</button>
<button v-if="all" type="button" class="btn btn-light btn-sm border" @click.prevent="all = false">
<Icon v="caret-up" />
</button>
<button v-else type="button" class="btn btn-light btn-sm border" @click.prevent="all = true">
<Icon v="caret-down" />
</button>
</div>
<draggable
v-model="remainingOptions"
tag="ul"
ghost-class="ghost"
class="list-inline"
:group="ulid"
:item-key="(element: string) => element"
@end="$emit('update:modelValue', iVal)"
>
<template #item="{ element }">
<li v-if="isRemainingOptionVisible(element)" class="list-inline-item py-1">
<a
href="#"
class="badge bg-light text-dark p-2"
@click.prevent="$emit('update:modelValue', [...iVal, element])"
>
<slot :v="element" :desc="options[element]">
{{ element }}
</slot>
<span class="text-success">
<Icon v="plus" />
</span>
</a>
</li>
</template>
</draggable>
</div>
</template>
<script lang="ts">
import { ulid } from 'ulid';
import { defineComponent } from 'vue';
import type { PropType } from 'vue';
import draggable from 'vuedraggable';
export default defineComponent({
components: {
draggable,
},
props: {
modelValue: { required: true, type: Array as PropType<string[]> },
options: { required: true, type: Object as PropType<Record<string, string>> },
},
emits: ['update:modelValue'],
data() {
return {
iVal: this.modelValue,
remainingOptions: [] as string[],
ulid: ulid(),
search: '',
all: false,
};
},
watch: {
modelValue() {
this.iVal = this.modelValue;
this.remainingOptions = this.buildRemainingOptions();
this.search = '';
},
},
created() {
this.remainingOptions = this.buildRemainingOptions();
},
methods: {
buildRemainingOptions(): string[] {
return Object.keys(this.options).filter((o) => !this.modelValue.includes(o));
},
isRemainingOptionVisible(val: string): boolean {
if (this.search) {
return this.options[val].toLowerCase().includes(this.search.toLowerCase());
}
return this.all;
},
},
});
</script>
<style lang="scss" scoped>
.ghost {
opacity: 0.5;
background: #c8ebfb;
}
.drop-empty:empty {
min-height: 3em;
}
</style>