drop-player
A comprehensive, universal, open-source React media player powering drop.mov.
React media player for video (HLS / progressive), audio, image, and PDF. One unified component — media mode is inferred from the source URL.
Install
npm i drop-player lucide-react
Required peers: react >=18, react-dom >=18, lucide-react >=0.300
Optional peers (install only when needed):
| Package | When |
|---|---|
hls.js >=1.4 |
HLS video (.m3u8) |
pdfjs-dist >=4.0 |
PDF canvas rendering (zoom, pan, page navigation) |
waveform-data >=4.5 |
Audio waveform display |
Quick start
import { Player } from 'drop-player';
import 'drop-player/styles.css';
function App() {
return (
<div style={{ width: 640, height: 360 }}>
<Player sources="https://example.com/video.mp4" />
</div>
);
}
The stylesheet is required — it provides drop-player-prefixed classes and CSS variables.
Components
| Export | Intent |
|---|---|
Player |
Generic — mode inferred from URL |
VideoPlayer |
Video |
AudioPlayer |
Audio |
ImageViewer |
Image |
PdfViewer |
VideoPlayer, AudioPlayer etc. are aliases for readability — they all share the same PlayerProps and PlayerRef, and media mode is always auto-detected from the source URL. Use Player when the media type is mixed or unknown.
Supported media
| Mode | Formats |
|---|---|
| Video | HLS (.m3u8), MP4, WebM |
| Audio | MP3, WAV, Ogg, AAC, FLAC, M4A, WebM, Opus |
| Image | JPEG, PNG, GIF, WebP, AVIF, SVG |
Canvas rendering via pdf.js (zoom, pan, page navigation); falls back to browser-native <object> when pdfjs-dist is not installed |
Browser-only runtime. Safe to import in SSR frameworks (Next.js, Remix, Astro, etc.) — components render nothing on the server and activate on the client.
Sources
// String — type inferred from extension
<VideoPlayer sources="https://example.com/video.mp4" />
// Object — HLS with original-quality fallback
<VideoPlayer sources={{ url: 'stream.m3u8', originalUrl: 'original.mp4' }} />
// Array — source selector shown
<VideoPlayer sources={[
{ url: 'stream.m3u8', label: 'HLS' },
{ url: 'original.mp4', label: 'Original' },
]} />
// Explicit MIME type — useful when URL has no extension (e.g. CDN, signed URL)
<Player sources={{ url: 'https://cdn.example.com/abc123', mimeType: 'video/mp4' }} />
// null — shows error UI
<VideoPlayer sources={null} />
Type is detected from path extension (.mp4, .m3u8, .pdf, .jpg, …) or mimeType.
Props
Props are organised into four groups to keep the surface area manageable:
<VideoPlayer
sources="video.mp4"
className="rounded-lg"
crossOrigin="anonymous"
poster="poster.jpg"
playback={{ autoPlay: true, volume: 0.8 }}
ui={{ features: { saveCapture: true, copyCapture: true }, locale: 'ja' }}
slots={{ topRightOverlay: <Badge /> }}
events={{ onPlay: () => log('playing') }}
/>
Top-level
| Prop | Type | Default | Description |
|---|---|---|---|
sources |
string | MediaSource | MediaSource[] | null |
— | Media source(s) |
className |
string |
— | CSS class on root container |
crossOrigin |
'anonymous' | 'use-credentials' |
'anonymous' |
CORS for media elements |
poster |
string |
— | Poster image (video) |
storageKey |
string |
— | Custom prefix for localStorage keys (default: drop_player_, custom: <storageKey>_). Muted state is persisted automatically. |
storage |
StorageAdapter |
— | Custom storage backend (must implement getItem, setItem, removeItem). Defaults to localStorage (SSR-safe). |
hlsConfig |
Partial<HlsConfig> |
— | Custom hls.js config overrides |
playback — PlayerPlaybackConfig
| Key | Type | Default | Description |
|---|---|---|---|
autoPlay |
boolean |
false |
Start immediately |
loop |
boolean |
false |
Loop playback |
muted |
boolean |
false |
Start muted |
volume |
number |
1 |
Initial volume (0–1) |
initialTime |
number |
0 |
Start position in seconds |
seekStep |
number |
10 |
Seconds to skip with arrow left/right keys |
ui — PlayerUiConfig
| Key | Type | Default | Description |
|---|---|---|---|
showControls |
boolean |
true |
Show bottom control bar |
showTitle |
boolean |
auto | Show source title/selector |
showStatusOverlay |
boolean |
false |
Show status pill overlay on player actions (play, seek, volume, etc.) |
features |
PlayerFeatures |
defaultFeatures |
Toggle individual controls |
locale |
string |
'en' |
Display language (built-in: 'en', 'ja') |
translations |
Partial<Translations> |
— | Custom translation overrides, merged on top of the locale's built-in strings |
frameRate |
number |
auto / 30 |
Frame rate for timecode/frames display. Auto-detected from HLS manifest when available; falls back to 30. |
timeDisplayFormats |
TimeDisplayFormat[] |
defaultTimeDisplayFormats |
Time display formats to cycle through on click |
filmGauge |
number |
16 |
Frames per foot for 'feet-frames' display (e.g. 16 for 35mm, 40 for 16mm) |
bpm |
number |
120 |
BPM for 'bars-beats' display |
timeSignature |
string |
'4/4' |
Time signature for 'bars-beats' display (e.g. '3/4', '6/8') |
markers |
Marker[] |
[] |
Seekbar markers — see Marker types |
Marker types
Marker is a discriminated union. Each type renders a different shape on the seekbar track:
type |
Shape | Extra fields |
|---|---|---|
'circle' (default) |
Filled dot | — |
'line' |
Vertical line | — |
'square' |
Filled square | — |
'custom' |
Your ReactNode | content: ReactNode (required) |
All types share these base fields:
| Field | Type | Default | Description |
|---|---|---|---|
time |
number |
— | Position in seconds |
color |
string |
CSS var | CSS color value |
snap |
boolean |
false |
Enable snap-to on seek |
snapThreshold |
number |
12 |
Snap threshold in px (only when snap: true) |
import type { Marker } from 'drop-player';
const markers: Marker[] = [
{ time: 10, type: 'circle', snap: true },
{ time: 30, type: 'line' },
{ time: 60, type: 'square', color: 'red' },
{
time: 90,
type: 'custom',
content: <div style={{ ... }}>Label</div>,
},
];
<VideoPlayer sources={url} ui={{ markers }} />
slots — PlayerSlots
| Slot | Type | Position |
|---|---|---|
overlay |
ReactNode |
Full-area overlay between media and controls |
controlsStart |
ReactNode |
Left of control bar |
controlsEnd |
ReactNode |
Right of control bar (before fullscreen) |
topLeftOverlay |
ReactNode |
Top-left corner |
topRightOverlay |
ReactNode |
Top-right corner |
loadingIndicator |
ReactNode |
Centre (while loading) |
errorDisplay |
(error: Error) => ReactNode |
Centre (on error) |
Note:
overlayhaspointer-events: noneby default. Addpointer-events: autoto child elements that need to be interactive.
events — PlayerEvents
| Event | Payload | When |
|---|---|---|
onStateChange |
PlayerState |
Any state change (consolidated snapshot) |
onPlay |
— | Playback starts |
onPause |
— | Playback pauses |
onEnded |
— | Playback ends |
onTimeUpdate |
time |
Current time changes |
onDurationChange |
duration |
Duration known/changed |
onVolumeChange |
volume, muted |
Volume or mute changes |
onPlaybackRateChange |
rate |
Playback rate changes |
onError |
Error |
Playback error |
onLoadedMetadata |
VideoMetadata |
Video metadata loaded |
onLoadStart |
— | Loading begins |
onProgress |
TimeRanges |
Buffering progress |
onWaiting |
— | Waiting for data |
onCanPlay |
— | Ready to play |
onPlaying |
— | Actually playing |
onSeekStart |
time |
Seek begins |
onSeeking |
time |
During seek |
onSeekEnd |
time |
Seek ends |
onFullscreenChange |
boolean |
Fullscreen toggled |
onFrameCapture |
FrameCapture |
Frame captured |
onActiveSourceChange |
index |
Active source changed |
onQualityLevelChange |
QualityLevel |
Quality level changed |
onFallback |
FallbackEvent |
Fell back to original URL |
onTimeDisplayFormatChange |
TimeDisplayFormat |
Time display format changed |
Features
ui.features toggles individual controls. Omitted keys inherit from defaultFeatures.
Two presets are exported for convenience:
| Export | Description |
|---|---|
defaultFeatures |
Most controls on, opt-in features off |
noFeatures |
All controls off — use as a base for minimal builds |
import { noFeatures } from 'drop-player';
// Default — most controls on
<VideoPlayer sources={url} />
// Add capture buttons to defaults
<VideoPlayer sources={url} ui={{ features: { saveCapture: true, copyCapture: true } }} />
// Remove loop from defaults
<VideoPlayer sources={url} ui={{ features: { loop: false } }} />
// Build from scratch
<VideoPlayer sources={url} ui={{ features: { ...noFeatures, playButton: true, fullscreen: true } }} />
| Flag | Default | Applies to |
|---|---|---|
playButton |
true |
video, audio |
loop |
true |
video, audio |
timeDisplay |
true |
video, audio |
seekBar |
true |
video, audio |
volume |
true |
video, audio |
saveCapture |
false |
video, image |
copyCapture |
false |
video, image |
qualitySelector |
true |
video (HLS) |
fullscreen |
true |
all |
zoom |
true |
image, PDF (requires pdfjs-dist) |
sourceNavigation |
true |
all (when multiple sources) |
playbackSpeed |
true |
video, audio |
pip |
true |
video (browser PiP API required) |
keyboardShortcuts |
true |
video, audio |
Time display formats
ui.timeDisplayFormats controls which time formats the user can cycle through by clicking the time display. Two presets are exported:
| Export | Formats |
|---|---|
defaultTimeDisplayFormats |
['elapsed-total', 'remaining'] |
allTimeDisplayFormats |
['elapsed-total', 'remaining', 'timecode', 'frames', 'seconds-frames', 'feet-frames', 'bars-beats'] |
import { defaultTimeDisplayFormats, allTimeDisplayFormats } from 'drop-player';
// Default — elapsed / total and remaining
<VideoPlayer sources={url} />
// Add timecode and frames
<VideoPlayer sources={url} ui={{ timeDisplayFormats: allTimeDisplayFormats }} />
// Film workflow — add feet+frames (35mm)
<VideoPlayer sources={url} ui={{
timeDisplayFormats: [...defaultTimeDisplayFormats, 'timecode', 'feet-frames'],
filmGauge: 16,
}} />
// Music — add bars:beats
<AudioPlayer sources={url} ui={{
timeDisplayFormats: [...defaultTimeDisplayFormats, 'bars-beats'],
bpm: 92,
timeSignature: '4/4',
}} />
// Lock to single format (no cycling)
<VideoPlayer sources={url} ui={{ timeDisplayFormats: ['timecode'] }} />
| Format | Display | Extra props |
|---|---|---|
'elapsed-total' |
1:23 / 5:00 |
— |
'remaining' |
-3:37 |
— |
'timecode' |
00:01:23:15 |
frameRate |
'frames' |
2475 / 9000 |
frameRate |
'seconds-frames' |
83+15 / 300+00 |
frameRate |
'feet-frames' |
154+11 / 562+08 |
frameRate, filmGauge |
'bars-beats' |
47:4 / 192:1 |
bpm, timeSignature |
Frame rate is auto-detected from HLS manifests when available. For progressive sources, set ui.frameRate explicitly.
Theming
Override CSS variables on .drop-player:
@import 'drop-player/styles.css';
.drop-player {
--drop-player-blue: oklch(70.7% 0.165 254.624);
--drop-player-yellow: oklch(85.2% 0.199 91.936);
--drop-player-green: oklch(79.2% 0.209 151.711);
--drop-player-red: oklch(70.4% 0.191 22.216);
--drop-player-muted: oklch(55.2% 0.016 285.938);
--drop-player-marker-circle: var(--drop-player-yellow);
--drop-player-border-radius: 8px;
}
| Variable | Default | Description |
|---|---|---|
--drop-player-blue |
oklch(70.7% 0.165 254.624) |
Accent color (active states, focus ring) |
--drop-player-yellow |
oklch(85.2% 0.199 91.936) |
Highlight color |
--drop-player-green |
oklch(79.2% 0.209 151.711) |
Positive color (check marks, best quality) |
--drop-player-red |
oklch(70.4% 0.191 22.216) |
Negative color (errors) |
--drop-player-muted |
oklch(55.2% 0.016 285.938) |
Muted / inactive color |
--drop-player-marker-circle |
var(--drop-player-yellow) |
Marker color (circle, line, square) |
--drop-player-border-radius |
0 |
Container border radius |
--drop-player-aspect-ratio |
varies | Aspect ratio (16/9 video, 32/9 audio, 4/3 image, 1/1.414 PDF) |
Ref API
Access imperative methods via PlayerRef:
const ref = useRef<PlayerRef>(null);
<VideoPlayer ref={ref} sources={url} />
// Later:
ref.current?.play();
ref.current?.seek(30);
| Method | Returns | Notes |
|---|---|---|
play() |
Promise<void> |
|
pause() |
void |
|
toggle() |
void |
|
seek(time) |
void |
|
seekRelative(delta) |
void |
|
seekToFrame(frame) |
void |
Video only |
getCurrentTime() |
number |
|
getDuration() |
number |
|
getVolume() |
number |
|
isMuted() |
boolean |
|
isPaused() |
boolean |
|
isFullscreen() |
boolean |
|
getPlaybackRate() |
number |
|
setVolume(n) |
void |
|
setMuted(bool) |
void |
|
setPlaybackRate(rate) |
void |
|
getTimeDisplayFormat() |
TimeDisplayFormat |
|
setTimeDisplayFormat(format) |
void |
|
captureFrame(opts?) |
Promise<FrameCapture> |
Video/image; throws for audio/PDF |
requestFullscreen() |
Promise<void> |
|
exitFullscreen() |
Promise<void> |
|
toggleFullscreen() |
void |
|
getVideoElement() |
HTMLVideoElement | null |
|
getContainerElement() |
HTMLDivElement | null |
Error handling
events.onError and slots.errorDisplay receive an Error with a typed name:
error.name |
Meaning |
|---|---|
errorNoSources |
sources is null or empty |
errorAborted |
Playback aborted |
errorNetwork |
Network error |
errorDecode |
Decode failure |
errorNotSupported |
Format not supported |
errorUnknown |
Unknown error |
Built-in UI uses ui.locale to localise these. Override with slots.errorDisplay for custom rendering.
Translations
Built-in locales: 'en' and 'ja'. Set via ui.locale.
To add a new locale or override specific strings, use ui.translations:
<VideoPlayer
sources="video.mp4"
ui={{
locale: 'en',
translations: {
play: 'Reproducir',
pause: 'Pausar',
mute: 'Silenciar',
unmute: 'Activar sonido',
fullscreen: 'Pantalla completa',
exitFullscreen: 'Salir de pantalla completa',
},
}}
/>
Translations are merged on top of the base locale — you only need to provide the keys you want to change.
Custom storage
Player preferences (muted state) are persisted to localStorage by default. Use storageKey to namespace keys or storage to replace the backend entirely.
// Custom namespace
<VideoPlayer sources="video.mp4" storageKey="my_app" />
// Keys stored as: my_app_muted, my_app_volume, etc.
// Custom backend (e.g. Supabase)
const supabaseStorage = {
getItem: (key: string) => {
// Read from Supabase cache or return null
return localStorage.getItem(key);
},
setItem: (key: string, value: string) => {
localStorage.setItem(key, value);
supabase.from('preferences').upsert({ key, value });
},
removeItem: (key: string) => {
localStorage.removeItem(key);
supabase.from('preferences').delete().eq('key', key);
},
};
<VideoPlayer sources="video.mp4" storage={supabaseStorage} />
The StorageAdapter interface requires three methods: getItem, setItem, and removeItem. Operations should be synchronous (return values immediately); use a local cache with async sync for remote backends.
Utilities
Helper functions exported for use outside the player (custom UI, playlists, timecode overlays, etc.):
// From the main entry (includes React)
import {
formatTime, formatTimecode, secondsToFrames, parseFrameRate,
formatFeetFrames, formatSecondsFrames, formatBarsBeats,
} from 'drop-player';
// From the lightweight sub-path (no React dependency — safe for Server Components)
import {
formatTime, formatTimecode, secondsToFrames, parseFrameRate,
formatFeetFrames, formatSecondsFrames, formatBarsBeats,
} from 'drop-player/utils';
formatTime(125); // "02:05"
formatTime(3661); // "01:01:01"
formatTimecode(125.5, 30); // "00:02:05:15"
formatTimecode(125.5, '30000/1001');// "00:02:05:14" (29.97fps)
secondsToFrames(10, 24); // 240
parseFrameRate('30000/1001'); // 29.97...
formatSecondsFrames(83.5, 30); // "83+15"
formatFeetFrames(10, 24, 16); // "15+00"
formatBarsBeats(10, 120, '4/4'); // "5:1"
| Function | Signature | Description |
|---|---|---|
formatTime |
(seconds?) => string |
Format as MM:SS or HH:MM:SS |
formatTimecode |
(seconds?, frameRate?) => string |
Format as SMPTE timecode HH:MM:SS:FF |
secondsToFrames |
(seconds?, frameRate?) => number |
Convert seconds to frame number |
parseFrameRate |
(frameRate?) => number |
Parse frame rate string (e.g. "30000/1001") to number |
formatSecondsFrames |
(seconds?, frameRate?) => string |
Format as seconds+frames S+FF |
formatFeetFrames |
(seconds?, frameRate?, filmGauge?) => string |
Format as feet+frames FFF+FF |
formatBarsBeats |
(seconds?, bpm?, timeSignature?) => string |
Format as bars:beats B:b |
License
MIT
Changelog
1.5.5
Features
- Add
drop-player/utilssub-path export — pure formatter functions (formatTime,formatTimecode,secondsToFrames,parseFrameRate,formatSecondsFrames,formatFeetFrames,formatBarsBeats) importable without React, safe for Server Components
1.5.4
Features
- Add
overlayslot — full-area overlay layer between media and controls (pointer-events: noneby default)
1.5.2
Bug Fixes
- Fix SSR safety: guard
ResizeObserverusage inuseElementWidthshook withtypeofcheck to preventReferenceErrorin server-side rendering environments
1.5.0
Breaking Changes
- Remove ambient light feature —
ambientLighthas been removed fromPlayerFeatures,VideoState,VideoCoreRef, and all related UI/CSS. If you were usingfeatures={{ ambientLight: true }}, remove it.
1.4.0
Features
- Add responsive controls bar — controls dynamically collapse into an overflow menu based on available width
- Add overflow menu (⋯) that groups secondary controls (ambient light, PiP, playback speed, capture) when space is limited
- Split capture button into separate
CopyCaptureButtonandSaveCaptureButtoncontrols - Add
useFeedbackhook for transient action feedback (e.g. "Copied!") - Add
useMediaQueryanduseElementWidthshooks for responsive layout
Bug Fixes
- Fix marker positions not rendering correctly on the seekbar
- Fix SEO: add prerender script for demo site, update
llms.txt/llms-ja.txt
1.3.0
Breaking Changes
- Marker API redesigned —
Markeris now a discriminated union. Replacetype: 'scene'withtype: 'circle'. The oldtype: 'custom'(no-op) now requirescontent: ReactNode. slots.seekbarOverlayremoved — usetype: 'custom'markers instead.- CSS variable
--drop-player-marker-scenerenamed to--drop-player-marker-circle.--drop-player-marker-customremoved. - Snap is now opt-in per marker — add
snap: trueto each marker that should snap. Previously snap was always active when markers were present.
Features
- New marker types:
'circle'(renamed from'scene'),'line','square','custom' snap?: boolean— enable snap-to per marker (default:false)snapThreshold?: number— per-marker snap distance in px (default:12, only used whensnap: true)content: ReactNode— render arbitrary content on the seekbar track at a specific time (type: 'custom')- Named marker types (
CircleMarker,LineMarker,SquareMarker,CustomMarker) exported from package root
1.2.5
- Revert 1.2.4
1.2.4 (deprecated)
Fix Webpack/Turbopack build error (— reverted,Module not found) when optional peer dependencies are not installedwebpackIgnoreprevents bundlers from resolving installed packages at runtime
1.2.3
Bug Fixes
- Fix controls overlay not recoverable on mobile for Image, PDF, and Audio modes — tapping the content area now toggles controls visibility, matching Video behavior
- Fix player blocking page scroll on mobile — allow vertical scrolling (
pan-y) for all media types; only block scroll when Image/PDF is zoomed in
1.2.2
Bug Fixes
- Fix controls UI not showing on tap for Image, PDF, and Audio modes
1.2.1
Bug Fixes
- Fix PDF cleanup on unmount (add
destroyWorkercall) - Improve
StatusOverlayaccessibility and translation handling - Fix re-exported types from package root
1.2.0
Features
- Add pdf.js integration as optional peer dependency for canvas-based PDF rendering with real zoom, pan, and page navigation
- Add
PageNavigationcontrol (prev/next page withChevronUp/ChevronDownicons) - Add
PdfCoreRefimperative handle (zoomIn,zoomOut,resetZoom,nextPage,prevPage,goToPage) - Add keyboard shortcuts for PDF mode (
ArrowUp/ArrowDownfor pages,+/-/0for zoom) - Add swipe gesture for PDF page navigation (disabled when zoomed)
- Fallback to browser-native
<object>rendering whenpdfjs-distis not installed
Improvements
- PDF pages fit container at 100% zoom (no excess margins)
- Zoom re-renders via pdf.js for crisp vector text (debounced 150ms with immediate CSS feedback)
- Page change resets zoom to 1x
- Controls and title auto-hide after 3 seconds for image and PDF modes (consistent with video/audio)
- Source change resets readiness state so loading overlay covers stale content
- Add
tabular-numsto zoom level display - Controls overlay now has proper z-index to remain visible during loading
1.1.7
Features
- Add
seekStepplayback option to configure arrow key skip duration in seconds (default: 10) - Add
seekStepButtonsfeature flag for skip forward/backward buttons - Add
showStatusOverlayUI option — pill-style overlay showing player actions (play/pause, seek, volume, speed, quality, zoom, loop)
1.1.6
Features
- Add multi-source navigation (prev/next buttons, swipe gesture, keyboard navigation)
- Add
sourceNavigationfeature flag to show/hide prev/next buttons - Add time display formats:
seconds-frames,feet-frames,bars-beats - Auto-detect frame rate from HLS level metadata
- Add CSS color variables for easier theme customization
Improvements
- Expand touch/click target for seekbar and volume slider thumbs without changing visual appearance
- Style dropdown scrollbar to match player theme
1.1.5
Improvements
- Add drag-to-seek on audio waveform (click to seek, drag to scrub)
- Add accessibility attributes on audio waveform area (
role="slider",aria-valuemin/max/now) - Improve PDF error handling: pre-validate URL reachability, fire
onErrorwitherrorNetworkon failure - Suppress
AbortErrorfrom play/pause races
Bug Fixes
- Fix audio player not showing error state when loading fails
Documentation
- Add CSS variables reference table, feature presets, Translations, Custom storage, Utilities sections to README
1.1.4
Improvements
VideoPlayer,AudioPlayer,ImageViewer,PdfViewernow set media mode directly, skipping source type inference- Add default
aspect-ratioper media mode (video: 16/9, audio: 32/9, image: 4/3, pdf: 1/1.414), overridable via--drop-player-aspect-ratio - Warn when URL has no recognizable extension and
mimeTypeis not provided
Bug Fixes
- Fix
min-width: 100%redundancy on player container
1.1.3
Bug Fixes
- Fix hydration mismatch caused by PiP support detection running during SSR
1.1.2
Bug Fixes
- Unify mouse/touch handling with Pointer Events for consistent drag-to-seek and controls toggle
- Improve HLS fatal error handling: network errors now trigger fallback, media error recovery limited to one attempt
- Type
hlsConfigasPartial<HlsConfig>instead ofRecord<string, unknown> - Add
network-errortoFallbackReasontype - Rename legacy
@dropmov/playerreferences todrop-playerin console warnings
Styles
- Responsive timestamp position in controls bar
1.1.1
Features
- Add
storageprop for customStorageAdapter(replaces default localStorage) - Add
translationsprop for custom i18n string overrides - Widen
localetype from'en' | 'ja'tostringfor custom locales - Stabilize React hooks to avoid unnecessary re-renders with object props
Styles
- Fix ambient light mode rendering
- Reposition timestamp display
1.1.0
Features
- Add
storageKeyprop to customize localStorage key prefix (default:drop_player_, custom:<storageKey>_) - Persist and restore muted state across sessions via localStorage
Breaking Changes
- Remove
persistenceKeyprop and playback position persistence feature - Remove
onPositionSave/onPositionRestoreevent callbacks
1.0.7
Bug Fixes
- Fix tooltip not dismissing on Safari and touch devices
- Fix multiple tooltips displaying simultaneously
- Auto-dismiss tooltips after timeout on touch interactions
- Fix muted state behavior
1.0.6
Features
- Add playback speed selector control
- Add Picture-in-Picture (PiP) button control
Bug Fixes
- Fix tooltip position not recalculating when content changes
1.0.5
Features
- Support
hlsConfigprop for custom hls.js configuration - Tune ABR settings for smoother adaptive bitrate switching
Bug Fixes
- Dynamically import
waveform-datato reduce initial bundle size - Fix tooltip position
1.0.4
Maintenance
- Disable sourcemap output and enable minification in build
- Remove unnecessary documentation files
1.0.3
Breaking Changes
- Remove Tailwind CSS dependency from library output; all styles are now plain CSS with
drop-player-*BEM classes - Consumers no longer need Tailwind — just import
drop-player/styles.css
Bug Fixes
- Fix controls and seekbar not rendering (Tailwind utility classes missing from build output)
1.0.2
Bug Fixes
- Fix player and controls not spanning full width in flex/aspect-ratio layouts
1.0.1
Maintenance
- Update dependency packages
1.0.0
Initial Release
- Video and audio player component with custom controls
- Keyboard shortcuts support
- Marker/chapter support (scene markers, custom markers)
- Playback speed control
- Volume control with mute toggle
- Fullscreen support
- Ambient light effect
- Multiple source selector
- Customizable theming via CSS variables
- Error handling with retry