Files
granblue-party/components/ModalSelector.vue
2026-03-27 10:14:29 +03:00

176 lines
4.4 KiB
Vue

<template>
<modal :show="show" @close="close()">
<template v-slot:header>
<span class="flex flex-row flex-wrap items-center">
<input class="input input-sm" placeholder="Name" ref="nameField" type="text" v-model="name_searched">
<button class="btn btn-sm btn-red mx-2" @click="name_searched = ''">Clear</button>
<data-filter
class="my-2 mr-2"
v-for="(category, index) in getFilters"
:key="index"
:category="category.name"
:data="data_model[category.key].data">
</data-filter>
</span>
</template>
<div v-if="message.length < 1">
Loading...
</div>
<div v-else>
<table class="table">
<thead>
<tr>
<th v-for="(category, index) in getColumns" :key="index" class="whitespace-nowrap">{{ category.name }}</th>
</tr>
</thead>
<tbody>
<tr v-if="canUnselect" @click="selectItem(null)">
<td v-for="index in getColumns.length" :key="index">-</td>
</tr>
<tr v-for="item in getData" :key="item.id" @click="selectItem(item.id)">
<td v-for="(val, index) in getColumn(item)" :key="index">{{ val }}</td>
</tr>
</tbody>
</table>
</div>
<template v-slot:footer>
{{ getResultsCount }}
</template>
</modal>
</template>
<script>
import DataModel from '@/js/data-model'
import Utils from '@/js/utils'
import Modal from './common/Modal.vue'
import DataFilter from './common/DataFilter.vue'
export default {
model: {
prop: 'show',
event: 'close'
},
components: {
Modal,
DataFilter
},
props: {
show: {
type: Boolean,
required: true
},
route: {
type: String,
required: true
},
routeParameters: {
type: String,
default: undefined
},
categories: {
type: Array,
required: true
},
dataModel: {
type: Object,
default: undefined
},
canUnselect: {
type: Boolean,
default: true,
},
},
data() {
return {
message: [],
previousRoute: '',
data_model: {},
name_searched: '',
}
},
methods: {
selectItem(id) {
this.$emit('item-selected', id);
this.close();
},
close() {
this.$emit('close', false);
},
getColumn(item) {
return this.getColumns.map(c => {
return this.data_model[c.key].expand(item, this.getLang);
});
}
},
computed: {
getData() {
return this.message.filter(item => {
return item.n.toLowerCase().includes(this.name_searched.toLowerCase()) &&
this.getFilters.every(f => {
return this.data_model[f.key].show(item[f.key])
});
});
},
getResultsCount() {
if (this.getData.length == 1) {
return this.getData.length + ' result'
}
return this.getData.length + ' results'
},
getColumns() {
return this.categories.filter(c => { return c.isColumn });
},
getFilters() {
return this.categories.filter(c => { return c.isFilter });
},
getLang() {
return this.$store.getters.getLang;
}
},
watch: {
show() {
if (this.show) {
let currentRoute = this.route;
if (this.routeParameters !== undefined) {
currentRoute += '?' + this.routeParameters;
}
// Fetch the message again if the route changed
if (this.previousRoute !== currentRoute) {
this.message = [];
}
this.previousRoute = currentRoute;
if (this.message.length === 0) {
this.axios.get(currentRoute)
.then(response => this.message = response.data)
.catch(error => this.$store.dispatch('addAxiosErrorMessage', error));
}
let self = this;
this.$nextTick().then(() => {
self.name_searched = '';
self.$refs.nameField.focus();
});
}
}
},
mounted() {
// Copy the data model locally to modify "checked" properties
this.categories.forEach(c => {
this.$set(this.data_model, c.key, Utils.copy(DataModel[c.key]));
});
// Overload data model if specified
if (this.dataModel !== undefined) {
for (let [key, data] of Object.entries(this.dataModel)) {
this.$set(this.data_model, key, data);
}
}
}
}
</script>