mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-05 03:57:03 -04:00
131 lines
4.2 KiB
Vue
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>
|