files: seek bar — the ball goes where the seek went and waits there; the seek is on release

Same fix as Ham Share's: a trailing change event read the bar's old position
back as a drag in progress.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-10-23 15:11:31 -07:00
co-authored by Claude Opus 5.5
parent d1b3ef7efd
commit 1464e1f7a0
+19 -4
View File
@@ -214,6 +214,23 @@ export default function Player({ path, sudo }: { path: string; sudo: boolean; na
else v.pause()
poke()
}, [poke])
// The seek bar: while it is dragged the ball follows the finger (the input event); the seek
// itself is on release (the browser's own change event, which a click, a drag and the arrow
// keys all end with). The ball is put where the seek went at once, so it waits there while
// the piece is made rather than going back to where the film was.
const seekBar = useRef<HTMLInputElement>(null)
useEffect(() => {
const el = seekBar.current
if (!el) return
const commit = () => {
const x = Number(el.value), v = video.current
if (v && Math.abs(v.currentTime - x) > 0.05) v.currentTime = x
setTime(x)
setScrub(null)
}
el.addEventListener('change', commit)
return () => el.removeEventListener('change', commit)
})
const seekBy = useCallback((d: number) => {
const v = video.current
if (!v) return
@@ -314,10 +331,8 @@ export default function Player({ path, sudo }: { path: string; sudo: boolean; na
<div className="vp-bottom">
<div className="vp-seek" style={{ ['--p' as string]: dur ? `${(shown / dur) * 100}%` : '0%', ['--b' as string]: dur ? `${(buffered / dur) * 100}%` : '0%' }}>
<input type="range" min={0} max={dur || 1} step={0.1} value={shown} aria-label="Seek"
onChange={(e) => { setScrub(Number(e.target.value)); poke() }}
onPointerUp={(e) => { if (v) v.currentTime = Number((e.target as HTMLInputElement).value); setScrub(null) }}
onKeyUp={(e) => { if (v) v.currentTime = Number((e.target as HTMLInputElement).value); setScrub(null) }} />
<input ref={seekBar} type="range" min={0} max={dur || 1} step={0.1} value={shown} aria-label="Seek"
onChange={(e) => { setScrub(Number(e.target.value)); poke() }} />
</div>
<div className="vp-row">
<button className="vp-icon" onClick={toggle} aria-label={paused ? 'Play' : 'Pause'}>{paused ? <I d={PLAY} fill /> : <I d={PAUSE} />}</button>