Skjul flytende "Registrer score" når spillerlisten er nådd/passert
Ny playerListReached-tilstand skjuler den flytende knappen (og dens spacer) fra #140 så snart spillerlisten når headerens høyde -- forblir skjult resten av veien nedover, dukker opp igjen kun ved scroll tilbake opp forbi listen. Første forsøk med IntersectionObserver+rootMargin var feil (fyrer ikke på interne terskler mens allerede overlappende) -- byttet til en scroll/resize-lytter med direkte getBoundingClientRect-sjekk. Se CHANGELOG.md #141. Ikke rullet ut ennå. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
200d00cf5f
commit
76b306cb29
2 changed files with 101 additions and 13 deletions
37
CHANGELOG.md
37
CHANGELOG.md
|
|
@ -13547,3 +13547,40 @@ Neste steg:
|
|||
**Rullet ut 2026-08-20** -- bruker bekreftet, samlet med #139. Ren
|
||||
frontend. `docker compose build teecup_frontend && up -d`, rene
|
||||
logger, 200 OK.
|
||||
|
||||
141. **Flytende "Registrer score" hang igjen synlig etter at spillerlisten
|
||||
var nådd, 2026-08-20.** Direkte oppfølging av #140 -- bruker viste
|
||||
skjermbilde: selv etter å ha scrollet forbi spillerkortene (helt ned
|
||||
til kommentarfeltet), sto den flytende "Registrer score ↓"-knappen
|
||||
fortsatt og fløt over bunnen av skjermen, uten mening siden
|
||||
spillerlisten allerede var passert (pilen peker "ned til spillerne",
|
||||
men spillerne var nå OVER, ikke under). Bruker spurte om dette burde
|
||||
V0-promptes -- Claude vurderte det til å være ren VIS/SKJUL-LOGIKK
|
||||
rundt et allerede ferdig designet element (fra #140), ikke en ny
|
||||
visuell flate, og løste det direkte.
|
||||
|
||||
`round-detail.tsx`: ny `playerListReached`-tilstand, sant når
|
||||
spillerlistens toppkant har nådd/passert headerens høyde (112px,
|
||||
samme referanse som `scroll-mt-28`). Knappen (og spaceren under den,
|
||||
fra #140) vises nå kun når `mapExpanded && !playerListReached`.
|
||||
|
||||
**Feilslått første forsøk, funnet i scratch:** brukte først
|
||||
`IntersectionObserver` med en krympet `rootMargin` for å fange
|
||||
112px-krysningen. Viste seg feil -- med standard `threshold: [0]`
|
||||
fyrer observatøren KUN ved 0%->0%-kryssinger (elementet helt inn/ut av
|
||||
roten), ikke ved at `boundingClientRect.top` passerer en vilkårlig
|
||||
linje MENS elementet allerede overlapper roten. Spillerlisten ble
|
||||
dermed hengende fast på "ikke nådd" gjennom resten av scrollingen,
|
||||
identisk med bugen selv om koden så riktig ut. Erstattet med en vanlig
|
||||
`scroll`/`resize`-lytter (passive) som leser
|
||||
`getBoundingClientRect().top` direkte -- korrekt, kontinuerlig
|
||||
oppdatering.
|
||||
|
||||
**Verifisert i scratch** (samme mock-oppsett som #139/#140): utvidet
|
||||
banekartet, trykket flytende "Registrer score" -- knappen forsvant nå
|
||||
korrekt idet spillerkort 1 landet synlig, ingen tomt mellomrom stod
|
||||
igjen. Scrollet helt ned til kommentarfeltet -- knappen forble skjult
|
||||
(ikke lenger den opprinnelige bugen). Scrollet tilbake opp forbi
|
||||
spillerlisten -- knappen dukket korrekt opp igjen. `tsc --noEmit` rent.
|
||||
|
||||
**Ikke rullet ut ennå** -- venter på bekreftelse fra bruker.
|
||||
|
|
|
|||
|
|
@ -6719,6 +6719,43 @@ function PlayerHoleCards({
|
|||
const [mapExpanded, setMapExpanded] = useState(false)
|
||||
const [distanceAvailable, setDistanceAvailable] = useState(false)
|
||||
const playerListRef = useRef<HTMLUListElement>(null)
|
||||
// Flytende "Registrer score" (2026-08-20) skal kun vises mens
|
||||
// spillerlisten fortsatt er UTENFOR synlig område -- brukerfunn: den
|
||||
// hang igjen synlig (og dekket delvis over spillerkort 1) selv etter at
|
||||
// brukeren allerede hadde scrollet forbi/til spillerlisten, der den ikke
|
||||
// lenger gir mening ("Registrer score ↓" når du allerede ser
|
||||
// spillerne). "Reached" = listens toppkant har nådd/passert headerens
|
||||
// faktiske høyde (samme 112px som scroll-mt-28 andre steder i denne
|
||||
// filen) -- forblir sant BÅDE mens listen vises OG etter at brukeren
|
||||
// har scrollet helt forbi den (listen ligger da over synlig område,
|
||||
// `top` er negativ). Kun scroll TILBAKE OPP forbi listen (top blir
|
||||
// igjen større enn 112) gjør at knappen skal dukke opp igjen.
|
||||
//
|
||||
// Første forsøk brukte IntersectionObserver med en krympet rootMargin
|
||||
// -- viste seg FEIL i scratch-verifisering: med standard threshold=[0]
|
||||
// fyrer den kun ved 0%->>0%-kryssinger (element helt inn/ut av roten),
|
||||
// ikke ved at `boundingClientRect.top` passerer en INTERN linje midt i
|
||||
// et allerede-overlappende element. Listen ble dermed hengende på
|
||||
// "ikke nådd" gjennom hele resten av scrollingen forbi 112px-grensen.
|
||||
// Vanlig scroll-lytter med direkte `getBoundingClientRect()`-sjekk gir
|
||||
// korrekt, kontinuerlig oppdatering.
|
||||
const [playerListReached, setPlayerListReached] = useState(false)
|
||||
useEffect(() => {
|
||||
if (!mapExpanded) return
|
||||
const HEADER_HEIGHT_PX = 112
|
||||
function check() {
|
||||
const el = playerListRef.current
|
||||
if (!el) return
|
||||
setPlayerListReached(el.getBoundingClientRect().top <= HEADER_HEIGHT_PX)
|
||||
}
|
||||
check()
|
||||
window.addEventListener("scroll", check, { passive: true })
|
||||
window.addEventListener("resize", check)
|
||||
return () => {
|
||||
window.removeEventListener("scroll", check)
|
||||
window.removeEventListener("resize", check)
|
||||
}
|
||||
}, [mapExpanded])
|
||||
const holeMeta: Record<number, { par: number; index: number }> = {}
|
||||
for (const list of Object.values(holesByParticipant)) {
|
||||
for (const h of list) {
|
||||
|
|
@ -7102,23 +7139,34 @@ function PlayerHoleCards({
|
|||
/>
|
||||
)}
|
||||
|
||||
{/* Flytende "Registrer score" (V0-eksport, 2026-08-20) -- kun mens
|
||||
banekartet er utvidet. `fixed` posisjonerer mot selve viewporten
|
||||
(ingen ancestor her setter transform/filter, så det er trygt),
|
||||
gradient-scrim + skygge løfter den visuelt over innholdet under,
|
||||
`env(safe-area-inset-bottom)` unngår at den klemmes helt inntil
|
||||
kanten på telefoner med gest-navigasjon. `pointer-events-none` på
|
||||
selve scrim-sonen når skjult/i overgang, slik at den aldri
|
||||
blokkerer klikk på innhold bak. */}
|
||||
{/* Flytende "Registrer score" (V0-eksport, 2026-08-20) -- mens
|
||||
banekartet er utvidet OG spillerlisten fortsatt er utenfor synlig
|
||||
område (se `playerListReached` over -- uten denne betingelsen hang
|
||||
knappen igjen synlig, og dekket delvis over spillerkort 1, selv
|
||||
etter at brukeren allerede hadde scrollet dit). `fixed`
|
||||
posisjonerer mot selve viewporten (ingen ancestor her setter
|
||||
transform/filter, så det er trygt), gradient-scrim + skygge
|
||||
løfter den visuelt over innholdet under, `env(safe-area-inset-
|
||||
bottom)` unngår at den klemmes helt inntil kanten på telefoner
|
||||
med gest-navigasjon. `pointer-events-none` på selve scrim-sonen
|
||||
når skjult/i overgang, slik at den aldri blokkerer klikk på
|
||||
innhold bak. */}
|
||||
<div
|
||||
aria-hidden={!mapExpanded}
|
||||
aria-hidden={!mapExpanded || playerListReached}
|
||||
className={cn(
|
||||
"fixed inset-x-0 bottom-0 z-30 px-5 pb-[calc(0.875rem+env(safe-area-inset-bottom))] pt-8 transition-all duration-300 ease-out",
|
||||
"bg-gradient-to-t from-background via-background/90 to-transparent",
|
||||
mapExpanded ? "translate-y-0 opacity-100" : "pointer-events-none translate-y-full opacity-0",
|
||||
mapExpanded && !playerListReached
|
||||
? "translate-y-0 opacity-100"
|
||||
: "pointer-events-none translate-y-full opacity-0",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"mx-auto w-full max-w-3xl",
|
||||
mapExpanded && !playerListReached ? "pointer-events-auto" : "pointer-events-none",
|
||||
)}
|
||||
>
|
||||
<div className={cn("mx-auto w-full max-w-3xl", mapExpanded ? "pointer-events-auto" : "pointer-events-none")}>
|
||||
<div className="rounded-2xl shadow-xl shadow-black/25">
|
||||
<RegisterScoreButton
|
||||
onClick={() => playerListRef.current?.scrollIntoView({ behavior: "smooth", block: "start" })}
|
||||
|
|
@ -7127,8 +7175,11 @@ function PlayerHoleCards({
|
|||
</div>
|
||||
</div>
|
||||
{/* Gir siste innhold på siden rom til å scrolle klar av den flytende
|
||||
knappen i stedet for å bli permanent skjult bak den. */}
|
||||
{mapExpanded && <div aria-hidden="true" className="h-20" />}
|
||||
knappen i stedet for å bli permanent skjult bak den -- samme
|
||||
betingelse som selve knappen (2026-08-20-rettelsen), ellers ble
|
||||
det stående et tomt, uforklart mellomrom selv etter at knappen
|
||||
alt var skjult. */}
|
||||
{mapExpanded && !playerListReached && <div aria-hidden="true" className="h-20" />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue