interface SearchBarOptions { searchBarSelector?: string; buttonSelector?: string; } export default class SearchBar { private searchBar: HTMLDivElement | null; private searchButtons: NodeListOf | null; private searchInput: HTMLInputElement | null; private isOpen: boolean = false; private lastScrollY: number = 0; private activeButton: HTMLButtonElement | null = null; constructor(options: SearchBarOptions = {}) { this.searchBar = document.querySelector('#search-module') as HTMLDivElement; this.searchButtons = document.querySelectorAll( '.tools-container .search-button' ) as NodeListOf; // Il y a 2 boutons de recherche (1 mobile; 1 desktop) this.searchInput = document.querySelector( '.search-module__search-form__input' ) as HTMLInputElement; this.init(); } private init(): void { if (!this.searchBar || !this.searchButtons) return; // Initialiser l'état this.isOpen = this.searchBar.hasAttribute('opened'); this.lastScrollY = window.scrollY; this.updateAriaHidden(); // Ajouter les event listeners this.searchButtons.forEach((button) => button.addEventListener('click', (event) => this.toggle(event)) ); this.searchBar.addEventListener('transitionend', this.handleTransitionEnd.bind(this)); document.addEventListener('keydown', this.handleKeyDown.bind(this)); window.addEventListener('scroll', this.handleScroll.bind(this)); } private toggle(event: Event): void { if (!this.searchBar) return; // Stocker le bouton qui a été cliqué this.activeButton = event.currentTarget as HTMLButtonElement; this.isOpen = !this.isOpen; if (this.isOpen) { this.open(); } else { this.close(); } } public open(): void { if (!this.searchBar) return; this.searchBar.removeAttribute('closed'); this.searchBar.setAttribute('opened', ''); 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 { if (!this.searchBar) return; // Défocus l'input de recherche if (this.searchInput) { this.searchInput.blur(); } this.searchBar.setAttribute('closing', ''); this.searchBar.removeAttribute('opened'); 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 { this.updateAriaHidden(); } private handleKeyDown(event: KeyboardEvent): void { if (event.key === 'Escape' && this.isOpen) { this.close(); } } private handleScroll(): void { if (!this.isOpen) return; this.close(); } private updateAriaHidden(): void { if (!this.searchBar) return; if (this.searchBar.hasAttribute('closed') || this.searchBar.hasAttribute('closing')) { this.searchBar.setAttribute('aria-hidden', 'true'); } else { this.searchBar.setAttribute('aria-hidden', 'false'); } } public isSearchBarOpen(): boolean { return this.isOpen; } public destroy(): void { if (!this.searchBar || !this.searchButtons) return; // 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)); document.removeEventListener('keydown', this.handleKeyDown.bind(this)); window.removeEventListener('scroll', this.handleScroll.bind(this)); } } // Fonction de compatibilité pour l'import existant export function searchBarInit(): SearchBar { return new SearchBar(); }