Video Player
A player with a scrubber, quality and subtitle menus. Controls are real buttons on a real timeline, so the keyboard reaches every one of them — a div-based scrubber never does.
src/components/ui/video-player.tsxAbout
From the component's own source, where it was written.
A video player with a real scrubber, quality and subtitle menus.
The scrubber is the kit's Slider — a range input underneath, not a div with a drag handler. That one decision buys arrow-key seeking, Home and End, screen-reader announcement of the position and a working focus ring, all of which a hand-rolled track has to reimplement and usually does not. It was a bare range input until recently, which meant the OS accent colour showed through on the one black bar in the product; the colour variables below point the shared component at the chrome's own ink instead.
Time is formatted from the media's own duration, so an hour-long video shows 1:02:15 and a clip shows 0:42 rather than 00:00:42. Padding every clip to hours is the giveaway of a player that formats blindly.
Subtitles default to off but the menu is always present. A player that hides the menu when only one track exists makes people believe there are none.
Install
npx astralyx-ui add video-player
Usage
import { VideoPlayer } from '@/components/ui/video-player'
<VideoPlayer src={src} poster={poster} qualities={qualities} subtitles={tracks} />Composer
Change the props and the source below follows.
<VideoPlayer src={src} qualities={qualities} subtitles={tracks} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
VideoPlayer
Extends Omit<ComponentProps<'div'>, 'title'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | required | Passed to the underlying <video>. The poster is what fills the frame before play, instead of a black rectangle. |
| poster | string | — | Passed to the underlying <video>. The poster is what fills the frame before play, instead of a black rectangle. |
| qualities | VideoTrack[] | — | Controlled: switching a rendition means changing the source, which is yours to do. Omit to hide the menu. |
| quality | string | — | Controlled: switching a rendition means changing the source, which is yours to do. Omit to hide the menu. |
| onQualityChange | (id: string) => void | — | Controlled: switching a rendition means changing the source, which is yours to do. Omit to hide the menu. |
| subtitles | VideoTrack[] | — | null is "off", which is a real selection and appears in the menu as one. |
| subtitle | string | null | — | null is "off", which is a real selection and appears in the menu as one. |
| onSubtitleChange | (id: string | null) => void | — | — |
| title | ReactNode | — | — |
| playLabel | string | 'Play' | — |
| pauseLabel | string | 'Pause' | — |
| muteLabel | string | 'Mute' | — |
| unmuteLabel | string | 'Unmute' | — |
| seekLabel | string | 'Seek' | Accessible name for the scrubber. |
| settingsLabel | string | 'Playback settings' | — |
| fullscreenLabel | string | 'Full screen' | — |
| qualityLabel | ReactNode | 'Quality' | Heading over the quality menu. |
| subtitlesLabel | ReactNode | 'Subtitles' | Heading over the subtitle menu. |
| subtitlesOffLabel | ReactNode | 'Off' | The "no subtitles" choice, which is a real selection. |
| progressLabel | (at: string, total: string) => string | DEFAULT_PROGRESS_LABEL | Spoken position, given the formatted current time and duration. |
| display | hours when needed | — | A 40-minute video shows 12:04, a 2-hour one shows 1:12:04. Padding everything to hours wastes the width. |
Functions
| Function | Signature | Description |
|---|---|---|
| formatTime | (seconds: number, showHours: boolean) => string | — |
Types
VideoTrack
A video player with a real scrubber, quality and subtitle menus.
| Field | Type | Description |
|---|---|---|
| id | string | — |
| label | ReactNode | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| keyboard | space, arrows, m, f | Play/pause, seek, mute, fullscreen. The scrubber is a slider, so arrow keys seek from it too. |