Grille des besoins : corrige le chevauchement de la ligne d'en-tête

La cellule « Affectés / idéal » (figée en haut à gauche) et le libellé
« N postes » partageaient le même z-index (6) — une égalité entre deux
éléments sticky peut se départager de façon imprévisible pendant un
défilement selon le navigateur. Par ailleurs, l'infobulle de survol
d'une cellule de créneau (z-20) n'était pas isolée dans son propre
contexte d'empilement et rivalisait donc directement avec les en-têtes
figés (max z-6) : un survol déclenché par la molette pendant un
défilement (le curseur reste immobile, ce sont les cellules qui
défilent dessous) pouvait la faire passer au-dessus de l'en-tête le
temps de sa transition de disparition.

Hiérarchie refaite sans égalité (coins figés 20/21 > en-têtes 11/12 >
libellés de poste 10 > cellules, chacune isolée via `relative z-0`).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
AdamThomas
2026-09-28 19:34:34 +02:00
co-authored by Claude Sonnet 5
parent 8b9c0ac498
commit 5f7718adfd
2 changed files with 41 additions and 56 deletions
+15 -6
View File
@@ -223,7 +223,7 @@ export function GrillePage() {
<div className="grid w-max border-b-[3px] border-ink" style={{ gridTemplateColumns }}>
<div
style={{ gridColumn: '1 / span 1', gridRow: 1 }}
className="sticky left-0 top-0 z-[6] flex items-center gap-[6px] border-b-2 border-r-[3px] border-ink border-b-cream bg-ink px-2 py-[6px] font-display text-[10px] font-extrabold uppercase tracking-wider text-cream"
className="sticky left-0 top-0 z-[20] flex items-center gap-[6px] border-b-2 border-r-[3px] border-ink border-b-cream bg-ink px-2 py-[6px] font-display text-[10px] font-extrabold uppercase tracking-wider text-cream"
>
{t('grille.positionsCount', { count: matrix.data.positions.length })}
<HelpTip text={t('grille.footnote')} side="bottom" />
@@ -231,7 +231,7 @@ export function GrillePage() {
{shownDays.map((day, di) => (
<div
key={day.date}
className="sticky top-0 z-[4] flex items-baseline gap-[7px] border-b-2 border-r-[3px] border-ink border-b-cream border-r-cream bg-ink px-[9px] py-[6px] text-cream"
className="sticky top-0 z-[12] flex items-baseline gap-[7px] border-b-2 border-r-[3px] border-ink border-b-cream border-r-cream bg-ink px-[9px] py-[6px] text-cream"
style={{
gridColumn: `${dayColumnStarts[di]} / span ${day.slotLabels.length}`,
gridRow: 1,
@@ -245,7 +245,7 @@ export function GrillePage() {
<div
style={{ gridColumn: '1 / span 1', gridRow: 2 }}
className="sticky left-0 top-[27px] z-[6] flex items-center border-b-[3px] border-r-[3px] border-ink bg-sand px-2 font-mono text-[8.5px] opacity-60"
className="sticky left-0 top-[27px] z-[21] flex items-center border-b-[3px] border-r-[3px] border-ink bg-sand px-2 font-mono text-[8.5px] opacity-60"
>
{t('grille.assignedRequired')}
</div>
@@ -254,7 +254,7 @@ export function GrillePage() {
<div
key={`${day.date}|${i}`}
style={{ gridColumn: `${dayColumnStarts[di] + i} / span 1`, gridRow: 2 }}
className="sticky top-[27px] z-[3] overflow-hidden text-ellipsis whitespace-nowrap border-b-[3px] border-r border-ink bg-sand px-[2px] py-[5px] text-center font-mono text-[9.5px]"
className="sticky top-[27px] z-[11] overflow-hidden text-ellipsis whitespace-nowrap border-b-[3px] border-r border-ink bg-sand px-[2px] py-[5px] text-center font-mono text-[9.5px]"
>
{label}
</div>
@@ -354,7 +354,7 @@ function PositionRow({
<>
<div
style={{ gridColumn: '1 / span 1', gridRow: rowIndex }}
className="sticky left-0 z-[2] flex h-9 flex-col justify-center gap-[1px] border-b border-r-[3px] border-ink bg-cream px-2"
className="sticky left-0 z-[10] flex h-9 flex-col justify-center gap-[1px] border-b border-r-[3px] border-ink bg-cream px-2"
>
<span className="truncate font-display text-[12px] font-bold tracking-tight">
{position.name}
@@ -443,7 +443,16 @@ function ShiftCell({
onClick={() => onSelect(shift.shift_id)}
style={{ gridColumn, gridRow }}
className={cx(
'group relative flex h-9 cursor-pointer items-center justify-center gap-[3px] border-b border-r border-ink',
// `relative z-0` (plutôt que `relative` seul) crée un contexte
// d'empilement local : l'infobulle interne (`z-20`, voir plus bas)
// ne rivalise qu'avec les autres enfants de cette cellule, jamais
// avec les en-têtes figés de la grille (z-10 à 21) — sans ça, un
// survol déclenché par la molette pendant un défilement (le
// curseur reste immobile à l'écran, ce sont les cellules qui
// défilent dessous) pouvait faire passer l'infobulle au-dessus de
// la ligne d'en-tête « Affectés / idéal » pendant sa transition de
// disparition.
'group relative z-0 flex h-9 cursor-pointer items-center justify-center gap-[3px] border-b border-r border-ink',
undefinedNeed && 'bg-sand-dark hover:bg-sand',
isCrit && 'bg-hatch-orange',
isOver && 'bg-hatch-mauve hover:bg-mauve-hover',