Chapitre 2 : Navigation
Navbar
La barre de navigation supérieure. Affiche le titre et des actions.
Syntaxe de base
import { Navbar, UpperNavbarItem } from '@cap-rel/smartcommon';
import { FiMenu, FiUser } from 'react-icons/fi';
function MyPage() {
return (
<>
<Navbar title="Mon Application">
<UpperNavbarItem icon={FiMenu} onClick={openMenu} />
<UpperNavbarItem icon={FiUser} onClick={goToProfile} />
</Navbar>
{/* Contenu */}
</>
);
}
Props Navbar
| Prop | Type | Description |
|---|---|---|
| title | string | Titre principal |
| subtitle | string | Sous-titre optionnel |
| showBack | boolean | Afficher le bouton retour |
| onBack | function | Callback du bouton retour |
Props UpperNavbarItem
| Prop | Type | Description |
|---|---|---|
| icon | Component | Icône react-icons |
| onClick | function | Action au clic |
| badge | number | Compteur de notification |
Sidebar
Menu latéral avec navigation.
Syntaxe
import { useState } from 'react';
import { Sidebar, Button } from '@cap-rel/smartcommon';
import { useNavigation, useGlobalStates, useApi } from '@cap-rel/smartcommon';
import { FiHome, FiList, FiSettings, FiLogOut } from 'react-icons/fi';
function MainLayout({ children }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const nav = useNavigation();
const api = useApi();
const gst = useGlobalStates();
const menuItems = [
{ icon: FiHome, label: 'Accueil', path: '/' },
{ icon: FiList, label: 'Produits', path: '/products' },
{ icon: FiSettings, label: 'Paramètres', path: '/settings' }
];
const handleLogout = async () => {
await api.logout();
nav.navigate('/login');
};
return (
<>
<Sidebar
isOpen={isSidebarOpen}
onClose={() => setIsSidebarOpen(false)}
>
{/* En-tête utilisateur */}
<div className="p-4 border-b">
<p className="font-bold">{gst.get('session')?.user?.name}</p>
<p className="text-sm text-gray-500">{gst.get('session')?.user?.email}</p>
</div>
{/* Menu */}
<nav className="p-2">
{menuItems.map(item => (
<button
key={item.path}
onClick={() => {
nav.navigate(item.path);
setIsSidebarOpen(false);
}}
className="flex items-center gap-3 w-full p-3 rounded hover:bg-gray-100"
>
<item.icon />
{item.label}
</button>
))}
</nav>
{/* Déconnexion */}
<div className="p-4 border-t mt-auto">
<Button onClick={handleLogout} variant="outline" className="w-full">
<FiLogOut className="mr-2" />
Déconnexion
</Button>
</div>
</Sidebar>
{children}
</>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| isOpen | boolean | Contrôle l'affichage |
| onClose | function | Callback à la fermeture |
| position | string | 'left' ou 'right' |
Tabbar
Barre d'onglets en bas de l'écran (navigation mobile).
Syntaxe
import { Tabbar, TabbarItem } from '@cap-rel/smartcommon';
import { useNavigation } from '@cap-rel/smartcommon';
import { useLocation } from 'react-router-dom';
import { FiHome, FiSearch, FiShoppingCart, FiUser } from 'react-icons/fi';
function AppTabbar() {
const nav = useNavigation();
const location = useLocation();
const tabs = [
{ icon: FiHome, label: 'Accueil', path: '/' },
{ icon: FiSearch, label: 'Recherche', path: '/search' },
{ icon: FiShoppingCart, label: 'Panier', path: '/cart', badge: 3 },
{ icon: FiUser, label: 'Profil', path: '/profile' }
];
return (
<Tabbar>
{tabs.map(tab => (
<TabbarItem
key={tab.path}
icon={tab.icon}
label={tab.label}
active={location.pathname === tab.path}
badge={tab.badge}
onClick={() => nav.navigate(tab.path)}
/>
))}
</Tabbar>
);
}
Props TabbarItem
| Prop | Type | Description |
|---|---|---|
| icon | Component | Icône react-icons |
| label | string | Texte sous l'icône |
| active | boolean | État sélectionné |
| badge | number | Compteur notification |
| onClick | function | Action au clic |
useNavigation
Hook pour la navigation programmatique.
Syntaxe
import { useNavigation } from '@cap-rel/smartcommon';
function MyComponent() {
const nav = useNavigation();
return (
<div>
{/* Navigation vers une route */}
<button onClick={() => nav.navigate('/products')}>
Voir les produits
</button>
{/* Navigation avec paramètre */}
<button onClick={() => nav.navigate(`/products/${productId}`)}>
Détails
</button>
{/* Retour arrière */}
<button onClick={() => nav.navigate(-1)}>
Retour
</button>
{/* Remplacement (pas d'historique) */}
<button onClick={() => nav.replace('/login').to()}>
Se connecter
</button>
</div>
);
}
Exemple complet : Layout avec navigation
// components/layouts/MainLayout/index.jsx
import { useState } from 'react';
import { useLocation } from 'react-router-dom';
import {
Navbar,
Sidebar,
Tabbar,
TabbarItem,
UpperNavbarItem
} from '@cap-rel/smartcommon';
import { useNavigation, useGlobalStates, useApi } from '@cap-rel/smartcommon';
import {
FiMenu,
FiHome,
FiList,
FiPlus,
FiSettings,
FiLogOut
} from 'react-icons/fi';
export const MainLayout = ({ children, title }) => {
const [sidebarOpen, setSidebarOpen] = useState(false);
const nav = useNavigation();
const location = useLocation();
const api = useApi();
const gst = useGlobalStates();
const handleLogout = async () => {
await api.logout();
nav.navigate('/login');
};
return (
<div className="min-h-screen flex flex-col">
{/* Navbar */}
<Navbar title={title}>
<UpperNavbarItem
icon={FiMenu}
onClick={() => setSidebarOpen(true)}
/>
</Navbar>
{/* Sidebar */}
<Sidebar
isOpen={sidebarOpen}
onClose={() => setSidebarOpen(false)}
>
<div className="p-4 border-b">
<p className="font-bold">{gst.get('session')?.user?.name}</p>
</div>
<nav className="flex-1 p-2">
<SidebarLink
icon={FiHome}
label="Accueil"
onClick={() => {
nav.navigate('/');
setSidebarOpen(false);
}}
/>
<SidebarLink
icon={FiList}
label="Produits"
onClick={() => {
nav.navigate('/products');
setSidebarOpen(false);
}}
/>
<SidebarLink
icon={FiSettings}
label="Paramètres"
onClick={() => {
nav.navigate('/settings');
setSidebarOpen(false);
}}
/>
</nav>
<div className="p-4 border-t">
<button
onClick={handleLogout}
className="flex items-center gap-2 text-red-500"
>
<FiLogOut /> Déconnexion
</button>
</div>
</Sidebar>
{/* Contenu principal */}
<main className="flex-1 pb-16">
{children}
</main>
{/* Tabbar */}
<Tabbar>
<TabbarItem
icon={FiHome}
label="Accueil"
active={location.pathname === '/'}
onClick={() => nav.navigate('/')}
/>
<TabbarItem
icon={FiList}
label="Produits"
active={location.pathname.startsWith('/products')}
onClick={() => nav.navigate('/products')}
/>
<TabbarItem
icon={FiPlus}
label="Nouveau"
onClick={() => nav.navigate('/products/new')}
/>
<TabbarItem
icon={FiSettings}
label="Paramètres"
active={location.pathname === '/settings'}
onClick={() => nav.navigate('/settings')}
/>
</Tabbar>
</div>
);
};
// Composant helper
const SidebarLink = ({ icon: Icon, label, onClick }) => (
<button
onClick={onClick}
className="flex items-center gap-3 w-full p-3 rounded hover:bg-gray-100"
>
<Icon />
{label}
</button>
);
Utilisation du layout
// components/pages/private/ProductsPage/index.jsx
import { MainLayout } from '../../../layouts/MainLayout';
import { Block, List, ListItem } from '@cap-rel/smartcommon';
export const ProductsPage = () => {
const products = [...];
return (
<MainLayout title="Produits">
<Block>
<List>
{products.map(p => (
<ListItem
key={p.id}
title={p.label}
subtitle={`${p.price} €`}
/>
))}
</List>
</Block>
</MainLayout>
);
};
Points clés à retenir
- Navbar = barre supérieure avec titre et actions
- Sidebar = menu latéral coulissant
- Tabbar = navigation par onglets en bas
- useNavigation = navigation programmatique
- Combiner ces composants dans un Layout réutilisable
← Chapitre précédent | Retour au module | Chapitre suivant : Formulaires ->