feat(shorts): flèches en ligne avec le menu + fondu des contrôles au repos
CI / build-and-test (push) Successful in 14m54s

Demandes (page /shorts) : retrouver les flèches Préc./Suiv. là où se
trouve le menu, et des contrôles presque invisibles au repos pour
laisser la vidéo respirer (ton pro).

- Flèches déplacées du milieu des côtés vers la rangée HAUTE du cadre,
  centrées : même niveau que le FAB menu, sans chevaucher la flèche
  retour mobile (gauche) ni le FAB (droite). Le toast « Lien copié »
  passe en z-30 pour rester lisible sous la rangée.
- Fondu au repos : nouvelles règles index.css — flèches (.shorts-ctrl)
  et FAB à 0.18 d'opacité quand le chrome est au repos (2,5 s sans
  souris), pleins au moindre mouvement (chromeVisible) ou au survol
  direct (:hover), jamais atténués quand le panneau est ouvert
  (pm-fab-open). Sur tactile, le chrome ne se masque jamais (pas de
  mousemove : comportement voulu, mobile inchangé).
- Le minuteur d'immobilité est armé au chargement (pokeChrome sous
  matchMedia('(hover: hover)')) : sans un premier mousemove,
  chromeVisible restait true et le fondu ne s'armait jamais.
- mouseenter sur l'iframe : en entrant sur la vidéo, le document
  parent ne reçoit pas les mouvements internes à l'iframe — l'entrée
  réveille les contrôles.

Recette Playwright desktop+mobile, 14/14 : position haute centrée,
absence de chevauchements, repos 0.18 (flèches + FAB), retour plein
après mouvement réel, mobile plein en permanence. Build + test:section
verts avant commit.
This commit is contained in:
2026-10-02 11:28:03 -04:00
parent 3ab61821b2
commit e44b545e01
3 changed files with 46 additions and 13 deletions
@@ -24,6 +24,7 @@
<div #shortsFrame class="shorts-frame relative shrink-0 overflow-hidden bg-black rounded-none ring-0 shadow-none sm:rounded-2xl sm:ring-1 sm:ring-white/10 sm:shadow-2xl">
@if (embedUrl(); as url) {
<iframe [src]="url" [title]="current()?.title || 'Short video'" class="absolute inset-0 w-full h-full" [class.embed-wide]="wideEmbed()" frameborder="0" playsinline
(mouseenter)="pokeChrome()"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture; web-share"
allowfullscreen referrerpolicy="origin"></iframe>
} @else if (embedWaiting()) {
@@ -205,22 +206,27 @@
}
</div>
<!-- Navigation précédent / suivant : SUR la vidéo (hors auto-hide du
chrome, hors geste tactile — stopPropagation dans les handlers). -->
<button type="button" (click)="prev(); $event.stopPropagation()" [disabled]="!canPrev()"
class="absolute left-1.5 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-40"
aria-label="Short précédent">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M15.5 4.5 8 12l7.5 7.5z"/></svg>
</button>
<button type="button" (click)="next(); $event.stopPropagation()" [disabled]="!canNext()"
class="absolute right-1.5 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-40"
aria-label="Short suivant">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8.5 4.5 16 12l-7.5 7.5z"/></svg>
</button>
<!-- Navigation précédent / suivant : MÊME LIGNE que le menu (haut
du cadre, centré pour ne pas chevaucher la flèche retour mobile
ni le FAB) — on les retrouve là où se trouve le menu.
Classe shorts-ctrl : fondu au repos, pleins au mouvement/
survol (index.css). stopPropagation dans les handlers. -->
<div class="absolute left-1/2 top-1.5 z-20 flex -translate-x-1/2 gap-2">
<button type="button" (click)="prev(); $event.stopPropagation()" [disabled]="!canPrev()"
class="shorts-ctrl flex h-10 w-10 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-40"
aria-label="Short précédent">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M15.5 4.5 8 12l7.5 7.5z"/></svg>
</button>
<button type="button" (click)="next(); $event.stopPropagation()" [disabled]="!canNext()"
class="shorts-ctrl flex h-10 w-10 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-40"
aria-label="Short suivant">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8.5 4.5 16 12l-7.5 7.5z"/></svg>
</button>
</div>
}
@if (shareCopied()) {
<div class="absolute top-2 left-1/2 -translate-x-1/2 px-3 py-1.5 rounded-full bg-black/70 text-white text-xs" role="status">Lien copié ✓</div>
<div class="absolute top-2 left-1/2 -translate-x-1/2 z-30 px-3 py-1.5 rounded-full bg-black/70 text-white text-xs" role="status">Lien copié ✓</div>
}
</div>
</div>
@@ -714,6 +714,11 @@ export class WatchShortComponent implements OnDestroy {
});
try { window.addEventListener('message', this.handleShortMessage); } catch {}
try { document.addEventListener('fullscreenchange', this.handleFullscreenChange); } catch {}
// Arme le minuteur d'immobilité dès le chargement : sans un premier
// mousemove, chromeVisible resterait à true et les contrôles flottants
// (flèches + FAB) ne feraient jamais leur fondu « au repos ». Desktop
// seulement (hover) : sur tactile le chrome reste visible en permanence.
try { if (matchMedia('(hover: hover)').matches) this.pokeChrome(); } catch {}
}
ngOnDestroy(): void {