mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-14 08:28:09 -04:00
124 lines
3.3 KiB
Vue
124 lines
3.3 KiB
Vue
<template>
|
|
<div class="select flex-grow-1">
|
|
<input
|
|
ref="filter"
|
|
v-model="filter"
|
|
type="text"
|
|
class="form-control"
|
|
v-bind="$attrs"
|
|
@focus="show"
|
|
@blur="hide"
|
|
@keydown="filterKeydown"
|
|
>
|
|
<div v-show="shown" class="list-group shadow">
|
|
<a
|
|
v-for="(display, option) in visibleOptions()"
|
|
:class="['list-group-item', 'list-group-item-action', highlightedOption === option ? 'active' : '']"
|
|
href="#"
|
|
@click.prevent="select(option)"
|
|
>
|
|
{{ display }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
export default {
|
|
inheritAttrs: false,
|
|
props: {
|
|
modelValue: { required: true, type: String },
|
|
options: { required: true },
|
|
freeform: { type: Boolean },
|
|
},
|
|
emits: ['update:modelValue'],
|
|
data() {
|
|
return {
|
|
filter: this.options[this.modelValue] || this.modelValue || '',
|
|
shown: false,
|
|
highlighted: -1,
|
|
};
|
|
},
|
|
computed: {
|
|
highlightedOption() {
|
|
return Object.keys(this.visibleOptions())[this.highlighted];
|
|
},
|
|
},
|
|
watch: {
|
|
modelValue() {
|
|
this.filter = this.options[this.modelValue] || this.modelValue;
|
|
this.highlighted = -1;
|
|
},
|
|
filter() {
|
|
if (this.freeform) {
|
|
this.$emit('update:modelValue', this.filter);
|
|
}
|
|
},
|
|
},
|
|
methods: {
|
|
select(option) {
|
|
this.$emit('update:modelValue', option);
|
|
this.hide();
|
|
this.highlighted = -1;
|
|
},
|
|
show() {
|
|
this.shown = true;
|
|
},
|
|
hide() {
|
|
setTimeout(() => {
|
|
this.shown = false;
|
|
}, 100);
|
|
},
|
|
filterKeydown(e) {
|
|
if (!this.shown) {
|
|
this.show();
|
|
}
|
|
|
|
switch (e.key) {
|
|
case 'ArrowUp':
|
|
this.highlighted--;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
break;
|
|
case 'ArrowDown':
|
|
this.highlighted++;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
break;
|
|
case 'Enter':
|
|
if (this.highlightedOption) {
|
|
this.select(this.highlightedOption);
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
visibleOptions() {
|
|
return Object.fromEntries(Object.entries(this.options).filter(([option, display]) => {
|
|
return !this.filter ||
|
|
option.toLowerCase().includes(this.filter.toLowerCase()) ||
|
|
display.toLowerCase().includes(this.filter.toLowerCase());
|
|
}));
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.select {
|
|
position: relative;
|
|
.list-group {
|
|
position: absolute;
|
|
top: 100%;
|
|
max-height: 300px;
|
|
width: min(300px, 100%);
|
|
overflow-y: auto;
|
|
z-index: 999;
|
|
}
|
|
}
|
|
</style>
|