Skip to content

feat: add Gesture component / add useMediaContainer#805

Open
luwes wants to merge 12 commits intomainfrom
gesture
Open

feat: add Gesture component / add useMediaContainer#805
luwes wants to merge 12 commits intomainfrom
gesture

Conversation

@luwes
Copy link
Collaborator

@luwes luwes commented Mar 10, 2026

Todo:

  • account for mobile touch. show / hide controls

Adds a gesture component for React and HTML.

Has a type and command prop to customize in the future.
(inspired by @mihar-22 https://vidstack.io/docs/player/components/display/gesture/)

Question: should this component also have a pointer type prop?
Gestures behave differently for mouse or touch (tap), example: display click -> play / pause for mouse, show / hide controls for tap

@luwes luwes self-assigned this Mar 10, 2026
@vercel
Copy link

vercel bot commented Mar 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Mar 13, 2026 8:43pm

Request Review

@netlify
Copy link

netlify bot commented Mar 10, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 7cf099a
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69b476ddea3fc600089af2a5
😎 Deploy Preview https://deploy-preview-805--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

📦 Bundle Size Report

🎨 @videojs/html

Path Base PR Diff %
/video/minimal-skin 21.21 kB 21.54 kB +344 B +1.6% 🔺
/video/skin 21.37 kB 21.68 kB +319 B +1.5% 🔺
/ui/gesture 1.73 kB 🆕
/video (default) 21.97 kB 22.29 kB +327 B +1.5% 🔺
/video (default + hls) 152.28 kB 152.63 kB +362 B +0.2% 🔺
/video (minimal) 21.79 kB 22.11 kB +327 B +1.5% 🔺
/video (minimal + hls) 152.17 kB 152.52 kB +358 B +0.2% 🔺
Presets (7)
Entry Size
/video (default) 22.29 kB
/video (default + hls) 152.63 kB
/video (minimal) 22.11 kB
/video (minimal + hls) 152.52 kB
/audio (default) 20.82 kB
/audio (minimal) 20.77 kB
/background 6.46 kB
Media (4)
Entry Size
/media/background-video 617 B
/media/container 1.91 kB
/media/hls-video 131.23 kB
/media/simple-hls-video 11.89 kB
Players (3)
Entry Size
/video/player 6.33 kB
/audio/player 6.32 kB
/background/player 6.31 kB
Skins (16)
Entry Type Size
/video/minimal-skin.css css 2.65 kB
/video/skin.css css 2.68 kB
/video/minimal-skin js 21.54 kB
/video/minimal-skin.tailwind js 21.55 kB
/video/skin js 21.68 kB
/video/skin.tailwind js 21.79 kB
/audio/minimal-skin.css css 2.17 kB
/audio/skin.css css 2.19 kB
/audio/minimal-skin js 20.25 kB
/audio/minimal-skin.tailwind js 20.20 kB
/audio/skin js 20.22 kB
/audio/skin.tailwind js 20.39 kB
/background/skin.css css 124 B
/background/skin js 996 B
/base.css css 205 B
/shared.css css 35 B
UI Components (22)
Entry Size
/ui/alert-dialog 2.14 kB
/ui/alert-dialog-close 1.26 kB
/ui/alert-dialog-description 1.53 kB
/ui/alert-dialog-title 1.55 kB
/ui/buffering-indicator 1.81 kB
/ui/captions-button 1.83 kB
/ui/controls 1.87 kB
/ui/fullscreen-button 1.89 kB
/ui/gesture 1.73 kB
/ui/mute-button 1.84 kB
/ui/pip-button 1.85 kB
/ui/play-button 1.84 kB
/ui/playback-rate-button 1.86 kB
/ui/popover 3.16 kB
/ui/poster 1.74 kB
/ui/seek-button 1.85 kB
/ui/slider 2.04 kB
/ui/thumbnail 2.17 kB
/ui/time 1.66 kB
/ui/time-slider 2.09 kB
/ui/tooltip 2.48 kB
/ui/volume-slider 2.25 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react

Path Base PR Diff %
/video/minimal-skin 16.78 kB 17.11 kB +337 B +2.0% 🔺
/ui/gesture 1.87 kB 🆕
/video (minimal) 16.89 kB 17.21 kB +323 B +1.9% 🔺
Presets (7)
Entry Size
/video (default) 17.19 kB
/video (default + hls) 147.91 kB
/video (minimal) 17.21 kB
/video (minimal + hls) 148.05 kB
/audio (default) 14.71 kB
/audio (minimal) 14.78 kB
/background 3.22 kB
Media (3)
Entry Size
/media/background-video 560 B
/media/hls-video 131.61 kB
/media/simple-hls-video 12.36 kB
Skins (14)
Entry Type Size
/video/minimal-skin.css css 2.64 kB
/video/skin.css css 2.68 kB
/video/minimal-skin js 17.11 kB
/video/minimal-skin.tailwind js 19.58 kB
/video/skin js 17.09 kB
/video/skin.tailwind js 19.66 kB
/audio/minimal-skin.css css 2.16 kB
/audio/skin.css css 2.18 kB
/audio/minimal-skin js 14.70 kB
/audio/minimal-skin.tailwind js 16.57 kB
/audio/skin js 14.66 kB
/audio/skin.tailwind js 16.73 kB
/background/skin.css css 90 B
/background/skin js 272 B
UI Components (18)
Entry Size
/ui/alert-dialog 2.75 kB
/ui/buffering-indicator 2.32 kB
/ui/captions-button 2.35 kB
/ui/controls 2.32 kB
/ui/fullscreen-button 2.33 kB
/ui/gesture 1.87 kB
/ui/mute-button 2.33 kB
/ui/pip-button 2.36 kB
/ui/play-button 2.34 kB
/ui/playback-rate-button 2.34 kB
/ui/popover 2.98 kB
/ui/poster 2.15 kB
/ui/seek-button 2.35 kB
/ui/slider 3.20 kB
/ui/time 2.43 kB
/ui/time-slider 2.88 kB
/ui/tooltip 3.30 kB
/ui/volume-slider 2.79 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core

(no changes)

Entries (5)
Entry Size
. 4.97 kB
/dom 8.07 kB
/dom/media/custom-media-element 1.76 kB
/dom/media/hls 131.14 kB
/dom/media/simple-hls 11.85 kB

🏷️ @videojs/element

(no changes)

Entries (2)
Entry Size
. 999 B
/context 936 B

📦 @videojs/store

(no changes)

Entries (3)
Entry Size
. 1.32 kB
/html 700 B
/react 360 B

🔧 @videojs/utils

(no changes)

Entries (10)
Entry Size
/array 104 B
/dom 1003 B
/events 227 B
/function 261 B
/object 119 B
/predicate 265 B
/string 148 B
/style 190 B
/time 478 B
/number 158 B

📦 @videojs/spf

(no changes)

Entries (3)
Entry Size
. 40 B
/dom 10.04 kB
/playback-engine 9.95 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 10, 2026

CI Failure Diagnosis

File Type What failed
packages/core/src/core/ui/gesture/tests/gesture-core.test.ts typecheck Tests reference GestureCore.activate() which no longer exists (TS2339, TS2554).
packages/core/src/core/ui/gesture/tests/gesture-core.test.ts test 5 tests fail with TypeError: core.activate is not a function.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new cross-platform “gesture” abstraction (core + React + HTML) and wires it into the default/minimal video skins to toggle playback on pointer interactions, supported by a new useMediaContainer hook in React for container-level event binding.

Changes:

  • Introduces GestureCore in @videojs/core plus initial unit tests.
  • Adds React Gesture component and HTML media-gesture custom element, and includes them in video skin templates/presets.
  • Extends React player context to track the media container (useMediaContainer, container registration in Container).

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/react/src/ui/gesture/index.ts Adds a barrel export for the React Gesture component.
packages/react/src/ui/gesture/gesture.tsx Implements React headless Gesture component that binds pointer events to the media container.
packages/react/src/testing/mocks.tsx Updates test helpers to include container/setContainer in PlayerContextValue.
packages/react/src/presets/video/skin.tsx Installs default gesture behavior into the default React video skin.
packages/react/src/presets/video/minimal-skin.tsx Installs default gesture behavior into the minimal React video skin.
packages/react/src/player/tests/context.test.tsx Updates context tests for extended PlayerContextValue shape.
packages/react/src/player/create-player.tsx Provider now tracks container state and exposes it via context.
packages/react/src/player/context.tsx Adds useMediaContainer and container registration lifecycle in Container.
packages/react/src/media/tests/video.test.tsx Updates PlayerContextValue usage to include container fields.
packages/react/src/media/tests/audio.test.tsx Updates PlayerContextValue usage to include container fields.
packages/react/src/index.ts Exports useMediaContainer and React Gesture from the package entrypoint.
packages/html/src/ui/gesture/gesture-element.ts Adds media-gesture custom element that binds to the player container and forwards events to GestureCore.
packages/html/src/index.ts Exports GestureElement from the HTML package entrypoint.
packages/html/src/define/video/skin.ts Registers gesture element and adds <media-gesture> to the default video skin template.
packages/html/src/define/video/minimal-skin.ts Registers gesture element and adds <media-gesture> to the minimal video skin template.
packages/html/src/define/ui/gesture.ts Adds safeDefine(GestureElement) module for side-effect registration.
packages/html/src/define/background/skin.ts Adjusts background skin slot markup (removes redundant slot="media" attribute).
packages/core/src/core/ui/gesture/tests/gesture-core.test.ts Adds initial unit tests for GestureCore behavior.
packages/core/src/core/ui/gesture/gesture-core.ts Introduces GestureCore, allowed gesture types/commands, and pointer-type handling.
packages/core/src/core/index.ts Re-exports GestureCore from the core package entrypoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Copy link
Member

@mihar-22 mihar-22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks for doing this! Main suggestion is creating core/dom for the component, moving pointer type there, and sharing event handling logic with html/react.

Comment on lines +9 to +10
type: GestureType;
command: GestureCommand;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(request): missing jsdoc comments, used by API reference tooling for docs.

Comment on lines +16 to +20
export const PointerTypes = {
MOUSE: 'mouse',
PEN: 'pen',
TOUCH: 'touch',
} as const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(request): this is handy but more a DOM specific type. Can we move this to core/ui/types, or as per suggestion below have core/dom layer for gesture and move to core/dom/ui/event

Comment on lines +44 to +45
// TODO: Should `pointerType` be a prop that can be configured?
if (pointerType && pointerType === PointerTypes.MOUSE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I like it and if it makes sense for future gestures then good idea!

(suggestion): if it doesn't make sense as a prop, you could consider moving "pointer type" handling to the DOM layer or framework-layer since it's just a DOM event related guard and not core state management piece.

this.#media = media;
}

handleGesture({ pointerType }: { pointerType: string }): void {
Copy link
Member

@mihar-22 mihar-22 Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(request): stricter type pointerType: PointerType.

Requires creating the type:

type PointerType = PointerTypes[typeof keyof PointerTypes]

Not required if guarding is moved to core/dom

Comment on lines +52 to +69
if (changed.has('type') && ALLOWED_GESTURE_TYPES.includes(this.type)) {
this.#disconnect?.abort();
this.#disconnect = new AbortController();
const { signal } = this.#disconnect;

const container = this.#player.value?.target?.container;
container?.addEventListener(
this.type,
(event: PointerEvent) => {
const target = event.target as Element;
if (target !== container && !target.localName.endsWith('video')) return;

this.#core.handleGesture(event);
},
{ signal }
);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(suggestion): could move this to core/dom to share with html and react. The pointer types could live there too which would make more sense. Claude should be able to do it by referencing other examples.

Comment on lines +16 to +18
else {
if (__DEV__) logMissingFeature('Gesture', 'playback');
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit): could be else if (__DEV__)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants