mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-30 18:12:25 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			84 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			84 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Mixin for keyboard navigation in dropdown menus.
 | |
|  * This can be used in any component that has a dropdown menu with <li> items.
 | |
|  * The following example shows how to use this mixin in your component:
 | |
|  * <template>
 | |
|  *   <div>
 | |
|  *     <input type="text" @keydown="menuNavigationHandler">
 | |
|  *     <ul ref="menu">
 | |
|  *       <li v-for="(item, index) in itemsToShow" :key="index" :class="isMenuItemSelected(item) ? ... : ''" @click="clickedOption($event, item)">
 | |
|  *         {{ item }}
 | |
|  *       </li>
 | |
|  *     </ul>
 | |
|  *   </div>
 | |
|  * </template>
 | |
|  *
 | |
|  * This mixin assumes the following are defined in your component:
 | |
|  * itemsToShow: Array of items to show in the dropdown
 | |
|  * clickedOption: Event handler for when an item is clicked
 | |
|  * submitForm: Event handler for when the form is submitted
 | |
|  *
 | |
|  * It also assumes you have a ref="menu" on the menu element.
 | |
|  */
 | |
| export default {
 | |
|   data() {
 | |
|     return {
 | |
|       selectedMenuItemIndex: null
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     menuNavigationHandler(event) {
 | |
|       let items = this.itemsToShow
 | |
|       if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
 | |
|         event.preventDefault()
 | |
|         if (!items.length) return
 | |
|         if (event.key === 'ArrowDown') {
 | |
|           if (this.selectedMenuItemIndex === null) {
 | |
|             this.selectedMenuItemIndex = 0
 | |
|           } else {
 | |
|             this.selectedMenuItemIndex = Math.min(this.selectedMenuItemIndex + 1, items.length - 1)
 | |
|           }
 | |
|         } else if (event.key === 'ArrowUp') {
 | |
|           if (this.selectedMenuItemIndex === null) {
 | |
|             this.selectedMenuItemIndex = items.length - 1
 | |
|           } else {
 | |
|             this.selectedMenuItemIndex = Math.max(this.selectedMenuItemIndex - 1, 0)
 | |
|           }
 | |
|         }
 | |
|         this.recalcScroll()
 | |
|       } else if (event.key === 'Enter') {
 | |
|         event.preventDefault()
 | |
|         if (this.selectedMenuItemIndex !== null) {
 | |
|           this.clickedOption(event, items[this.selectedMenuItemIndex])
 | |
|         } else {
 | |
|           this.submitForm()
 | |
|         }
 | |
|       } else {
 | |
|         this.selectedMenuItemIndex = null
 | |
|       }
 | |
|     },
 | |
|     recalcScroll() {
 | |
|       const menu = this.$refs.menu
 | |
|       if (!menu) return
 | |
|       var menuItems = menu.querySelectorAll('li')
 | |
|       if (!menuItems.length) return
 | |
|       var selectedItem = menuItems[this.selectedMenuItemIndex]
 | |
|       if (!selectedItem) return
 | |
|       var menuHeight = menu.offsetHeight
 | |
|       var itemHeight = selectedItem.offsetHeight
 | |
|       var itemTop = selectedItem.offsetTop
 | |
|       var itemBottom = itemTop + itemHeight
 | |
|       if (itemBottom > menu.scrollTop + menuHeight) {
 | |
|         let menuPaddingBottom = parseFloat(window.getComputedStyle(menu).paddingBottom)
 | |
|         menu.scrollTop = itemBottom - menuHeight + menuPaddingBottom
 | |
|       } else if (itemTop < menu.scrollTop) {
 | |
|         let menuPaddingTop = parseFloat(window.getComputedStyle(menu).paddingTop)
 | |
|         menu.scrollTop = itemTop - menuPaddingTop
 | |
|       }
 | |
|     },
 | |
|     isMenuItemSelected(item) {
 | |
|       return this.selectedMenuItemIndex !== null && this.itemsToShow[this.selectedMenuItemIndex] === item
 | |
|     }
 | |
|   }
 | |
| }
 |