Top Bar petits écrans

This commit is contained in:
2026-06-14 17:53:02 +02:00
parent 1bc626e7d5
commit 440f59d86c
2 changed files with 130 additions and 18 deletions
+67 -18
View File
@@ -217,10 +217,22 @@ function IconPlusCircle() {
);
}
const IconBurger = () => (
<svg width="28" height="28" viewBox="0 0 28 28" fill="none"
stroke="currentColor" strokeWidth="2" strokeLinecap="round"
aria-hidden="true">
<line x1="4" y1="8" x2="24" y2="8"/>
<line x1="4" y1="14" x2="24" y2="14"/>
<line x1="4" y1="20" x2="24" y2="20"/>
</svg>
);
export default function Layout() {
const { sidebarCollapsed, setSidebarCollapsed, toggleSidebar, displayMode, setDisplayMode } = useUi();
const navigate = useNavigate();
const [navIcons, setNavIcons] = useState({});
const [burgerOpen, setBurgerOpen] = useState(false);
const burgerRef = useRef(null);
// Auto-collapse en mode portrait sur tablette (7681200px)
useEffect(() => {
@@ -231,6 +243,16 @@ export default function Layout() {
return () => mq.removeEventListener('change', handler);
}, [setSidebarCollapsed]);
// Fermer le burger au clic extérieur
useEffect(() => {
if (!burgerOpen) return;
const handler = (e) => {
if (burgerRef.current && !burgerRef.current.contains(e.target)) setBurgerOpen(false);
};
document.addEventListener('mousedown', handler);
return () => document.removeEventListener('mousedown', handler);
}, [burgerOpen]);
useEffect(() => {
api.get('/icons').then(rows => {
const m = {};
@@ -302,28 +324,55 @@ export default function Layout() {
<div className="topbar topbar-global">
<ProjectSearch />
<div className="topbar-right">
<button
className="btn-add-invest"
onClick={() => navigate('/investissements?new=1')}
>
<IconPlusCircle />
Ajout Investissement
{/* Boutons visibles sur grand écran */}
<button className="btn-add-invest topbar-action-btn" onClick={() => navigate('/investissements?new=1')}>
<IconPlusCircle />Ajout Investissement
</button>
<button
className="btn-add-invest"
onClick={() => navigate('/remboursements?new=1')}
>
<IconPlusCircle />
Nouveau remboursement
<button className="btn-add-invest topbar-action-btn" onClick={() => navigate('/remboursements?new=1')}>
<IconPlusCircle />Nouveau remboursement
</button>
<button
className="btn-add-invest"
onClick={() => navigate('/depots-retraits?new=1')}
>
<IconPlusCircle />
Nouveau dépôt/retrait
<button className="btn-add-invest topbar-action-btn" onClick={() => navigate('/depots-retraits?new=1')}>
<IconPlusCircle />Nouveau dépôt/retrait
</button>
{/* Bouton burger — visible sur tablette/mobile */}
<div className="topbar-burger-wrap" ref={burgerRef}>
<button
className="topbar-burger-btn"
onClick={() => setBurgerOpen(v => !v)}
aria-label="Menu actions"
aria-expanded={burgerOpen}
>
<IconBurger />
</button>
{burgerOpen && (
<div className="topbar-burger-menu">
<button className="topbar-burger-search-item" onClick={() => { setBurgerOpen(false); document.querySelector('.project-search-input')?.focus(); }}>
<svg width="15" height="15" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" aria-hidden="true"><circle cx="7.5" cy="7.5" r="5.5"/><line x1="11.5" y1="11.5" x2="16" y2="16"/></svg>
Rechercher un projet
</button>
<div className="topbar-burger-sep" />
<button className="topbar-burger-item" onClick={() => { setBurgerOpen(false); navigate('/investissements?new=1'); }}>
<IconPlusCircle />Ajout Investissement
</button>
<button className="topbar-burger-item" onClick={() => { setBurgerOpen(false); navigate('/remboursements?new=1'); }}>
<IconPlusCircle />Nouveau remboursement
</button>
<button className="topbar-burger-item" onClick={() => { setBurgerOpen(false); navigate('/depots-retraits?new=1'); }}>
<IconPlusCircle />Nouveau dépôt/retrait
</button>
<div className="topbar-burger-sep" />
<div className="topbar-burger-toggle">
<span className="topbar-burger-toggle-label">Affichage</span>
<div className="display-toggle" role="group" aria-label="Mode d'affichage">
<button className={`display-toggle-btn${displayMode === 'brut' ? ' active' : ''}`} onClick={() => setDisplayMode('brut')} aria-pressed={displayMode === 'brut'}>Brut</button>
<button className={`display-toggle-btn${displayMode === 'net' ? ' active' : ''}`} onClick={() => setDisplayMode('net')} aria-pressed={displayMode === 'net'}>Net</button>
</div>
</div>
</div>
)}
</div>
<div className="display-toggle" role="group" aria-label="Mode d'affichage">
<button
className={`display-toggle-btn${displayMode === 'brut' ? ' active' : ''}`}
+63
View File
@@ -240,6 +240,69 @@ a { color: var(--primary); }
}
.btn-add-invest:hover { background: var(--primary-hover); box-shadow: 0 3px 12px rgba(59,130,246,.5); }
/* ── Burger menu topbar ──────────────────────────────────────── */
.topbar-burger-wrap { position: relative; display: none; }
.topbar-burger-btn {
display: flex; align-items: center; justify-content: center;
padding: 0; background: none; border: none;
cursor: pointer; color: var(--text);
transition: color .15s;
}
.topbar-burger-btn:hover { color: var(--primary); }
.topbar-burger-menu {
position: absolute; top: calc(100% + 8px); right: 0;
background: var(--surface); border: 1px solid var(--border);
border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,.10);
min-width: 220px; z-index: 200; padding: 6px;
display: flex; flex-direction: column; gap: 2px;
}
.topbar-burger-item {
display: flex; align-items: center; gap: 8px;
padding: 9px 12px; border: none; border-radius: 6px;
background: none; color: var(--text); font-size: var(--fs-base);
font-weight: 600; cursor: pointer; text-align: left; width: 100%;
transition: background .12s;
}
.topbar-burger-item:hover { background: var(--surface-2); }
.topbar-burger-item svg { color: var(--primary); flex-shrink: 0; }
.topbar-burger-search-item {
display: flex; align-items: center; gap: 8px;
padding: 9px 12px; border: none; border-radius: 6px;
background: none; color: var(--text-muted); font-size: var(--fs-sm);
cursor: pointer; text-align: left; width: 100%;
transition: background .12s;
}
.topbar-burger-search-item:hover { background: var(--surface-2); }
.topbar-burger-sep { height: 1px; background: var(--border); margin: 2px 0; }
/* Bloc toggle dans le burger */
.topbar-burger-toggle {
display: flex; align-items: center; justify-content: space-between;
padding: 6px 12px 4px;
}
.topbar-burger-toggle-label {
font-size: var(--fs-sm); color: var(--text-muted);
}
/* Tablette : masquer les boutons texte et le toggle, afficher le burger */
@media (max-width: 1100px) {
.topbar-action-btn { display: none !important; }
.topbar-burger-wrap { display: block; }
.topbar-global .topbar-right > .display-toggle { display: none; }
}
/* Mobile : masquer aussi la barre de recherche dans la topbar */
@media (max-width: 640px) {
.topbar-global .project-search-wrap { display: none; }
.topbar-burger-search-item { display: flex; }
}
/* Sur tablette, masquer l'item recherche dans le burger (search reste visible) */
@media (min-width: 641px) and (max-width: 1100px) {
.topbar-burger-search-item,
.topbar-burger-search-item + .topbar-burger-sep { display: none; }
}
/* btn-icon — bouton icône discret (cercle hover) */
.btn-icon {
display: flex; align-items: center; justify-content: center;