fchat-rising/components/FilterableSelect.vue

101 lines
3.4 KiB
Vue
Raw Normal View History

2017-09-02 01:50:31 +00:00
<template>
2019-09-17 17:14:14 +00:00
<dropdown class="filterable-select" linkClass="custom-select" :keepOpen="true">
<template slot="title" v-if="multiple">{{label}}</template>
<slot v-else slot="title" :option="selected">{{label}}</slot>
<div style="padding:10px;">
2019-09-17 17:14:14 +00:00
<input v-model="filter" class="form-control" :placeholder="placeholder" @mousedown.stop/>
</div>
<div class="dropdown-items">
<template v-if="multiple">
<a href="#" @click.stop="select(option)" v-for="option in filtered" class="dropdown-item">
2019-01-03 17:38:17 +00:00
<input type="checkbox" :checked="isSelected(option)"/>
<slot :option="option">{{option}}</slot>
</a>
</template>
<template v-else>
<a href="#" @click="select(option)" v-for="option in filtered" class="dropdown-item">
<slot :option="option">{{option}}</slot>
</a>
</template>
2017-09-02 01:50:31 +00:00
</div>
</dropdown>
2017-09-02 01:50:31 +00:00
</template>
<script lang="ts">
2019-01-03 17:38:17 +00:00
import {Component, Prop, Watch} from '@f-list/vue-ts';
2017-09-02 01:50:31 +00:00
import Vue from 'vue';
import Dropdown from '../components/Dropdown.vue';
2017-09-02 01:50:31 +00:00
@Component({
components: {dropdown: Dropdown}
})
2017-09-02 01:50:31 +00:00
export default class FilterableSelect extends Vue {
2019-09-17 17:14:14 +00:00
@Prop
2017-09-02 01:50:31 +00:00
readonly placeholder?: string;
@Prop({required: true})
readonly options!: object[];
2017-09-02 01:50:31 +00:00
@Prop({default: () => ((filter: RegExp, value: string) => filter.test(value))})
readonly filterFunc!: (filter: RegExp, value: object) => boolean;
2019-09-17 17:14:14 +00:00
@Prop
2017-09-02 01:50:31 +00:00
readonly multiple?: true;
2019-09-17 17:14:14 +00:00
@Prop
2017-09-02 01:50:31 +00:00
readonly value?: object | object[];
2019-09-17 17:14:14 +00:00
@Prop
2017-09-02 01:50:31 +00:00
readonly title?: string;
filter = '';
2019-01-03 17:38:17 +00:00
selected: object | object[] | undefined = this.value !== undefined ? this.value : (this.multiple !== undefined ? [] : undefined);
2017-09-02 01:50:31 +00:00
@Watch('value')
2019-01-03 17:38:17 +00:00
watchValue(newValue: object | object[] | undefined): void {
2017-09-02 01:50:31 +00:00
this.selected = newValue;
}
select(item: object): void {
if(this.multiple !== undefined) {
const selected = <object[]>this.selected;
const index = selected.indexOf(item);
if(index === -1) selected.push(item);
else selected.splice(index, 1);
2019-09-17 17:14:14 +00:00
} else
2018-04-11 19:17:58 +00:00
this.selected = item;
2017-09-02 01:50:31 +00:00
this.$emit('input', this.selected);
}
2019-01-03 17:38:17 +00:00
isSelected(option: object): boolean {
return (<object[]>this.selected).indexOf(option) !== -1;
}
2017-09-02 01:50:31 +00:00
get filtered(): object[] {
return this.options.filter((x) => this.filterFunc(this.filterRegex, x));
}
get label(): string | undefined {
return this.multiple !== undefined ? `${this.title} - ${(<object[]>this.selected).length}` :
2019-01-03 17:38:17 +00:00
(this.selected !== undefined ? this.selected.toString() : this.title);
2017-09-02 01:50:31 +00:00
}
get filterRegex(): RegExp {
return new RegExp(this.filter.replace(/[^\w]/gi, '\\$&'), 'i');
2017-09-02 01:50:31 +00:00
}
}
</script>
<style lang="scss">
2017-09-02 01:50:31 +00:00
.filterable-select {
.dropdown-items {
2017-09-02 01:50:31 +00:00
max-height: 200px;
overflow-y: auto;
}
2019-09-17 17:14:14 +00:00
2017-09-02 01:50:31 +00:00
button {
display: flex;
text-align: left
}
2017-10-16 23:58:57 +00:00
input[type=checkbox] {
vertical-align: text-bottom;
margin-right: 5px;
}
2017-09-02 01:50:31 +00:00
}
</style>