Move search bar into dedicated component
This commit is contained in:
		
							
								
								
									
										94
									
								
								pvc-vue/src/components/general/VMSearchBar.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								pvc-vue/src/components/general/VMSearchBar.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="search-box"> | ||||||
|  |     <i class="fas fa-search search-icon"></i> | ||||||
|  |     <input  | ||||||
|  |       type="text"  | ||||||
|  |       placeholder="Search VMs..."  | ||||||
|  |       :value="modelValue" | ||||||
|  |       @input="handleSearch" | ||||||
|  |       @focus="handleFocus" | ||||||
|  |       @blur="handleBlur" | ||||||
|  |       class="form-control search-input" | ||||||
|  |     > | ||||||
|  |     <button  | ||||||
|  |       v-if="modelValue && showList && showClearButton"  | ||||||
|  |       class="btn-clear"  | ||||||
|  |       @click="clearSearch" | ||||||
|  |     > | ||||||
|  |       <i class="fas fa-times"></i> | ||||||
|  |     </button> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { defineProps, defineEmits } from 'vue'; | ||||||
|  |  | ||||||
|  | const props = defineProps({ | ||||||
|  |   modelValue: { | ||||||
|  |     type: String, | ||||||
|  |     required: true | ||||||
|  |   }, | ||||||
|  |   showList: { | ||||||
|  |     type: Boolean, | ||||||
|  |     required: true | ||||||
|  |   }, | ||||||
|  |   showClearButton: { | ||||||
|  |     type: Boolean, | ||||||
|  |     default: true | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | const emit = defineEmits(['update:modelValue', 'search', 'focus', 'blur', 'clear']); | ||||||
|  |  | ||||||
|  | const handleSearch = (event) => { | ||||||
|  |   emit('update:modelValue', event.target.value); | ||||||
|  |   emit('search', event.target.value); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const handleFocus = (event) => { | ||||||
|  |   emit('focus', event); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const handleBlur = (event) => { | ||||||
|  |   emit('blur', event); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const clearSearch = () => { | ||||||
|  |   emit('update:modelValue', ''); | ||||||
|  |   emit('clear'); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .search-box { | ||||||
|  |   position: relative; | ||||||
|  |   flex: 1; | ||||||
|  |   min-width: 200px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .search-icon { | ||||||
|  |   position: absolute; | ||||||
|  |   left: 10px; | ||||||
|  |   top: 50%; | ||||||
|  |   transform: translateY(-50%); | ||||||
|  |   color: #6c757d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .search-input { | ||||||
|  |   padding-left: 30px; | ||||||
|  |   padding-right: 30px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .btn-clear { | ||||||
|  |   position: absolute; | ||||||
|  |   right: 10px; | ||||||
|  |   top: 50%; | ||||||
|  |   transform: translateY(-50%); | ||||||
|  |   background: none; | ||||||
|  |   border: none; | ||||||
|  |   color: #6c757d; | ||||||
|  |   cursor: pointer; | ||||||
|  |   padding: 0; | ||||||
|  |   font-size: 0.875rem; | ||||||
|  | } | ||||||
|  | </style>  | ||||||
| @@ -11,25 +11,15 @@ | |||||||
|           <i class="fas fa-list"></i> List VMs |           <i class="fas fa-list"></i> List VMs | ||||||
|         </button> |         </button> | ||||||
|          |          | ||||||
|         <div class="search-box"> |         <VMSearchBar | ||||||
|           <i class="fas fa-search search-icon"></i> |           :model-value="showVMList ? searchQuery : selectedVMData?.name || ''" | ||||||
|           <input  |           :show-list="showVMList" | ||||||
|             type="text"  |           :show-clear-button="showVMList" | ||||||
|             placeholder="Search VMs..."  |           @search="handleSearch" | ||||||
|             :value="showVMList ? searchQuery : (selectedVMData?.name || '')" |           @focus="handleSearchFocus" | ||||||
|             @input="handleSearch" |           @blur="handleSearchBlur" | ||||||
|             @focus="handleSearchFocus" |           @clear="clearSearch" | ||||||
|             @blur="handleSearchBlur" |         /> | ||||||
|             class="form-control search-input" |  | ||||||
|           > |  | ||||||
|           <button  |  | ||||||
|             v-if="searchQuery && showVMList"  |  | ||||||
|             class="btn-clear"  |  | ||||||
|             @click="clearSearch" |  | ||||||
|           > |  | ||||||
|             <i class="fas fa-times"></i> |  | ||||||
|           </button> |  | ||||||
|         </div> |  | ||||||
|          |          | ||||||
|         <div class="filter-dropdown"> |         <div class="filter-dropdown"> | ||||||
|           <button  |           <button  | ||||||
| @@ -217,6 +207,7 @@ import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'; | |||||||
| import { useRouter, useRoute } from 'vue-router'; | import { useRouter, useRoute } from 'vue-router'; | ||||||
| import CollapsibleSection from '../../general/CollapsibleSection.vue'; | import CollapsibleSection from '../../general/CollapsibleSection.vue'; | ||||||
| import ValueCard from '../../general/ValueCard.vue'; | import ValueCard from '../../general/ValueCard.vue'; | ||||||
|  | import VMSearchBar from '../../general/VMSearchBar.vue'; | ||||||
|  |  | ||||||
| const router = useRouter(); | const router = useRouter(); | ||||||
| const route = useRoute(); | const route = useRoute(); | ||||||
| @@ -428,8 +419,8 @@ const getStatusClass = (state) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| // Handle search input | // Handle search input | ||||||
| const handleSearch = (event) => { | const handleSearch = (value) => { | ||||||
|   searchQuery.value = event.target.value; |   searchQuery.value = value; | ||||||
|   searchActive.value = true; |   searchActive.value = true; | ||||||
|   filterVMs(); |   filterVMs(); | ||||||
| }; | }; | ||||||
| @@ -637,6 +628,13 @@ const formatStorage = (sizeGB) => { | |||||||
|   if (sizeGB < 1024) return sizeGB + ' GB'; |   if (sizeGB < 1024) return sizeGB + ' GB'; | ||||||
|   return (sizeGB / 1024).toFixed(1) + ' TB'; |   return (sizeGB / 1024).toFixed(1) + ' TB'; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | // Clear search | ||||||
|  | const clearSearch = () => { | ||||||
|  |   searchQuery.value = ''; | ||||||
|  |   searchActive.value = false; | ||||||
|  |   filterVMs(); | ||||||
|  | }; | ||||||
| </script>  | </script>  | ||||||
|  |  | ||||||
| <style scoped> | <style scoped> | ||||||
| @@ -687,39 +685,6 @@ const formatStorage = (sizeGB) => { | |||||||
|   margin-left: 0.5rem; |   margin-left: 0.5rem; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Search box styles */ |  | ||||||
| .search-box { |  | ||||||
|   position: relative; |  | ||||||
|   flex: 1; |  | ||||||
|   min-width: 200px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .search-icon { |  | ||||||
|   position: absolute; |  | ||||||
|   left: 10px; |  | ||||||
|   top: 50%; |  | ||||||
|   transform: translateY(-50%); |  | ||||||
|   color: #6c757d; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .search-input { |  | ||||||
|   padding-left: 30px; |  | ||||||
|   padding-right: 30px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .btn-clear { |  | ||||||
|   position: absolute; |  | ||||||
|   right: 10px; |  | ||||||
|   top: 50%; |  | ||||||
|   transform: translateY(-50%); |  | ||||||
|   background: none; |  | ||||||
|   border: none; |  | ||||||
|   color: #6c757d; |  | ||||||
|   cursor: pointer; |  | ||||||
|   padding: 0; |  | ||||||
|   font-size: 0.875rem; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* VM List styles */ | /* VM List styles */ | ||||||
| .vm-list-container { | .vm-list-container { | ||||||
|   background-color: white; |   background-color: white; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user