feat(shorts): flèches en ligne avec le menu + fondu des contrôles au repos
CI / build-and-test (push) Successful in 14m54s
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:
@@ -572,6 +572,28 @@ body {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* Contrôles flottants (rangée de flèches + FAB menu) : presque fondu au repos
|
||||
pour laisser la vidéo respirer (ton pro), pleins dès le moindre mouvement
|
||||
de souris (chromeVisible) ou survol direct. Le panneau ouvert et le FAB
|
||||
ouvert ne sont jamais atténués. Le tactile ne déclenche presque pas
|
||||
mousemove : sur mobile tout reste donc plein (comportement voulu). */
|
||||
.shorts-frame .shorts-ctrl,
|
||||
.shorts-frame app-player-menu .pm-fab {
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
.shorts-chrome-hidden .shorts-frame .shorts-ctrl {
|
||||
opacity: 0.18;
|
||||
}
|
||||
.shorts-chrome-hidden .shorts-frame .shorts-ctrl:not(:disabled):hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.shorts-chrome-hidden .shorts-frame app-player-menu .pm-fab {
|
||||
opacity: 0.18;
|
||||
}
|
||||
.shorts-chrome-hidden .shorts-frame app-player-menu .pm-fab:hover,
|
||||
.shorts-chrome-hidden .shorts-frame app-player-menu .pm-fab.pm-fab-open {
|
||||
opacity: 1;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.shorts-chrome {
|
||||
transition: none;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user