Compare commits
	
		
			3 Commits
		
	
	
		
			47b4e6e182
			...
			56ca497041
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					56ca497041 | ||
| 
						 | 
					4b708f1f40 | ||
| 
						 | 
					b891929956 | 
@@ -13,8 +13,25 @@
 | 
			
		||||
        <i class="fas fa-list"></i> List VMs
 | 
			
		||||
      </button>
 | 
			
		||||
      
 | 
			
		||||
      <div class="search-box">
 | 
			
		||||
        <i class="fas fa-search search-icon"></i>
 | 
			
		||||
      <!-- Search box - visible when drawer is open -->
 | 
			
		||||
      <div v-if="showList" class="search-box">
 | 
			
		||||
        <button 
 | 
			
		||||
          v-if="shouldShowClearButton"
 | 
			
		||||
          class="btn-clear btn-clear-left" 
 | 
			
		||||
          @click.stop="handleClearButton"
 | 
			
		||||
          title="Clear search"
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fas fa-times"></i>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button 
 | 
			
		||||
          v-else
 | 
			
		||||
          class="btn-clear btn-clear-left disabled" 
 | 
			
		||||
          disabled
 | 
			
		||||
          title="No search to clear"
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fas fa-times"></i>
 | 
			
		||||
        </button>
 | 
			
		||||
        
 | 
			
		||||
        <input 
 | 
			
		||||
          type="text" 
 | 
			
		||||
          placeholder="Search VMs..." 
 | 
			
		||||
@@ -26,14 +43,32 @@
 | 
			
		||||
          :class="{ 'search-active': showList && isFilterActive }"
 | 
			
		||||
          class="form-control search-input"
 | 
			
		||||
        >
 | 
			
		||||
        
 | 
			
		||||
        <i class="fas fa-search search-icon-right-open"></i>
 | 
			
		||||
      </div>
 | 
			
		||||
      
 | 
			
		||||
      <!-- VM Display - visible when drawer is closed -->
 | 
			
		||||
      <div v-else class="vm-display" @click.stop="openSearchDrawer">
 | 
			
		||||
        <button 
 | 
			
		||||
          v-if="shouldShowClearButton"
 | 
			
		||||
          class="btn-clear" 
 | 
			
		||||
          @click.stop="handleClearButton"
 | 
			
		||||
          :title="showList ? 'Clear search' : 'Clear selected VM'"
 | 
			
		||||
          v-if="selectedVMName"
 | 
			
		||||
          class="btn-clear vm-clear-btn" 
 | 
			
		||||
          @click.stop="clearSelectedVM"
 | 
			
		||||
          title="Clear selected VM"
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fas fa-times"></i>
 | 
			
		||||
        </button>
 | 
			
		||||
        <button 
 | 
			
		||||
          v-else
 | 
			
		||||
          class="btn-clear vm-clear-btn disabled" 
 | 
			
		||||
          disabled
 | 
			
		||||
          title="No VM selected"
 | 
			
		||||
        >
 | 
			
		||||
          <i class="fas fa-times"></i>
 | 
			
		||||
        </button>
 | 
			
		||||
        
 | 
			
		||||
        <i class="fas fa-desktop vm-icon"></i>
 | 
			
		||||
        <span class="vm-name">{{ selectedVMName || 'Select a VM...' }}</span>
 | 
			
		||||
        <i class="fas fa-search search-icon-right-closed"></i>
 | 
			
		||||
      </div>
 | 
			
		||||
      
 | 
			
		||||
      <div class="filter-dropdown" ref="filterDropdown">
 | 
			
		||||
@@ -197,6 +232,11 @@ const isVMSelected = (vmName) => {
 | 
			
		||||
         localStorage.getItem('selectedVMId') === vmName;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Computed property for the selected VM name
 | 
			
		||||
const selectedVMName = computed(() => {
 | 
			
		||||
  return props.selectedVM || props.vmFromUrl || localStorage.getItem('selectedVMId') || '';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Initialize the component
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  // Set up click outside handler
 | 
			
		||||
@@ -304,35 +344,75 @@ const handleBlur = (event) => {
 | 
			
		||||
 | 
			
		||||
// Handle click on search input
 | 
			
		||||
const handleSearchClick = () => {
 | 
			
		||||
  if (props.showList) {
 | 
			
		||||
    // When clicking the search input while list is open, activate filtering
 | 
			
		||||
  // When clicking the search input, activate filtering mode
 | 
			
		||||
  if (props.showList && !isFilterActive.value) {
 | 
			
		||||
    isFilterActive.value = true;
 | 
			
		||||
    
 | 
			
		||||
    // Restore search text if available
 | 
			
		||||
    if (searchText.value && searchText.value !== inputValue.value) {
 | 
			
		||||
      inputValue.value = searchText.value;
 | 
			
		||||
      emit('update:modelValue', searchText.value);
 | 
			
		||||
    // Restore saved search text if available
 | 
			
		||||
    const savedSearchText = localStorage.getItem('vmSearchText');
 | 
			
		||||
    if (savedSearchText && inputValue.value !== savedSearchText) {
 | 
			
		||||
      inputValue.value = savedSearchText;
 | 
			
		||||
      emit('update:modelValue', savedSearchText);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Toggle the VM list
 | 
			
		||||
const toggleList = () => {
 | 
			
		||||
  // If the list is already open, toggle filtering instead of closing
 | 
			
		||||
  if (props.showList) {
 | 
			
		||||
    // If we're in list mode (not filtering) and the button is clicked, close the drawer
 | 
			
		||||
    if (!isFilterActive.value) {
 | 
			
		||||
      // Save the current search text before closing
 | 
			
		||||
      if (props.modelValue) {
 | 
			
		||||
        searchText.value = props.modelValue;
 | 
			
		||||
        // Save to localStorage
 | 
			
		||||
        localStorage.setItem('vmSearchText', props.modelValue);
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Close the drawer
 | 
			
		||||
      emit('toggle-list');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  // When toggling the list, ensure search text is preserved
 | 
			
		||||
  const savedSearchText = localStorage.getItem('vmSearchText');
 | 
			
		||||
    // Toggle filtering mode
 | 
			
		||||
    isFilterActive.value = !isFilterActive.value;
 | 
			
		||||
    
 | 
			
		||||
  if (!props.showList) {
 | 
			
		||||
    // When opening the list, restore search text if available
 | 
			
		||||
    if (savedSearchText && !inputValue.value) {
 | 
			
		||||
    // If we're turning filtering on, make sure the search text is applied
 | 
			
		||||
    if (isFilterActive.value && searchText.value) {
 | 
			
		||||
      inputValue.value = searchText.value;
 | 
			
		||||
      emit('update:modelValue', searchText.value);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // If we're turning filtering off (switching to list mode), scroll to selected VM
 | 
			
		||||
    if (!isFilterActive.value) {
 | 
			
		||||
      nextTick(() => {
 | 
			
		||||
        inputValue.value = savedSearchText;
 | 
			
		||||
        searchText.value = savedSearchText;
 | 
			
		||||
        emit('update:modelValue', savedSearchText);
 | 
			
		||||
        scrollToSelectedVM();
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // No need to emit toggle-list since we're not closing the list
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // If the list is closed, open it without filtering
 | 
			
		||||
  if (!props.showList) {
 | 
			
		||||
    // If we're opening the list, deactivate filtering
 | 
			
		||||
    isFilterActive.value = false;
 | 
			
		||||
    
 | 
			
		||||
    // Restore search text in the input, but don't apply filtering
 | 
			
		||||
    if (searchText.value) {
 | 
			
		||||
      inputValue.value = searchText.value;
 | 
			
		||||
      emit('update:modelValue', searchText.value);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Schedule scrolling to selected VM after the list opens
 | 
			
		||||
    nextTick(() => {
 | 
			
		||||
      scrollToSelectedVM();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  emit('toggle-list');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Handle clear button click
 | 
			
		||||
@@ -372,23 +452,9 @@ const clearSearch = () => {
 | 
			
		||||
 | 
			
		||||
// Clear selected VM
 | 
			
		||||
const clearSelectedVM = () => {
 | 
			
		||||
  // Clear the input value
 | 
			
		||||
  inputValue.value = '';
 | 
			
		||||
  
 | 
			
		||||
  // Clear from localStorage
 | 
			
		||||
  localStorage.removeItem('selectedVMId');
 | 
			
		||||
  
 | 
			
		||||
  // Don't clear vmSearchText - that's for search history
 | 
			
		||||
  // Only clear if the input value matches the selected VM
 | 
			
		||||
  const savedVMId = localStorage.getItem('selectedVMId');
 | 
			
		||||
  const savedSearchText = localStorage.getItem('vmSearchText');
 | 
			
		||||
  if (savedSearchText === savedVMId) {
 | 
			
		||||
    localStorage.removeItem('vmSearchText');
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Show the VM list
 | 
			
		||||
  emit('toggle-list');
 | 
			
		||||
  
 | 
			
		||||
  // Emit event to parent component
 | 
			
		||||
  emit('clear-vm');
 | 
			
		||||
};
 | 
			
		||||
@@ -663,6 +729,42 @@ const loadFiltersFromLocalStorage = () => {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Method to open the search drawer
 | 
			
		||||
const openSearchDrawer = (event) => {
 | 
			
		||||
  // Prevent event propagation
 | 
			
		||||
  event.stopPropagation();
 | 
			
		||||
  
 | 
			
		||||
  console.log('Opening search drawer');
 | 
			
		||||
  
 | 
			
		||||
  // Only open if it's not already open
 | 
			
		||||
  if (!props.showList) {
 | 
			
		||||
    // Set filter active to true to indicate we're in search mode
 | 
			
		||||
    isFilterActive.value = true;
 | 
			
		||||
    
 | 
			
		||||
    // Emit toggle-list to open the drawer
 | 
			
		||||
    emit('toggle-list');
 | 
			
		||||
    
 | 
			
		||||
    // Focus the search input after the drawer opens
 | 
			
		||||
    nextTick(() => {
 | 
			
		||||
      const searchInput = document.querySelector('.search-input');
 | 
			
		||||
      if (searchInput) {
 | 
			
		||||
        searchInput.focus();
 | 
			
		||||
        
 | 
			
		||||
        // Restore saved search text if available
 | 
			
		||||
        const savedSearchText = localStorage.getItem('vmSearchText');
 | 
			
		||||
        if (savedSearchText) {
 | 
			
		||||
          inputValue.value = savedSearchText;
 | 
			
		||||
          emit('update:modelValue', savedSearchText);
 | 
			
		||||
        } else {
 | 
			
		||||
          // If no saved search, clear the input
 | 
			
		||||
          inputValue.value = '';
 | 
			
		||||
          emit('update:modelValue', '');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
@@ -687,24 +789,18 @@ const loadFiltersFromLocalStorage = () => {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  min-width: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-icon {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 10px;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
  color: #6c757d;
 | 
			
		||||
  height: 38px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-input {
 | 
			
		||||
  padding-left: 30px;
 | 
			
		||||
  height: 38px;
 | 
			
		||||
  padding-left: 35px;
 | 
			
		||||
  padding-right: 30px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-clear {
 | 
			
		||||
.btn-clear-left {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  right: 10px;
 | 
			
		||||
  left: 10px;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
  background: none;
 | 
			
		||||
@@ -714,11 +810,11 @@ const loadFiltersFromLocalStorage = () => {
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  font-size: 0.875rem;
 | 
			
		||||
  transition: color 0.2s;
 | 
			
		||||
  z-index: 5; /* Ensure it's above other elements */
 | 
			
		||||
  z-index: 5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-clear:hover {
 | 
			
		||||
  color: #dc3545; /* Red color on hover */
 | 
			
		||||
.btn-clear-left:hover {
 | 
			
		||||
  color: #dc3545;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list-toggle-btn {
 | 
			
		||||
@@ -836,7 +932,11 @@ const loadFiltersFromLocalStorage = () => {
 | 
			
		||||
 | 
			
		||||
.vm-name {
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  padding-left: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vm-state {
 | 
			
		||||
@@ -976,4 +1076,94 @@ const loadFiltersFromLocalStorage = () => {
 | 
			
		||||
  border-color: #0d6efd;
 | 
			
		||||
  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add these styles for the VM display */
 | 
			
		||||
.vm-display {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  min-width: 200px;
 | 
			
		||||
  height: 38px;
 | 
			
		||||
  padding: 0.375rem 0.75rem;
 | 
			
		||||
  padding-left: 35px;
 | 
			
		||||
  padding-right: 30px;
 | 
			
		||||
  background-color: #f8f9fa;
 | 
			
		||||
  border: 1px solid #ced4da;
 | 
			
		||||
  border-radius: 0.25rem;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  transition: background-color 0.2s;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
  z-index: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a hover effect to make it clear it's clickable */
 | 
			
		||||
.vm-display:hover {
 | 
			
		||||
  background-color: #e9ecef;
 | 
			
		||||
  border-color: #adb5bd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add an active state for when it's clicked */
 | 
			
		||||
.vm-display:active {
 | 
			
		||||
  background-color: #dee2e6;
 | 
			
		||||
  border-color: #adb5bd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vm-clear-btn {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 9px;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
  background: none;
 | 
			
		||||
  border: none;
 | 
			
		||||
  color: #6c757d;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  font-size: 0.875rem;
 | 
			
		||||
  transition: color 0.2s;
 | 
			
		||||
  z-index: 5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vm-clear-btn:hover {
 | 
			
		||||
  color: #dc3545;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vm-icon {
 | 
			
		||||
  color: #6c757d;
 | 
			
		||||
  margin-right: 0.5rem;
 | 
			
		||||
  margin-left: -0.25rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vm-name {
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
  flex: 1;
 | 
			
		||||
  padding-left: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-icon-right-open {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  right: 10px;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
  color: #6c757d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-icon-right-closed {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  right: 9px;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  transform: translateY(-50%);
 | 
			
		||||
  color: #6c757d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add disabled style for clear buttons */
 | 
			
		||||
.btn-clear-left.disabled,
 | 
			
		||||
.vm-clear-btn.disabled {
 | 
			
		||||
  color: #ced4da;
 | 
			
		||||
  cursor: default;
 | 
			
		||||
  pointer-events: none;
 | 
			
		||||
}
 | 
			
		||||
</style> 
 | 
			
		||||
		Reference in New Issue
	
	Block a user