fix: stabilize volume hover panel

This commit is contained in:
2026-04-18 07:09:42 +02:00
parent aa14114963
commit 99e86bfdad
3 changed files with 38 additions and 35 deletions

View File

@@ -232,7 +232,7 @@ templ VideoPlayer(data WatchPageData) {
</svg>
</button>
<div data-volume-wrap class="volume-wrap relative flex h-10 w-10 items-center justify-center overflow-visible">
<span data-volume-bridge class="pointer-events-none absolute inset-x-0 bottom-full h-[calc(100%+100px)]"></span>
<span data-volume-bridge class="absolute inset-x-0 bottom-full h-[calc(100%+26px)]"></span>
<div class="volume-panel pointer-events-none absolute bottom-[calc(100%+26px)] left-1/2 z-30 -translate-x-1/2 opacity-0 invisible transition-opacity">
<input
data-volume-range

View File

@@ -29,6 +29,7 @@ const initPlayer = (): void => {
const iconPause = container.querySelector('[data-icon-pause]') as SVGElement
const muteBtn = container.querySelector('[data-mute]') as HTMLButtonElement
const volumeWrap = container.querySelector('[data-volume-wrap]') as HTMLElement
const volumePanel = container.querySelector('.volume-panel') as HTMLElement
const volumeRange = container.querySelector('[data-volume-range]') as HTMLInputElement
const iconVolume = container.querySelector('[data-icon-volume]') as SVGElement
const iconMuted = container.querySelector('[data-icon-muted]') as SVGElement
@@ -501,25 +502,35 @@ const initPlayer = (): void => {
showControls()
})
muteBtn?.addEventListener('mouseenter', () => {
muteBtn.classList.add('show-volume')
isHoveringVolume = true
showControls()
})
const setVolumePanelOpen = (isOpen: boolean): void => {
if (volumePanel) {
volumePanel.classList.toggle('is-visible', isOpen)
}
volumeWrap?.classList.toggle('is-volume-open', isOpen)
isHoveringVolume = isOpen
if (isOpen) showControls()
}
muteBtn?.addEventListener('mouseleave', () => {
isHoveringVolume = false
showControls()
})
const openVolumePanel = (): void => {
setVolumePanelOpen(true)
}
volumeWrap?.addEventListener('focusin', () => {
isHoveringVolume = true
showControls()
})
const closeVolumePanel = (): void => {
setVolumePanelOpen(false)
}
volumeWrap?.addEventListener('focusout', () => {
isHoveringVolume = false
showControls()
closeVolumePanel()
muteBtn?.addEventListener('mouseenter', openVolumePanel)
volumeWrap?.addEventListener('mouseleave', closeVolumePanel)
volumeWrap?.addEventListener('focusin', openVolumePanel)
volumeWrap?.addEventListener('focusout', (event: FocusEvent) => {
const nextTarget = event.relatedTarget
if (nextTarget instanceof Node && volumeWrap.contains(nextTarget)) return
closeVolumePanel()
})
backwardBtn?.addEventListener('click', () => seekBy(-10))

View File

@@ -75,29 +75,21 @@
box-shadow: 0 0 10px rgba(0, 0, 0, 0.55);
}
.volume-wrap::before {
content: '';
position: absolute;
left: -10px;
right: -10px;
bottom: 100%;
height: 130px;
}
.volume-wrap:has([data-mute]:hover) .volume-panel,
.volume-wrap:has([data-volume-bridge]:hover) .volume-panel,
.volume-wrap:has(.volume-panel:hover) .volume-panel,
.volume-wrap:focus-within .volume-panel,
.volume-panel:hover,
.volume-panel:focus-within {
.volume-panel.is-visible {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.volume-wrap:has([data-mute]:hover) .volume-underline,
.volume-wrap:has([data-volume-bridge]:hover) .volume-underline,
.volume-wrap:has(.volume-panel:hover) .volume-underline,
.volume-wrap[data-volume-wrap] [data-volume-bridge] {
pointer-events: none;
}
.volume-wrap.is-volume-open[data-volume-wrap] [data-volume-bridge] {
pointer-events: auto;
}
.volume-wrap:has(.volume-panel.is-visible) .volume-underline,
.volume-wrap:focus-within .volume-underline {
opacity: 1;
}