diff options
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue')
-rw-r--r-- | app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue b/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue new file mode 100644 index 00000000000..5d0ed8b0821 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/dropdown_keyboard_navigation.vue @@ -0,0 +1,81 @@ +<script> +import { UP_KEY_CODE, DOWN_KEY_CODE, TAB_KEY_CODE } from '~/lib/utils/keycodes'; + +export default { + model: { + prop: 'index', + event: 'change', + }, + props: { + /* v-model property to manage location in list */ + index: { + type: Number, + required: true, + }, + /* Highest index that can be navigated to */ + max: { + type: Number, + required: true, + }, + /* Lowest index that can be navigated to */ + min: { + type: Number, + required: true, + }, + /* Which index to set v-model to on init */ + defaultIndex: { + type: Number, + required: true, + }, + }, + watch: { + max() { + // If the max index (list length) changes, reset the index + this.$emit('change', this.defaultIndex); + }, + }, + created() { + this.$emit('change', this.defaultIndex); + document.addEventListener('keydown', this.handleKeydown); + }, + beforeDestroy() { + document.removeEventListener('keydown', this.handleKeydown); + }, + methods: { + handleKeydown(event) { + if (event.keyCode === DOWN_KEY_CODE) { + // Prevents moving scrollbar + event.preventDefault(); + event.stopPropagation(); + // Moves to next index + this.increment(1); + } else if (event.keyCode === UP_KEY_CODE) { + // Prevents moving scrollbar + event.preventDefault(); + event.stopPropagation(); + // Moves to previous index + this.increment(-1); + } else if (event.keyCode === TAB_KEY_CODE) { + this.$emit('tab'); + } + }, + increment(val) { + if (this.max === 0) { + return; + } + + const nextIndex = Math.max(this.min, Math.min(this.index + val, this.max)); + + // Return if the index didn't change + if (nextIndex === this.index) { + return; + } + + this.$emit('change', nextIndex); + }, + }, + render() { + return this.$slots.default; + }, +}; +</script> |