FEATURE Fixing & Refining search bar behaviour

This commit is contained in:
Nonimart 2025-09-16 14:02:17 +02:00
parent 8211ba7a6b
commit e707843129
2 changed files with 79 additions and 12 deletions

View File

@ -10,7 +10,7 @@
bottom-0 bottom-0
/* overflow-x-hidden */ /* overflow-x-hidden */
transform translate-y-full; transform translate-y-full;
@apply block; @apply block overflow-x-hidden;
animation: translate-in 700ms forwards cubic-bezier(0, 0.51, 0.23, 0.99), animation: translate-in 700ms forwards cubic-bezier(0, 0.51, 0.23, 0.99),
fade-in 600ms forwards ease-out; fade-in 600ms forwards ease-out;
@ -33,6 +33,23 @@
} }
} }
@keyframes fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes translate-out {
from {
transform: translateY(100%);
}
to {
transform: translateY(calc(100% - 100px));
}
}
&[closed] { &[closed] {
@apply hidden; @apply hidden;
} }
@ -101,5 +118,5 @@
} }
body:has(.search-module[opened]) main { body:has(.search-module[opened]) main {
filter: blur(2px) brightness(0.9); filter: blur(2px) brightness(0.8);
} }

View File

@ -5,34 +5,47 @@ interface SearchBarOptions {
export default class SearchBar { export default class SearchBar {
private searchBar: HTMLDivElement | null; private searchBar: HTMLDivElement | null;
private searchButton: HTMLButtonElement | null; private searchButtons: NodeListOf<HTMLButtonElement> | null;
private searchInput: HTMLInputElement | null;
private isOpen: boolean = false; private isOpen: boolean = false;
private lastScrollY: number = 0;
private activeButton: HTMLButtonElement | null = null;
constructor(options: SearchBarOptions = {}) { constructor(options: SearchBarOptions = {}) {
this.searchBar = document.querySelector('#search-module') as HTMLDivElement; this.searchBar = document.querySelector('#search-module') as HTMLDivElement;
this.searchButton = document.querySelector( this.searchButtons = document.querySelectorAll(
'.tools-container .search-button' '.tools-container .search-button'
) as HTMLButtonElement; ) as NodeListOf<HTMLButtonElement>; // Il y a 2 boutons de recherche (1 mobile; 1 desktop)
this.searchInput = document.querySelector(
'.search-module__search-form__input'
) as HTMLInputElement;
this.init(); this.init();
} }
private init(): void { private init(): void {
if (!this.searchBar || !this.searchButton) return; if (!this.searchBar || !this.searchButtons) return;
// Initialiser l'état // Initialiser l'état
this.isOpen = this.searchBar.hasAttribute('opened'); this.isOpen = this.searchBar.hasAttribute('opened');
this.lastScrollY = window.scrollY;
this.updateAriaHidden(); this.updateAriaHidden();
// Ajouter les event listeners // Ajouter les event listeners
this.searchButton.addEventListener('click', this.toggle.bind(this)); this.searchButtons.forEach((button) =>
button.addEventListener('click', (event) => this.toggle(event))
);
this.searchBar.addEventListener('transitionend', this.handleTransitionEnd.bind(this)); this.searchBar.addEventListener('transitionend', this.handleTransitionEnd.bind(this));
document.addEventListener('keydown', this.handleKeyDown.bind(this)); document.addEventListener('keydown', this.handleKeyDown.bind(this));
window.addEventListener('scroll', this.handleScroll.bind(this));
} }
private toggle(): void { private toggle(event: Event): void {
if (!this.searchBar) return; if (!this.searchBar) return;
// Stocker le bouton qui a été cliqué
this.activeButton = event.currentTarget as HTMLButtonElement;
this.isOpen = !this.isOpen; this.isOpen = !this.isOpen;
if (this.isOpen) { if (this.isOpen) {
@ -48,14 +61,42 @@ export default class SearchBar {
this.searchBar.removeAttribute('closed'); this.searchBar.removeAttribute('closed');
this.searchBar.setAttribute('opened', ''); this.searchBar.setAttribute('opened', '');
this.isOpen = true; this.isOpen = true;
// Focus automatique sur le champ de recherche après un petit délai pour laisser l'animation commencer
setTimeout(() => {
if (this.searchInput && this.isOpen) {
this.searchInput.focus();
}
}, 100);
} }
public close(): void { public close(): void {
if (!this.searchBar) return; if (!this.searchBar) return;
this.searchBar.setAttribute('closed', ''); // Défocus l'input de recherche
if (this.searchInput) {
this.searchInput.blur();
}
this.searchBar.setAttribute('closing', '');
this.searchBar.removeAttribute('opened'); this.searchBar.removeAttribute('opened');
this.isOpen = false; this.isOpen = false;
// Écouter la fin de l'animation de fermeture (on attend la plus longue: translate-out 800ms)
const handleAnimationEnd = (event: AnimationEvent) => {
if (event.animationName === 'translate-out') {
this.searchBar?.removeAttribute('closing');
this.searchBar?.setAttribute('closed', '');
this.searchBar?.removeEventListener('animationend', handleAnimationEnd);
// Refocus sur le bouton qui avait ouvert la barre après la fermeture
if (this.activeButton) {
this.activeButton.focus();
}
}
};
this.searchBar.addEventListener('animationend', handleAnimationEnd);
} }
private handleTransitionEnd(): void { private handleTransitionEnd(): void {
@ -68,10 +109,16 @@ export default class SearchBar {
} }
} }
private handleScroll(): void {
if (!this.isOpen) return;
this.close();
}
private updateAriaHidden(): void { private updateAriaHidden(): void {
if (!this.searchBar) return; if (!this.searchBar) return;
if (this.searchBar.hasAttribute('closed')) { if (this.searchBar.hasAttribute('closed') || this.searchBar.hasAttribute('closing')) {
this.searchBar.setAttribute('aria-hidden', 'true'); this.searchBar.setAttribute('aria-hidden', 'true');
} else { } else {
this.searchBar.setAttribute('aria-hidden', 'false'); this.searchBar.setAttribute('aria-hidden', 'false');
@ -83,10 +130,13 @@ export default class SearchBar {
} }
public destroy(): void { public destroy(): void {
if (!this.searchBar || !this.searchButton) return; if (!this.searchBar || !this.searchButtons) return;
this.searchButton.removeEventListener('click', this.toggle.bind(this)); // Note: Les event listeners anonymes ne peuvent pas être supprimés facilement
// Dans un vrai projet, il faudrait stocker les références des fonctions
this.searchBar.removeEventListener('transitionend', this.handleTransitionEnd.bind(this)); this.searchBar.removeEventListener('transitionend', this.handleTransitionEnd.bind(this));
document.removeEventListener('keydown', this.handleKeyDown.bind(this));
window.removeEventListener('scroll', this.handleScroll.bind(this));
} }
} }