diff --git a/tienda/templates/tienda/base.html b/tienda/templates/tienda/base.html
index f58a554..3ab8d2d 100644
--- a/tienda/templates/tienda/base.html
+++ b/tienda/templates/tienda/base.html
@@ -50,8 +50,11 @@
transition: background-color 0.2s;
}
- .search-suggestion-item:hover {
+ .search-suggestion-item:hover,
+ .search-suggestion-item.active {
background-color: #f8f9fa;
+ outline: 2px solid #0d6efd;
+ outline-offset: -2px;
}
.search-suggestion-item:last-child {
@@ -107,10 +110,10 @@
@@ -201,6 +204,35 @@
const searchSuggestions = document.getElementById('searchSuggestions');
const searchForm = document.getElementById('searchForm');
let searchTimeout;
+ let currentFocusIndex = -1;
+
+ // Helpers para gestionar el estado ARIA del combobox
+ function openSuggestions() {
+ searchSuggestions.classList.add('show');
+ searchInput.setAttribute('aria-expanded', 'true');
+ }
+
+ function closeSuggestions() {
+ searchSuggestions.classList.remove('show');
+ searchInput.setAttribute('aria-expanded', 'false');
+ searchInput.setAttribute('aria-activedescendant', '');
+ currentFocusIndex = -1;
+ }
+
+ function updateFocus(options) {
+ options.forEach((option, index) => {
+ const active = index === currentFocusIndex;
+ option.classList.toggle('active', active);
+ option.setAttribute('aria-selected', active ? 'true' : 'false');
+ });
+ if (currentFocusIndex >= 0) {
+ const activeOption = options[currentFocusIndex];
+ searchInput.setAttribute('aria-activedescendant', activeOption.id);
+ activeOption.scrollIntoView({ block: 'nearest' });
+ } else {
+ searchInput.setAttribute('aria-activedescendant', '');
+ }
+ }
// Escuchar cambios en el input
searchInput.addEventListener('input', function() {
@@ -208,7 +240,7 @@
const query = this.value.trim();
if (query.length < 2) {
- searchSuggestions.classList.remove('show');
+ closeSuggestions();
return;
}
@@ -218,6 +250,31 @@
}, 300);
});
+ // Navegación por teclado (ArrowDown/ArrowUp/Enter/Escape)
+ searchInput.addEventListener('keydown', function(event) {
+ const options = searchSuggestions.querySelectorAll('[role="option"]');
+ if (!options.length || !searchSuggestions.classList.contains('show')) {
+ return;
+ }
+
+ if (event.key === 'ArrowDown') {
+ event.preventDefault();
+ currentFocusIndex = Math.min(currentFocusIndex + 1, options.length - 1);
+ updateFocus(options);
+ } else if (event.key === 'ArrowUp') {
+ event.preventDefault();
+ currentFocusIndex = Math.max(currentFocusIndex - 1, -1);
+ updateFocus(options);
+ } else if (event.key === 'Enter' && currentFocusIndex >= 0) {
+ event.preventDefault();
+ const selected = options[currentFocusIndex];
+ window.location.href = selected.dataset.href;
+ } else if (event.key === 'Escape') {
+ closeSuggestions();
+ searchInput.focus();
+ }
+ });
+
// Función para obtener sugerencias del servidor
function fetchSuggestions(query) {
fetch(`{% url 'search_suggestions' %}?q=${encodeURIComponent(query)}`)
@@ -227,33 +284,37 @@
})
.catch(error => {
console.error('Error fetching suggestions:', error);
- searchSuggestions.classList.remove('show');
+ closeSuggestions();
});
}
// Función para mostrar las sugerencias
function displaySuggestions(suggestions, query) {
+ currentFocusIndex = -1;
if (suggestions.length === 0) {
searchSuggestions.innerHTML = '
No se encontraron productos
';
- searchSuggestions.classList.add('show');
+ openSuggestions();
return;
}
let html = '';
- suggestions.forEach(suggestion => {
+ suggestions.forEach((suggestion, index) => {
// Resaltar la coincidencia en el nombre
const highlightedName = highlightMatch(suggestion.name, query);
const priceWithVAT = (suggestion.price * 1.21).toFixed(2);
html += `
-
+
${highlightedName}
€${priceWithVAT}
-
+
`;
});
searchSuggestions.innerHTML = html;
- searchSuggestions.classList.add('show');
+ openSuggestions();
}
// Función para resaltar el texto que coincide
@@ -265,19 +326,19 @@
// Cerrar sugerencias cuando se hace clic fuera
document.addEventListener('click', function(event) {
if (!searchForm.contains(event.target)) {
- searchSuggestions.classList.remove('show');
+ closeSuggestions();
}
});
// Cerrar sugerencias al enviar el formulario
searchForm.addEventListener('submit', function() {
- searchSuggestions.classList.remove('show');
+ closeSuggestions();
});
// Mostrar sugerencias al hacer clic en el input (si hay texto)
searchInput.addEventListener('focus', function() {
if (this.value.trim().length >= 2 && searchSuggestions.innerHTML) {
- searchSuggestions.classList.add('show');
+ openSuggestions();
}
});