fchat-rising/components/FilterableSelect.vue

110 lines
3.8 KiB
Vue

<template>
<div class="dropdown filterable-select">
<button class="btn btn-default dropdown-toggle" :class="buttonClass" data-toggle="dropdown">
<span style="flex:1">
<template v-if="multiple">{{label}}</template>
<slot v-else :option="selected">{{label}}</slot>
</span>
<span class="caret" style="align-self:center;margin-left:5px"></span>
</button>
<div class="dropdown-menu filterable-select" @click.stop>
<div style="padding:10px;">
<input v-model="filter" class="form-control" :placeholder="placeholder"/>
</div>
<ul class="dropdown-menu">
<template v-if="multiple">
<li v-for="option in filtered">
<a href="#" @click.stop="select(option)">
<input type="checkbox" :checked="selected.indexOf(option) !== -1"/>
<slot :option="option">{{option}}</slot>
</a>
</li>
</template>
<template v-else>
<li v-for="option in filtered">
<a href="#" @click="select(option)">
<slot :option="option">{{option}}</slot>
</a>
</li>
</template>
</ul>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import Component from 'vue-class-component';
import {Prop, Watch} from 'vue-property-decorator';
@Component
export default class FilterableSelect extends Vue {
//tslint:disable:no-null-keyword
@Prop()
readonly placeholder?: string;
@Prop({required: true})
readonly options: object[];
@Prop({default: () => ((filter: RegExp, value: string) => filter.test(value))})
readonly filterFunc: (filter: RegExp, value: object) => boolean;
@Prop()
readonly multiple?: true;
@Prop()
readonly value?: object | object[];
@Prop()
readonly title?: string;
@Prop()
readonly buttonClass?: string;
filter = '';
selected: object | object[] | null = this.value !== undefined ? this.value : (this.multiple !== undefined ? [] : null);
@Watch('value')
watchValue(newValue: object | object[] | null): void {
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);
} else {
this.selected = item;
$('.dropdown-toggle', this.$el).dropdown('toggle');
}
this.$emit('input', this.selected);
}
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}` :
(this.selected !== null ? this.selected.toString() : this.title);
}
get filterRegex(): RegExp {
return new RegExp(this.filter.replace(/[^\w]/, '\\$&'), 'i');
}
}
</script>
<style lang="less">
.filterable-select {
ul.dropdown-menu {
padding: 0;
max-height: 200px;
overflow-y: auto;
position: static;
display: block;
border: 0;
box-shadow: none;
width: 100%;
}
button {
display: flex;
text-align: left
}
}
</style>