mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Update:Cleanup UI/UX for filter and sort dropdowns
This commit is contained in:
		
							parent
							
								
									1392baf1eb
								
							
						
					
					
						commit
						a9f5c64204
					
				| @ -1,6 +1,6 @@ | ||||
| <template> | ||||
|   <div ref="wrapper" class="relative" v-click-outside="clickOutside"> | ||||
|     <button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none sm:text-sm cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu"> | ||||
|     <button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu"> | ||||
|       <span class="flex items-center justify-between"> | ||||
|         <span class="block truncate text-xs">{{ selectedText }}</span> | ||||
|       </span> | ||||
| @ -14,12 +14,17 @@ | ||||
|       </div> | ||||
|     </button> | ||||
| 
 | ||||
|     <div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"> | ||||
|     <div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 text-sm ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none"> | ||||
|       <ul class="h-full w-full" role="listbox" aria-labelledby="listbox-label"> | ||||
|         <template v-for="item in items"> | ||||
|           <li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item)"> | ||||
|           <li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="option" @click="clickedOption(item)"> | ||||
|             <div class="flex items-center justify-between"> | ||||
|               <span class="font-normal ml-3 block truncate text-sm md:text-base">{{ item.text }}</span> | ||||
|               <span class="font-normal ml-3 block truncate">{{ item.text }}</span> | ||||
|             </div> | ||||
| 
 | ||||
|             <!-- selected checkmark icon --> | ||||
|             <div v-if="item.value === selected" class="absolute inset-y-0 right-2 h-full flex items-center pointer-events-none"> | ||||
|               <span class="material-icons text-base text-yellow-400">check</span> | ||||
|             </div> | ||||
|           </li> | ||||
|         </template> | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <template> | ||||
|   <div ref="wrapper" class="relative" v-click-outside="clickOutside"> | ||||
|     <button type="button" class="relative w-full h-full bg-fg border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none sm:text-sm cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu"> | ||||
|     <button type="button" class="relative w-full h-full bg-bg border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none sm:text-sm cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu"> | ||||
|       <span class="flex items-center justify-between"> | ||||
|         <span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span> | ||||
|       </span> | ||||
| @ -9,7 +9,7 @@ | ||||
|           <path fill-rule="evenodd" d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" /> | ||||
|         </svg> | ||||
|       </span> | ||||
|       <div v-else class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer text-gray-400 hover:text-gray-300" @mousedown.stop @mouseup.stop @click.stop.prevent="clearSelected"> | ||||
|       <div v-else class="ml-3 absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer text-gray-400 hover:text-gray-200" @mousedown.stop @mouseup.stop @click.stop.prevent="clearSelected"> | ||||
|         <span class="material-icons" style="font-size: 1.1rem">close</span> | ||||
|       </div> | ||||
|     </button> | ||||
| @ -17,23 +17,27 @@ | ||||
|     <div v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-sm libraryFilterMenu"> | ||||
|       <ul v-show="!sublist" class="h-full w-full" role="listbox" aria-labelledby="listbox-label"> | ||||
|         <template v-for="item in selectItems"> | ||||
|           <li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item)"> | ||||
|           <li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="option" @click="clickedOption(item)"> | ||||
|             <div class="flex items-center justify-between"> | ||||
|               <span class="font-normal ml-3 block truncate text-sm">{{ item.text }}</span> | ||||
|             </div> | ||||
|             <div v-if="item.sublist" class="absolute right-1 top-0 bottom-0 h-full flex items-center"> | ||||
|               <span class="material-icons text-2xl">arrow_right</span> | ||||
|             </div> | ||||
|             <!-- selected checkmark icon --> | ||||
|             <div v-if="item.value === selected" class="absolute inset-y-0 right-2 h-full flex items-center pointer-events-none"> | ||||
|               <span class="material-icons text-base text-yellow-400">check</span> | ||||
|             </div> | ||||
|           </li> | ||||
|         </template> | ||||
|       </ul> | ||||
|       <ul v-show="sublist" class="h-full w-full" role="listbox" aria-labelledby="listbox-label"> | ||||
|         <li class="text-gray-50 select-none relative py-2 pl-9 cursor-pointer hover:bg-black-400" role="option" @click="sublist = null"> | ||||
|         <li class="text-gray-50 select-none relative py-2 pl-9 cursor-pointer hover:bg-white/5" role="option" @click="sublist = null"> | ||||
|           <div class="absolute left-1 top-0 bottom-0 h-full flex items-center"> | ||||
|             <span class="material-icons text-2xl">arrow_left</span> | ||||
|           </div> | ||||
|           <div class="flex items-center justify-between"> | ||||
|             <span class="font-normal ml-3 block truncate">Back</span> | ||||
|             <span class="font-normal block truncate">Back</span> | ||||
|           </div> | ||||
|         </li> | ||||
|         <li v-if="!sublistItems.length" class="text-gray-400 select-none relative px-2" role="option"> | ||||
| @ -41,16 +45,15 @@ | ||||
|             <span class="font-normal block truncate py-2">No {{ sublist }}</span> | ||||
|           </div> | ||||
|         </li> | ||||
|         <li v-else-if="sublist === 'series'" class="text-gray-50 select-none relative px-2 cursor-pointer hover:bg-black-400" role="option" @click="clickedSublistOption($encode('no-series'))"> | ||||
|           <div class="flex items-center"> | ||||
|             <span class="font-normal block truncate py-2 text-xs text-white text-opacity-80">{{ $strings.MessageNoSeries }}</span> | ||||
|           </div> | ||||
|         </li> | ||||
|         <template v-for="item in sublistItems"> | ||||
|           <li :key="item.value" class="text-gray-50 select-none relative px-2 cursor-pointer hover:bg-black-400" :class="`${sublist}.${item.value}` === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedSublistOption(item.value)"> | ||||
|           <li :key="item.value" class="select-none relative px-2 cursor-pointer hover:bg-white/5" :class="`${sublist}.${item.value}` === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="option" @click="clickedSublistOption(item.value)"> | ||||
|             <div class="flex items-center"> | ||||
|               <span class="font-normal truncate py-2 text-xs">{{ item.text }}</span> | ||||
|             </div> | ||||
|             <!-- selected checkmark icon --> | ||||
|             <div v-if="`${sublist}.${item.value}` === selected" class="absolute inset-y-0 right-2 h-full flex items-center pointer-events-none"> | ||||
|               <span class="material-icons text-base text-yellow-400">check</span> | ||||
|             </div> | ||||
|           </li> | ||||
|         </template> | ||||
|       </ul> | ||||
| @ -72,9 +75,8 @@ export default { | ||||
|   }, | ||||
|   watch: { | ||||
|     showMenu(newVal) { | ||||
|       if (!newVal) { | ||||
|         if (this.sublist && !this.selectedItemSublist) this.sublist = null | ||||
|         if (!this.sublist && this.selectedItemSublist) this.sublist = this.selectedItemSublist | ||||
|       if (newVal) { | ||||
|         this.sublist = this.selectedItemSublist | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -260,20 +262,20 @@ export default { | ||||
|       return this.bookItems | ||||
|     }, | ||||
|     selectedItemSublist() { | ||||
|       return this.selected && this.selected.includes('.') ? this.selected.split('.')[0] : false | ||||
|       return this.selected?.includes('.') ? this.selected.split('.')[0] : null | ||||
|     }, | ||||
|     selectedText() { | ||||
|       if (!this.selected) return '' | ||||
|       var parts = this.selected.split('.') | ||||
|       var filterName = this.selectItems.find((i) => i.value === parts[0]) | ||||
|       var filterValue = null | ||||
|       const parts = this.selected.split('.') | ||||
|       const filterName = this.selectItems.find((i) => i.value === parts[0]) | ||||
|       let filterValue = null | ||||
|       if (parts.length > 1) { | ||||
|         var decoded = this.$decode(parts[1]) | ||||
|         const decoded = this.$decode(parts[1]) | ||||
|         if (decoded.startsWith('aut_')) { | ||||
|           var author = this.authors.find((au) => au.id == decoded) | ||||
|           const author = this.authors.find((au) => au.id == decoded) | ||||
|           if (author) filterValue = author.name | ||||
|         } else if (decoded.startsWith('ser_')) { | ||||
|           var series = this.series.find((se) => se.id == decoded) | ||||
|           const series = this.series.find((se) => se.id == decoded) | ||||
|           if (series) filterValue = series.name | ||||
|         } else { | ||||
|           filterValue = decoded | ||||
| @ -396,7 +398,7 @@ export default { | ||||
|       ] | ||||
|     }, | ||||
|     sublistItems() { | ||||
|       return (this[this.sublist] || []).map((item) => { | ||||
|       const sublistItems = (this[this.sublist] || []).map((item) => { | ||||
|         if (typeof item === 'string') { | ||||
|           return { | ||||
|             text: item, | ||||
| @ -409,6 +411,13 @@ export default { | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|       if (this.sublist === 'series') { | ||||
|         sublistItems.unshift({ | ||||
|           text: this.$strings.MessageNoSeries, | ||||
|           value: this.$encode('no-series') | ||||
|         }) | ||||
|       } | ||||
|       return sublistItems | ||||
|     }, | ||||
|     filterData() { | ||||
|       return this.$store.state.libraries.filterData || {} | ||||
| @ -433,7 +442,7 @@ export default { | ||||
|         return | ||||
|       } | ||||
| 
 | ||||
|       var val = option.value | ||||
|       const val = option.value | ||||
|       if (this.selected === val) { | ||||
|         this.showMenu = false | ||||
|         return | ||||
|  | ||||
| @ -7,11 +7,11 @@ | ||||
|       </span> | ||||
|     </button> | ||||
| 
 | ||||
|     <ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label"> | ||||
|     <ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-96 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-sm" role="listbox" aria-labelledby="listbox-label"> | ||||
|       <template v-for="item in selectItems"> | ||||
|         <li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item.value)"> | ||||
|         <li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="option" @click="clickedOption(item.value)"> | ||||
|           <div class="flex items-center"> | ||||
|             <span class="font-normal ml-3 block truncate text-xs">{{ item.text }}</span> | ||||
|             <span class="font-normal ml-3 block truncate">{{ item.text }}</span> | ||||
|           </div> | ||||
|           <span v-if="item.value === selected" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4"> | ||||
|             <span class="material-icons text-xl">{{ descending ? 'expand_more' : 'expand_less' }}</span> | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| <template> | ||||
|   <div ref="wrapper" class="relative" v-click-outside="clickOutside"> | ||||
|     <button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none sm:text-sm cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu"> | ||||
|     <button type="button" class="relative w-full h-full border border-gray-500 hover:border-gray-400 rounded shadow-sm pl-3 pr-3 py-0 text-left focus:outline-none cursor-pointer" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click.prevent="showMenu = !showMenu"> | ||||
|       <span class="flex items-center justify-between"> | ||||
|         <span class="block truncate text-xs" :class="!selectedText ? 'text-gray-300' : ''">{{ selectedText }}</span> | ||||
|         <span class="material-icons text-lg text-yellow-400">{{ descending ? 'expand_more' : 'expand_less' }}</span> | ||||
|       </span> | ||||
|     </button> | ||||
| 
 | ||||
|     <ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-80 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label"> | ||||
|     <ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-80 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-sm" role="listbox" aria-labelledby="listbox-label"> | ||||
|       <template v-for="item in items"> | ||||
|         <li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item.value)"> | ||||
|         <li :key="item.value" class="select-none relative py-2 pr-9 cursor-pointer hover:bg-white/5" :class="item.value === selected ? 'bg-white/5 text-yellow-400' : 'text-gray-200 hover:text-white'" role="option" @click="clickedOption(item.value)"> | ||||
|           <div class="flex items-center"> | ||||
|             <span class="font-normal ml-3 block truncate text-xs">{{ item.text }}</span> | ||||
|             <span class="font-normal ml-3 block truncate">{{ item.text }}</span> | ||||
|           </div> | ||||
|           <span v-if="item.value === selected" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4"> | ||||
|             <span class="material-icons text-xl">{{ descending ? 'expand_more' : 'expand_less' }}</span> | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
|       <div v-show="showMenu" ref="menuWrapper" class="absolute right-0 mt-1 z-10 bg-bg border border-black-200 shadow-lg rounded-md py-1 focus:outline-none sm:text-sm" :style="{ width: menuWidth + 'px' }"> | ||||
|         <template v-for="(item, index) in items"> | ||||
|           <template v-if="item.subitems"> | ||||
|             <div :key="index" class="flex items-center px-2 py-1.5 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-default" :class="{ 'bg-white/5': mouseoverItemIndex == index }" @mouseover="mouseoverItem(index)" @mouseleave="mouseleaveItem(index)" @click.stop> | ||||
|             <div :key="index" class="flex items-center px-2 py-1.5 hover:bg-white/5 text-white text-xs cursor-default" :class="{ 'bg-white/5': mouseoverItemIndex == index }" @mouseover="mouseoverItem(index)" @mouseleave="mouseleaveItem(index)" @click.stop> | ||||
|               <p>{{ item.text }}</p> | ||||
|             </div> | ||||
|             <div | ||||
| @ -25,12 +25,12 @@ | ||||
|               :class="openSubMenuLeft ? 'rounded-l-md' : 'rounded-r-md'" | ||||
|               :style="{ left: submenuLeftPos + 'px', top: index * 28 + 'px', width: submenuWidth + 'px' }" | ||||
|             > | ||||
|               <div v-for="(subitem, subitemindex) in item.subitems" :key="`subitem-${subitemindex}`" class="flex items-center px-2 py-1.5 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-pointer" @click.stop="clickAction(subitem.action, subitem.data)"> | ||||
|               <div v-for="(subitem, subitemindex) in item.subitems" :key="`subitem-${subitemindex}`" class="flex items-center px-2 py-1.5 hover:bg-white/5 text-white text-xs cursor-pointer" @click.stop="clickAction(subitem.action, subitem.data)"> | ||||
|                 <p>{{ subitem.text }}</p> | ||||
|               </div> | ||||
|             </div> | ||||
|           </template> | ||||
|           <div v-else :key="index" class="flex items-center px-2 py-1.5 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-pointer" @click.stop="clickAction(item.action)"> | ||||
|           <div v-else :key="index" class="flex items-center px-2 py-1.5 hover:bg-white/5 text-white text-xs cursor-pointer" @click.stop="clickAction(item.action)"> | ||||
|             <p class="text-left">{{ item.text }}</p> | ||||
|           </div> | ||||
|         </template> | ||||
|  | ||||
| @ -2,16 +2,16 @@ | ||||
|   <div ref="wrapper" class="absolute bg-bg rounded-md py-1 border border-black-200 shadow-lg z-50" v-click-outside="clickOutsideObj" :style="{ width: menuWidth + 'px' }" style="top: 0; left: 0"> | ||||
|     <template v-for="(item, index) in items"> | ||||
|       <template v-if="item.subitems"> | ||||
|         <div :key="index" class="flex items-center px-2 py-1.5 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-default" :class="{ 'bg-white/5': mouseoverItemIndex == index }" @mouseover="mouseoverItem(index)" @mouseleave="mouseleaveItem(index)" @click.stop> | ||||
|         <div :key="index" class="flex items-center px-2 py-1.5 hover:bg-white/5 text-white text-xs cursor-default" :class="{ 'bg-white/5': mouseoverItemIndex == index }" @mouseover="mouseoverItem(index)" @mouseleave="mouseleaveItem(index)" @click.stop> | ||||
|           <p>{{ item.text }}</p> | ||||
|         </div> | ||||
|         <div v-if="mouseoverItemIndex === index" :key="`subitems-${index}`" @mouseover="mouseoverSubItemMenu(index)" @mouseleave="mouseleaveSubItemMenu(index)" class="absolute bg-bg rounded-b-md border border-black-200 py-1 shadow-lg z-50" :class="openSubMenuLeft ? 'rounded-l-md' : 'rounded-r-md'" :style="{ left: submenuLeftPos + 'px', top: index * 28 + 'px', width: submenuWidth + 'px' }"> | ||||
|           <div v-for="(subitem, subitemindex) in item.subitems" :key="`subitem-${subitemindex}`" class="flex items-center px-2 py-1.5 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-pointer" @click.stop="clickAction(subitem.func, subitem.data)"> | ||||
|           <div v-for="(subitem, subitemindex) in item.subitems" :key="`subitem-${subitemindex}`" class="flex items-center px-2 py-1.5 hover:bg-white/5 text-white text-xs cursor-pointer" @click.stop="clickAction(subitem.func, subitem.data)"> | ||||
|             <p>{{ subitem.text }}</p> | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|       <div v-else :key="index" class="flex items-center px-2 py-1.5 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-pointer" @mouseover="mouseoverItem(index)" @mouseleave="mouseleaveItem(index)" @click.stop="clickAction(item.func)"> | ||||
|       <div v-else :key="index" class="flex items-center px-2 py-1.5 hover:bg-white/5 text-white text-xs cursor-pointer" @mouseover="mouseoverItem(index)" @mouseleave="mouseleaveItem(index)" @click.stop="clickAction(item.func)"> | ||||
|         <p>{{ item.text }}</p> | ||||
|       </div> | ||||
|     </template> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user