fix(player): controls bar taps are not player gestures (DR-098)

The bottom play/pause button did nothing. The gesture listener lives on
the outer container and touch events bubble, so tapping the button ran
handleTouchStart (toggle #1) and then the button's own onclick (toggle
#2). The two cancelled out, leaving the control apparently dead.

Ignore container-level gestures for touches that land on an interactive
control: buttons, links, inputs (the seek bar), or anything inside the
controls bar, now marked `data-player-controls`. The rule itself is a
pure function over the ancestor chain (isControlSurfaceTouch), so it is
unit tested without a DOM.

Same root shape as the play-overlay bug in the previous commit: a second
click target over the video that the gesture layer did not account for.
This commit is contained in:
2026-07-30 15:27:01 +02:00
parent b98a530f48
commit dc8b732465
7 changed files with 84 additions and 5 deletions
+22
View File
@@ -31,6 +31,28 @@ export const DOUBLE_TAP_WINDOW_MS = 300;
*/
export const TOUCH_CLICK_SUPPRESS_MS = 700;
/**
* Whether a touch landed on an interactive control rather than the bare video
* surface, and so must NOT be interpreted as a play/pause or seek gesture.
*
* The gesture listener sits on the outer container, and touch events bubble, so
* without this a tap on the bottom control bar runs the gesture handler (toggle
* #1) *and* the button's own click handler (toggle #2) — the two cancel out and
* the button appears dead. Buttons, links, inputs (the seek bar), and anything
* inside an element marked `data-player-controls` are treated as controls.
*
* Takes the ancestor chain as plain tag/attribute pairs so the rule is unit
* testable without a DOM.
*/
export function isControlSurfaceTouch(
ancestors: Array<{ tag: string; isPlayerControls?: boolean }>
): boolean {
const INTERACTIVE = new Set(["button", "a", "input", "select", "textarea", "label"]);
return ancestors.some(
(node) => node.isPlayerControls === true || INTERACTIVE.has(node.tag.toLowerCase())
);
}
/**
* Whether a `click` should be ignored because a touch tap already handled it.
*