SPB Git

spb/fabri-ka Public

Agrégateur de produits québécois — www.fabri-ka.com

HTML 57% Python 19.9% TypeScript 15.3% CSS 7.7%
3.5 KB · 112 lines tsx
Raw Blame History
1import { FormEvent, useEffect, useRef, useState } from 'react'2import { NavLink, useLocation, useNavigate } from 'react-router-dom'3import { IconClose, IconGrid, IconHome, IconSearch, IconStore } from './Icons'45/**6 * Mobile bottom tab bar (<768px) — Accueil, Produits, Boutiques, Recherche.7 * "Recherche" opens a full-screen search overlay with an autofocused input.8 */9export default function BottomNav() {10  const [searchOpen, setSearchOpen] = useState(false)11  const [query, setQuery] = useState('')12  const inputRef = useRef<HTMLInputElement>(null)13  const navigate = useNavigate()14  const location = useLocation()1516  // Close the overlay on navigation.17  useEffect(() => {18    setSearchOpen(false)19  }, [location.pathname, location.search])2021  // Autofocus + scroll lock while the overlay is open.22  useEffect(() => {23    if (!searchOpen) return24    document.body.style.overflow = 'hidden'25    const t = window.setTimeout(() => inputRef.current?.focus(), 60)26    const onKey = (e: KeyboardEvent) => {27      if (e.key === 'Escape') setSearchOpen(false)28    }29    window.addEventListener('keydown', onKey)30    return () => {31      document.body.style.overflow = ''32      window.clearTimeout(t)33      window.removeEventListener('keydown', onKey)34    }35  }, [searchOpen])3637  function onSubmit(e: FormEvent) {38    e.preventDefault()39    const q = query.trim()40    setSearchOpen(false)41    setQuery('')42    navigate(q ? `/produits?q=${encodeURIComponent(q)}` : '/produits')43  }4445  return (46    <>47      {searchOpen && (48        <div49          className="search-overlay"50          role="dialog"51          aria-modal="true"52          aria-label="Recherche"53        >54          <div className="search-overlay-bar">55            <form className="search-overlay-form" onSubmit={onSubmit} role="search">56              <span className="search-overlay-icon" aria-hidden="true">57                <IconSearch size={18} />58              </span>59              <input60                ref={inputRef}61                type="search"62                value={query}63                onChange={(e) => setQuery(e.target.value)}64                placeholder="Rechercher un produit québécois…"65                aria-label="Rechercher"66                enterKeyHint="search"67              />68            </form>69            <button70              type="button"71              className="search-overlay-close"72              onClick={() => setSearchOpen(false)}73              aria-label="Fermer la recherche"74            >75              <IconClose size={22} />76            </button>77          </div>78          <p className="search-overlay-hint">79            Produits fabriqués, conçus et vendus au Québec.80          </p>81        </div>82      )}8384      <nav className="bottom-nav" aria-label="Navigation mobile">85        <NavLink to="/" end className="bottom-nav-item">86          <IconHome size={22} />87          <span>Accueil</span>88        </NavLink>89        <NavLink to="/produits" className="bottom-nav-item">90          <IconGrid size={22} />91          <span>Produits</span>92        </NavLink>93        <NavLink to="/boutiques" className="bottom-nav-item">94          <IconStore size={22} />95          <span>Boutiques</span>96        </NavLink>97        <button98          type="button"99          className={100            searchOpen ? 'bottom-nav-item active' : 'bottom-nav-item'101          }102          onClick={() => setSearchOpen(true)}103          aria-label="Ouvrir la recherche"104        >105          <IconSearch size={22} />106          <span>Recherche</span>107        </button>108      </nav>109    </>110  )111}112