mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
co-authored by
Claude
parent
f514051263
commit
4448a5b252
@@ -0,0 +1,307 @@
|
||||
# @vjs-10/html-icons
|
||||
|
||||
> Web Component icon elements for Video.js
|
||||
|
||||
[](https://www.npmjs.com/package/@vjs-10/html-icons)
|
||||
|
||||
**Status:** Early Development
|
||||
|
||||
> **⚠️ PROTOTYPE - SUBJECT TO CHANGE**
|
||||
>
|
||||
> This package is in early prototype phase. Expect significant changes including:
|
||||
>
|
||||
> - Package restructuring and naming
|
||||
> - Breaking API changes
|
||||
> - Major architectural updates
|
||||
> - Incomplete or experimental features
|
||||
>
|
||||
> Not recommended for production use.
|
||||
|
||||
## Overview
|
||||
|
||||
`@vjs-10/html-icons` provides Web Component implementations of Video.js icons. These are DOM-ready icon elements that can be used directly in HTML or with any framework that supports custom elements.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Web Components** - Custom element icon implementations
|
||||
- **Framework Agnostic** - Works with vanilla JS, React, Vue, Angular, etc.
|
||||
- **Accessible** - Built-in ARIA attributes and semantic markup
|
||||
- **Customizable** - Style with CSS custom properties
|
||||
- **Lightweight** - Minimal runtime overhead
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @vjs-10/html-icons
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Import and Use
|
||||
|
||||
```typescript
|
||||
import '@vjs-10/html-icons';
|
||||
|
||||
// Or import specific icons
|
||||
import '@vjs-10/html-icons/play';
|
||||
import '@vjs-10/html-icons/pause';
|
||||
```
|
||||
|
||||
### In HTML
|
||||
|
||||
```html
|
||||
<!-- After importing, use custom elements -->
|
||||
<vjs-icon-play></vjs-icon-play>
|
||||
<vjs-icon-pause></vjs-icon-pause>
|
||||
<vjs-icon-volume-high></vjs-icon-volume-high>
|
||||
<vjs-icon-volume-low></vjs-icon-volume-low>
|
||||
<vjs-icon-volume-off></vjs-icon-volume-off>
|
||||
<vjs-icon-fullscreen-enter></vjs-icon-fullscreen-enter>
|
||||
<vjs-icon-fullscreen-exit></vjs-icon-fullscreen-exit>
|
||||
```
|
||||
|
||||
### With JavaScript
|
||||
|
||||
```typescript
|
||||
import { PauseIcon, PlayIcon, VolumeHighIcon } from '@vjs-10/html-icons';
|
||||
|
||||
// Create icon element
|
||||
const playIcon = new PlayIcon();
|
||||
document.body.appendChild(playIcon);
|
||||
|
||||
// Or use createElement
|
||||
const pauseIcon = document.createElement('vjs-icon-pause');
|
||||
document.body.appendChild(pauseIcon);
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
### CSS Custom Properties
|
||||
|
||||
```css
|
||||
vjs-icon-play {
|
||||
/* Size */
|
||||
--icon-size: 24px;
|
||||
|
||||
/* Color */
|
||||
--icon-color: #fff;
|
||||
|
||||
/* Opacity */
|
||||
--icon-opacity: 1;
|
||||
|
||||
/* Additional styles */
|
||||
width: var(--icon-size);
|
||||
height: var(--icon-size);
|
||||
color: var(--icon-color);
|
||||
}
|
||||
```
|
||||
|
||||
### Standard CSS
|
||||
|
||||
```css
|
||||
vjs-icon-play {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: blue;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
vjs-icon-play:hover {
|
||||
color: lightblue;
|
||||
}
|
||||
```
|
||||
|
||||
## Available Icons
|
||||
|
||||
### Playback Controls
|
||||
|
||||
- `<vjs-icon-play>` - Play button
|
||||
- `<vjs-icon-pause>` - Pause button
|
||||
- `<vjs-icon-spinner>` - Loading spinner
|
||||
|
||||
### Volume Controls
|
||||
|
||||
- `<vjs-icon-volume-high>` - High volume
|
||||
- `<vjs-icon-volume-low>` - Low volume
|
||||
- `<vjs-icon-volume-off>` - Muted
|
||||
|
||||
### Screen Controls
|
||||
|
||||
- `<vjs-icon-fullscreen-enter>` - Enter fullscreen
|
||||
- `<vjs-icon-fullscreen-exit>` - Exit fullscreen
|
||||
- `<vjs-icon-fullscreen-enter-alt>` - Alternative fullscreen enter
|
||||
- `<vjs-icon-fullscreen-exit-alt>` - Alternative fullscreen exit
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Button with Icon
|
||||
|
||||
```html
|
||||
<button class="play-button">
|
||||
<vjs-icon-play></vjs-icon-play>
|
||||
<span>Play Video</span>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.play-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
vjs-icon-play {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Toggle Icon State
|
||||
|
||||
```typescript
|
||||
import { PauseIcon, PlayIcon } from '@vjs-10/html-icons';
|
||||
|
||||
const button = document.querySelector('.play-pause-button');
|
||||
let isPlaying = false;
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
isPlaying = !isPlaying;
|
||||
|
||||
// Replace icon
|
||||
const oldIcon = button.querySelector('vjs-icon-play, vjs-icon-pause');
|
||||
const newIcon = isPlaying
|
||||
? new PauseIcon()
|
||||
: new PlayIcon();
|
||||
|
||||
oldIcon?.replaceWith(newIcon);
|
||||
});
|
||||
```
|
||||
|
||||
### With Framework (React Example)
|
||||
|
||||
```jsx
|
||||
// React automatically supports Web Components
|
||||
function PlayButton() {
|
||||
return (
|
||||
<button>
|
||||
<vjs-icon-play></vjs-icon-play>
|
||||
Play
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Dynamic Icon Selection
|
||||
|
||||
```typescript
|
||||
const iconMap = {
|
||||
play: 'vjs-icon-play',
|
||||
pause: 'vjs-icon-pause',
|
||||
'volume-high': 'vjs-icon-volume-high',
|
||||
'volume-off': 'vjs-icon-volume-off',
|
||||
};
|
||||
|
||||
function createIcon(name: keyof typeof iconMap) {
|
||||
return document.createElement(iconMap[name]);
|
||||
}
|
||||
|
||||
const playIcon = createIcon('play');
|
||||
const volumeIcon = createIcon('volume-high');
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
All icon components include:
|
||||
|
||||
- **ARIA labels** - Screen reader friendly descriptions
|
||||
- **Role attributes** - Semantic roles for assistive tech
|
||||
- **Focusable** - Keyboard navigation support
|
||||
|
||||
```html
|
||||
<!-- Automatically includes accessibility attributes -->
|
||||
<vjs-icon-play role="img" aria-label="Play"> </vjs-icon-play>
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
@vjs-10/icons (SVG source)
|
||||
↓
|
||||
@vjs-10/html-icons (Web Components)
|
||||
↓
|
||||
@vjs-10/html (Complete player UI)
|
||||
```
|
||||
|
||||
This package:
|
||||
|
||||
- Transforms SVG sources into Web Components
|
||||
- Registers custom elements in the browser
|
||||
- Provides a DOM-native icon system
|
||||
|
||||
## API Reference
|
||||
|
||||
### Icon Element Interface
|
||||
|
||||
```typescript
|
||||
interface IconElement extends HTMLElement {
|
||||
// Standard HTML element properties
|
||||
className: string;
|
||||
style: CSSStyleDeclaration;
|
||||
|
||||
// Custom properties
|
||||
size?: number;
|
||||
color?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
```typescript
|
||||
// All standard HTMLElement methods available
|
||||
icon.setAttribute('aria-label', 'Custom label');
|
||||
icon.classList.add('custom-class');
|
||||
icon.style.width = '32px';
|
||||
```
|
||||
|
||||
## Package Dependencies
|
||||
|
||||
- **Dependencies:** `@vjs-10/icons` (SVG source assets)
|
||||
- **Used by:** `@vjs-10/html` (HTML player UI)
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Build the package
|
||||
pnpm build
|
||||
|
||||
# Watch mode for development
|
||||
pnpm dev
|
||||
|
||||
# Run tests
|
||||
pnpm test
|
||||
|
||||
# Clean build artifacts
|
||||
pnpm clean
|
||||
```
|
||||
|
||||
## Browser Support
|
||||
|
||||
Web Components require:
|
||||
|
||||
- Chrome 54+
|
||||
- Firefox 63+
|
||||
- Safari 10.1+
|
||||
- Edge 79+
|
||||
|
||||
For older browsers, use a Web Components polyfill.
|
||||
|
||||
## Related Packages
|
||||
|
||||
- **[@vjs-10/icons](../../core/icons)** - Source SVG assets
|
||||
- **[@vjs-10/react-icons](../../react/react-icons)** - React icon components
|
||||
- **[@vjs-10/html](../html)** - Complete HTML player
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,330 @@
|
||||
# @vjs-10/html-media-elements
|
||||
|
||||
> Web Component media elements and utilities
|
||||
|
||||
[](https://www.npmjs.com/package/@vjs-10/html-media-elements)
|
||||
|
||||
**Status:** Early Development
|
||||
|
||||
> **⚠️ PROTOTYPE - SUBJECT TO CHANGE**
|
||||
>
|
||||
> This package is in early prototype phase. Expect significant changes including:
|
||||
>
|
||||
> - Package restructuring and naming
|
||||
> - Breaking API changes
|
||||
> - Major architectural updates
|
||||
> - Incomplete or experimental features
|
||||
>
|
||||
> Not recommended for production use.
|
||||
|
||||
## Overview
|
||||
|
||||
`@vjs-10/html-media-elements` provides Web Component implementations of media elements, offering enhanced functionality beyond native `<video>` and `<audio>` elements. These components integrate with the Video.js state management system and support advanced streaming protocols.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Web Components** - Standard custom elements for media
|
||||
- **Enhanced Media Elements** - Extended functionality beyond native elements
|
||||
- **State Integration** - Built-in media store connectivity via Context Protocol
|
||||
- **Streaming Support** - HLS, DASH support via playback engines
|
||||
- **Framework Agnostic** - Works with vanilla JS or any framework
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @vjs-10/html-media-elements
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Video Element
|
||||
|
||||
```typescript
|
||||
import '@vjs-10/html-media-elements';
|
||||
|
||||
// Use in HTML
|
||||
const html = `
|
||||
<vjs-video src="video.mp4" controls>
|
||||
Your browser doesn't support video.
|
||||
</vjs-video>
|
||||
`;
|
||||
```
|
||||
|
||||
### With JavaScript
|
||||
|
||||
```typescript
|
||||
import { VideoElement } from '@vjs-10/html-media-elements';
|
||||
|
||||
// Create video element
|
||||
const video = new VideoElement();
|
||||
video.src = 'https://example.com/video.mp4';
|
||||
video.controls = true;
|
||||
document.body.appendChild(video);
|
||||
|
||||
// Listen to events
|
||||
video.addEventListener('play', () => {
|
||||
console.log('Video started playing');
|
||||
});
|
||||
```
|
||||
|
||||
### HLS Streaming
|
||||
|
||||
```html
|
||||
<!-- HLS source automatically uses playback engine -->
|
||||
<vjs-video src="https://example.com/stream.m3u8" controls> </vjs-video>
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### VideoElement (`<vjs-video>`)
|
||||
|
||||
Enhanced video element with streaming support:
|
||||
|
||||
```html
|
||||
<vjs-video src="video.mp4" poster="poster.jpg" controls autoplay muted loop preload="metadata"> </vjs-video>
|
||||
```
|
||||
|
||||
**Attributes:**
|
||||
|
||||
- All standard `<video>` attributes
|
||||
- `src` - Media source (supports HLS .m3u8 files)
|
||||
- `poster` - Poster image URL
|
||||
- `controls` - Show native controls
|
||||
- `autoplay` - Auto-play on load
|
||||
- `muted` - Start muted
|
||||
- `loop` - Loop playback
|
||||
- `preload` - Preload strategy
|
||||
|
||||
### AudioElement (`<vjs-audio>`)
|
||||
|
||||
Enhanced audio element:
|
||||
|
||||
```html
|
||||
<vjs-audio src="audio.mp3" controls preload="auto"> </vjs-audio>
|
||||
```
|
||||
|
||||
## State Integration
|
||||
|
||||
Components automatically integrate with `@vjs-10/media-store`:
|
||||
|
||||
```typescript
|
||||
import { VideoElement } from '@vjs-10/html-media-elements';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
// Create media store
|
||||
const store = createMediaStore();
|
||||
|
||||
// Create video element
|
||||
const video = new VideoElement();
|
||||
video.store = store; // Connect to store
|
||||
|
||||
// Store automatically updates with media state
|
||||
store.currentTime.subscribe((time) => {
|
||||
console.log('Current time:', time);
|
||||
});
|
||||
|
||||
store.paused.subscribe((paused) => {
|
||||
console.log('Is paused:', paused);
|
||||
});
|
||||
```
|
||||
|
||||
## Context Protocol Integration
|
||||
|
||||
Uses [@open-wc/context-protocol](https://www.npmjs.com/package/@open-wc/context-protocol) for state sharing:
|
||||
|
||||
```html
|
||||
<!-- Provider shares store with descendants -->
|
||||
<vjs-media-provider>
|
||||
<vjs-video src="video.mp4"></vjs-video>
|
||||
<!-- Other components can access the same store -->
|
||||
<vjs-play-button></vjs-play-button>
|
||||
<vjs-time-slider></vjs-time-slider>
|
||||
</vjs-media-provider>
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Playback Engine Configuration
|
||||
|
||||
```typescript
|
||||
import { VideoElement } from '@vjs-10/html-media-elements';
|
||||
import { HlsJSPlaybackEngine } from '@vjs-10/playback-engine';
|
||||
|
||||
const video = new VideoElement();
|
||||
|
||||
// Configure HLS engine
|
||||
video.engineConfig = {
|
||||
debug: false,
|
||||
enableWorker: true,
|
||||
lowLatencyMode: true,
|
||||
};
|
||||
|
||||
video.src = 'stream.m3u8';
|
||||
```
|
||||
|
||||
### Source Switching
|
||||
|
||||
```typescript
|
||||
const video = new VideoElement();
|
||||
video.src = 'video1.mp4';
|
||||
|
||||
// Switch to different source
|
||||
setTimeout(() => {
|
||||
video.src = 'video2.mp4';
|
||||
video.load(); // Reload with new source
|
||||
}, 5000);
|
||||
|
||||
// Switch to HLS stream
|
||||
setTimeout(() => {
|
||||
video.src = 'stream.m3u8'; // Automatically uses HLS engine
|
||||
}, 10000);
|
||||
```
|
||||
|
||||
### Event Handling
|
||||
|
||||
```typescript
|
||||
const video = new VideoElement();
|
||||
|
||||
// Standard media events
|
||||
video.addEventListener('loadedmetadata', () => {
|
||||
console.log('Duration:', video.duration);
|
||||
});
|
||||
|
||||
video.addEventListener('timeupdate', () => {
|
||||
console.log('Current time:', video.currentTime);
|
||||
});
|
||||
|
||||
video.addEventListener('ended', () => {
|
||||
console.log('Playback ended');
|
||||
});
|
||||
|
||||
// Error handling
|
||||
video.addEventListener('error', (e) => {
|
||||
console.error('Media error:', e);
|
||||
});
|
||||
```
|
||||
|
||||
### Programmatic Control
|
||||
|
||||
```typescript
|
||||
const video = new VideoElement();
|
||||
video.src = 'video.mp4';
|
||||
|
||||
// Playback control
|
||||
await video.play();
|
||||
video.pause();
|
||||
|
||||
// Seeking
|
||||
video.currentTime = 30; // Seek to 30 seconds
|
||||
|
||||
// Volume
|
||||
video.volume = 0.5; // 50%
|
||||
video.muted = true;
|
||||
|
||||
// Playback rate
|
||||
video.playbackRate = 1.5; // 1.5x speed
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Properties
|
||||
|
||||
```typescript
|
||||
interface VideoElement extends HTMLElement {
|
||||
// Source
|
||||
src: string;
|
||||
currentSrc: string;
|
||||
|
||||
// Playback state
|
||||
paused: boolean;
|
||||
ended: boolean;
|
||||
seeking: boolean;
|
||||
|
||||
// Time
|
||||
currentTime: number;
|
||||
duration: number;
|
||||
|
||||
// Volume
|
||||
volume: number;
|
||||
muted: boolean;
|
||||
|
||||
// Playback
|
||||
playbackRate: number;
|
||||
autoplay: boolean;
|
||||
loop: boolean;
|
||||
controls: boolean;
|
||||
|
||||
// Loading
|
||||
preload: 'none' | 'metadata' | 'auto';
|
||||
readyState: number;
|
||||
|
||||
// Media store integration
|
||||
store?: MediaStore;
|
||||
engineConfig?: EngineConfig;
|
||||
}
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
```typescript
|
||||
interface VideoElementMethods {
|
||||
// Playback
|
||||
play(): Promise<void>
|
||||
pause(): void
|
||||
load(): void
|
||||
|
||||
// Seeking
|
||||
fastSeek(time: number): void
|
||||
|
||||
// Fullscreen
|
||||
requestFullscreen(): Promise<void>
|
||||
exitFullscreen(): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
Web Components support:
|
||||
|
||||
- Chrome 54+
|
||||
- Firefox 63+
|
||||
- Safari 10.1+
|
||||
- Edge 79+
|
||||
|
||||
HLS streaming support:
|
||||
|
||||
- All modern browsers via HLS.js
|
||||
|
||||
## Package Dependencies
|
||||
|
||||
- **Dependencies:**
|
||||
- `@vjs-10/media-store` - State management
|
||||
- `@open-wc/context-protocol` - State sharing
|
||||
- **Used by:** `@vjs-10/html` - Complete HTML player
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Build the package
|
||||
pnpm build
|
||||
|
||||
# Watch mode for development
|
||||
pnpm dev
|
||||
|
||||
# Run tests
|
||||
pnpm test
|
||||
|
||||
# Clean build artifacts
|
||||
pnpm clean
|
||||
```
|
||||
|
||||
## Related Packages
|
||||
|
||||
- **[@vjs-10/media-store](../../core/media-store)** - State management
|
||||
- **[@vjs-10/html-media-store](../html-media-store)** - HTML store integration
|
||||
- **[@vjs-10/html](../html)** - Complete HTML player
|
||||
- **[@vjs-10/react-media-elements](../../react/react-media-elements)** - React alternative
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,384 @@
|
||||
# @vjs-10/html-media-store
|
||||
|
||||
> HTML/DOM integration for Video.js media store
|
||||
|
||||
[](https://www.npmjs.com/package/@vjs-10/html-media-store)
|
||||
|
||||
**Status:** Early Development
|
||||
|
||||
> **⚠️ PROTOTYPE - SUBJECT TO CHANGE**
|
||||
>
|
||||
> This package is in early prototype phase. Expect significant changes including:
|
||||
>
|
||||
> - Package restructuring and naming
|
||||
> - Breaking API changes
|
||||
> - Major architectural updates
|
||||
> - Incomplete or experimental features
|
||||
>
|
||||
> Not recommended for production use.
|
||||
|
||||
## Overview
|
||||
|
||||
`@vjs-10/html-media-store` provides DOM-specific integration between `@vjs-10/media-store` and HTML media elements. It handles bi-directional synchronization between media element state and the reactive store, enabling automatic UI updates across all connected components.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Automatic State Sync** - Bi-directional sync between media element and store
|
||||
- **DOM Integration** - Native HTMLMediaElement support
|
||||
- **Event Management** - Automatic event listener setup and cleanup
|
||||
- **State Mediators** - Pre-configured mediators for common patterns
|
||||
- **Memory Safe** - Proper cleanup and resource management
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @vjs-10/html-media-store
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Integration
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
// Create store
|
||||
const store = createMediaStore();
|
||||
|
||||
// Connect to media element
|
||||
const videoElement = document.querySelector('video');
|
||||
const connection = connectMediaStore(store, videoElement);
|
||||
|
||||
// Store automatically syncs with video state
|
||||
store.currentTime.subscribe((time) => {
|
||||
console.log('Current time:', time);
|
||||
});
|
||||
|
||||
// Clean up when done
|
||||
connection.disconnect();
|
||||
```
|
||||
|
||||
## Core API
|
||||
|
||||
### connectMediaStore
|
||||
|
||||
Connect a media store to an HTMLMediaElement:
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
const video = document.querySelector('video');
|
||||
|
||||
const connection = connectMediaStore(store, video, {
|
||||
// Optional configuration
|
||||
autoPlay: false,
|
||||
syncInterval: 250, // ms between time updates
|
||||
});
|
||||
|
||||
// Returns connection object
|
||||
interface Connection {
|
||||
disconnect: () => void; // Clean up listeners
|
||||
reconnect: () => void; // Re-establish connection
|
||||
pause: () => void; // Pause sync temporarily
|
||||
resume: () => void; // Resume sync
|
||||
}
|
||||
```
|
||||
|
||||
### State Mediators
|
||||
|
||||
Pre-configured mediators handle complex state coordination:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
setupAudibleMediator,
|
||||
setupPlayableMediator,
|
||||
setupTemporalMediator,
|
||||
} from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
const video = document.querySelector('video');
|
||||
|
||||
// Playable mediator - play/pause/ended states
|
||||
const playableMediator = setupPlayableMediator(store, video);
|
||||
|
||||
// Audible mediator - volume/muted states
|
||||
const audibleMediator = setupAudibleMediator(store, video);
|
||||
|
||||
// Temporal mediator - time/duration/seeking states
|
||||
const temporalMediator = setupTemporalMediator(store, video);
|
||||
|
||||
// Clean up all mediators
|
||||
playableMediator.disconnect();
|
||||
audibleMediator.disconnect();
|
||||
temporalMediator.disconnect();
|
||||
```
|
||||
|
||||
## State Synchronization
|
||||
|
||||
### Automatic Sync
|
||||
|
||||
The connection automatically syncs these properties:
|
||||
|
||||
**Playback State:**
|
||||
|
||||
- `paused` ↔ `video.paused`
|
||||
- `ended` ↔ `video.ended`
|
||||
- `seeking` ↔ `video.seeking`
|
||||
|
||||
**Time:**
|
||||
|
||||
- `currentTime` ↔ `video.currentTime`
|
||||
- `duration` ↔ `video.duration`
|
||||
|
||||
**Volume:**
|
||||
|
||||
- `volume` ↔ `video.volume`
|
||||
- `muted` ↔ `video.muted`
|
||||
|
||||
**Loading:**
|
||||
|
||||
- `buffered` ↔ `video.buffered`
|
||||
- `readyState` ↔ `video.readyState`
|
||||
|
||||
### Bi-directional Updates
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
const video = document.querySelector('video');
|
||||
connectMediaStore(store, video);
|
||||
|
||||
// Update store → updates video
|
||||
store.paused.set(false); // Video starts playing
|
||||
|
||||
// Update video → updates store
|
||||
video.currentTime = 30; // Store reflects new time
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Custom Sync Configuration
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
|
||||
const connection = connectMediaStore(store, video, {
|
||||
// Sync configuration
|
||||
syncInterval: 100, // Update frequency (ms)
|
||||
syncOnSeek: true, // Sync immediately on seek
|
||||
syncOnPlay: true, // Sync immediately on play
|
||||
|
||||
// Event configuration
|
||||
useCapture: false, // Event capture phase
|
||||
passive: true, // Passive event listeners
|
||||
|
||||
// State configuration
|
||||
persistVolume: true, // Remember volume in localStorage
|
||||
persistMuted: true, // Remember muted state
|
||||
});
|
||||
```
|
||||
|
||||
### Selective State Sync
|
||||
|
||||
```typescript
|
||||
import { createSyncGroup } from '@vjs-10/html-media-store';
|
||||
|
||||
// Only sync specific properties
|
||||
const syncGroup = createSyncGroup(store, video, {
|
||||
properties: ['currentTime', 'paused', 'volume'],
|
||||
events: ['timeupdate', 'play', 'pause', 'volumechange'],
|
||||
});
|
||||
|
||||
syncGroup.start();
|
||||
syncGroup.stop();
|
||||
```
|
||||
|
||||
### Multiple Elements
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
|
||||
// Connect multiple elements to same store
|
||||
const video1 = document.querySelector('#video1');
|
||||
const video2 = document.querySelector('#video2');
|
||||
|
||||
const connection1 = connectMediaStore(store, video1);
|
||||
const connection2 = connectMediaStore(store, video2);
|
||||
|
||||
// Both videos sync to same state
|
||||
store.paused.set(false); // Both videos play
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Player with Custom Controls
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
const video = document.querySelector('video');
|
||||
connectMediaStore(store, video);
|
||||
|
||||
// Create custom play button
|
||||
const playButton = document.querySelector('.play-button');
|
||||
playButton.addEventListener('click', () => {
|
||||
store.paused.set(!store.paused.get());
|
||||
});
|
||||
|
||||
// Update button based on state
|
||||
store.paused.subscribe((paused) => {
|
||||
playButton.textContent = paused ? 'Play' : 'Pause';
|
||||
});
|
||||
```
|
||||
|
||||
### Progress Bar Sync
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
const video = document.querySelector('video');
|
||||
connectMediaStore(store, video);
|
||||
|
||||
const progressBar = document.querySelector('.progress-bar');
|
||||
|
||||
// Update progress bar from store
|
||||
store.currentTime.subscribe((time) => {
|
||||
const duration = store.duration.get();
|
||||
const percentage = (time / duration) * 100;
|
||||
progressBar.style.width = `${percentage}%`;
|
||||
});
|
||||
|
||||
// Seek from progress bar
|
||||
progressBar.addEventListener('click', (e) => {
|
||||
const rect = progressBar.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const percentage = x / rect.width;
|
||||
const duration = store.duration.get();
|
||||
store.currentTime.set(duration * percentage);
|
||||
});
|
||||
```
|
||||
|
||||
### State Persistence
|
||||
|
||||
```typescript
|
||||
import { connectMediaStore } from '@vjs-10/html-media-store';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
const store = createMediaStore();
|
||||
const video = document.querySelector('video');
|
||||
connectMediaStore(store, video);
|
||||
|
||||
// Save state to localStorage
|
||||
store.volume.subscribe((volume) => {
|
||||
localStorage.setItem('player-volume', volume.toString());
|
||||
});
|
||||
|
||||
store.currentTime.subscribe((time) => {
|
||||
if (time > 0) {
|
||||
localStorage.setItem('player-position', time.toString());
|
||||
}
|
||||
});
|
||||
|
||||
// Restore state
|
||||
const savedVolume = localStorage.getItem('player-volume');
|
||||
if (savedVolume) {
|
||||
store.volume.set(Number.parseFloat(savedVolume));
|
||||
}
|
||||
|
||||
const savedPosition = localStorage.getItem('player-position');
|
||||
if (savedPosition) {
|
||||
store.currentTime.set(Number.parseFloat(savedPosition));
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Functions
|
||||
|
||||
```typescript
|
||||
// Main connection function
|
||||
function connectMediaStore(
|
||||
store: MediaStore,
|
||||
element: HTMLMediaElement,
|
||||
options?: ConnectionOptions
|
||||
): Connection
|
||||
|
||||
// Mediator setup functions
|
||||
function setupPlayableMediator(store: MediaStore, element: HTMLMediaElement): Mediator
|
||||
function setupAudibleMediator(store: MediaStore, element: HTMLMediaElement): Mediator
|
||||
function setupTemporalMediator(store: MediaStore, element: HTMLMediaElement): Mediator
|
||||
```
|
||||
|
||||
### Types
|
||||
|
||||
```typescript
|
||||
interface ConnectionOptions {
|
||||
syncInterval?: number;
|
||||
syncOnSeek?: boolean;
|
||||
syncOnPlay?: boolean;
|
||||
useCapture?: boolean;
|
||||
passive?: boolean;
|
||||
persistVolume?: boolean;
|
||||
persistMuted?: boolean;
|
||||
}
|
||||
|
||||
interface Connection {
|
||||
disconnect: () => void;
|
||||
reconnect: () => void;
|
||||
pause: () => void;
|
||||
resume: () => void;
|
||||
}
|
||||
|
||||
interface Mediator {
|
||||
disconnect: () => void;
|
||||
pause: () => void;
|
||||
resume: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
## Package Dependencies
|
||||
|
||||
- **Dependencies:**
|
||||
- `@vjs-10/media-store` - State management
|
||||
- `@vjs-10/html-media-elements` - Media element components
|
||||
- **Used by:** `@vjs-10/html` - Complete HTML player
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Build the package
|
||||
pnpm build
|
||||
|
||||
# Watch mode for development
|
||||
pnpm dev
|
||||
|
||||
# Run tests
|
||||
pnpm test
|
||||
|
||||
# Clean build artifacts
|
||||
pnpm clean
|
||||
```
|
||||
|
||||
## Related Packages
|
||||
|
||||
- **[@vjs-10/media-store](../../core/media-store)** - Core state management
|
||||
- **[@vjs-10/html-media-elements](../html-media-elements)** - Media elements
|
||||
- **[@vjs-10/html](../html)** - Complete HTML player
|
||||
- **[@vjs-10/react-media-store](../../react/react-media-store)** - React equivalent
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,429 @@
|
||||
# @vjs-10/html
|
||||
|
||||
> Complete HTML/Web Components library for building media players
|
||||
|
||||
[](https://www.npmjs.com/package/@vjs-10/html)
|
||||
|
||||
**Status:** Early Development
|
||||
|
||||
> **⚠️ PROTOTYPE - SUBJECT TO CHANGE**
|
||||
>
|
||||
> This package is in early prototype phase. Expect significant changes including:
|
||||
>
|
||||
> - Package restructuring and naming
|
||||
> - Breaking API changes
|
||||
> - Major architectural updates
|
||||
> - Incomplete or experimental features
|
||||
>
|
||||
> Not recommended for production use.
|
||||
|
||||
## Overview
|
||||
|
||||
`@vjs-10/html` is a comprehensive library for building media players with vanilla JavaScript and Web Components. It provides a complete set of UI components, state management, and utilities for creating feature-rich, accessible video and audio players.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Complete UI Library** - Full suite of player components
|
||||
- **Web Components** - Standards-based custom elements
|
||||
- **Default Skin** - Production-ready player theme
|
||||
- **Framework Agnostic** - Works with vanilla JS or any framework
|
||||
- **HLS/DASH Support** - Built-in streaming protocol support
|
||||
- **Accessible** - WCAG compliant with keyboard navigation
|
||||
- **Customizable** - Style with CSS, extend with components
|
||||
- **Type Safe** - Full TypeScript support
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @vjs-10/html
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Minimal Player
|
||||
|
||||
```typescript
|
||||
import '@vjs-10/html';
|
||||
|
||||
// Use the complete player in HTML
|
||||
const html = `
|
||||
<vjs-player src="video.mp4" controls poster="poster.jpg">
|
||||
</vjs-player>
|
||||
`;
|
||||
```
|
||||
|
||||
### With JavaScript
|
||||
|
||||
```typescript
|
||||
import { VideoPlayer } from '@vjs-10/html';
|
||||
|
||||
// Create player programmatically
|
||||
const player = new VideoPlayer({
|
||||
src: 'https://example.com/video.mp4',
|
||||
poster: 'poster.jpg',
|
||||
controls: true,
|
||||
autoplay: false,
|
||||
});
|
||||
|
||||
document.body.appendChild(player);
|
||||
|
||||
// Player API
|
||||
player.play();
|
||||
player.pause();
|
||||
player.currentTime = 30;
|
||||
player.volume = 0.5;
|
||||
```
|
||||
|
||||
### HLS Streaming
|
||||
|
||||
```html
|
||||
<!-- HLS support automatically enabled -->
|
||||
<vjs-player
|
||||
src="https://example.com/stream.m3u8"
|
||||
controls
|
||||
autoplay
|
||||
muted>
|
||||
</vjs-player>
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### Player Container
|
||||
|
||||
The main player component that includes all UI elements:
|
||||
|
||||
```html
|
||||
<vjs-player
|
||||
src="video.mp4"
|
||||
poster="poster.jpg"
|
||||
controls
|
||||
autoplay
|
||||
muted
|
||||
loop>
|
||||
</vjs-player>
|
||||
```
|
||||
|
||||
### Media Provider
|
||||
|
||||
Provides shared state to child components:
|
||||
|
||||
```html
|
||||
<vjs-media-provider>
|
||||
<vjs-video src="video.mp4"></vjs-video>
|
||||
<vjs-controls>
|
||||
<vjs-play-button></vjs-play-button>
|
||||
<vjs-time-slider></vjs-time-slider>
|
||||
<vjs-volume-slider></vjs-volume-slider>
|
||||
</vjs-controls>
|
||||
</vjs-media-provider>
|
||||
```
|
||||
|
||||
### Control Components
|
||||
|
||||
Individual control elements:
|
||||
|
||||
```html
|
||||
<!-- Playback controls -->
|
||||
<vjs-play-button></vjs-play-button>
|
||||
<vjs-pause-button></vjs-pause-button>
|
||||
|
||||
<!-- Time controls -->
|
||||
<vjs-time-slider></vjs-time-slider>
|
||||
<vjs-current-time-display></vjs-current-time-display>
|
||||
<vjs-duration-display></vjs-duration-display>
|
||||
|
||||
<!-- Volume controls -->
|
||||
<vjs-mute-button></vjs-mute-button>
|
||||
<vjs-volume-slider></vjs-volume-slider>
|
||||
|
||||
<!-- Screen controls -->
|
||||
<vjs-fullscreen-button></vjs-fullscreen-button>
|
||||
|
||||
<!-- UI utilities -->
|
||||
<vjs-tooltip></vjs-tooltip>
|
||||
<vjs-popover></vjs-popover>
|
||||
```
|
||||
|
||||
## Default Skin
|
||||
|
||||
The package includes a production-ready default skin:
|
||||
|
||||
```typescript
|
||||
import '@vjs-10/html';
|
||||
import '@vjs-10/html/themes/default.css';
|
||||
|
||||
// Player with default theme
|
||||
const html = `
|
||||
<vjs-player class="vjs-theme-default" src="video.mp4" controls>
|
||||
</vjs-player>
|
||||
`;
|
||||
```
|
||||
|
||||
### Skin Features
|
||||
|
||||
- **Responsive Layout** - Adapts to container size
|
||||
- **Touch Support** - Mobile-friendly controls
|
||||
- **Keyboard Navigation** - Full accessibility support
|
||||
- **Hover States** - Interactive feedback
|
||||
- **Loading States** - Buffering indicators
|
||||
- **Error States** - User-friendly error messages
|
||||
|
||||
## Customization
|
||||
|
||||
### Custom CSS
|
||||
|
||||
```css
|
||||
/* Override default styles */
|
||||
vjs-player {
|
||||
--vjs-primary-color: #007bff;
|
||||
--vjs-control-size: 48px;
|
||||
--vjs-control-spacing: 8px;
|
||||
--vjs-font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
/* Style individual components */
|
||||
vjs-play-button {
|
||||
background: var(--vjs-primary-color);
|
||||
border-radius: 50%;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
vjs-time-slider {
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
```
|
||||
|
||||
### CSS Custom Properties
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Colors */
|
||||
--vjs-primary-color: #007bff;
|
||||
--vjs-text-color: #ffffff;
|
||||
--vjs-background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
/* Sizing */
|
||||
--vjs-control-size: 44px;
|
||||
--vjs-control-spacing: 8px;
|
||||
--vjs-control-bar-height: 48px;
|
||||
|
||||
/* Typography */
|
||||
--vjs-font-family: system-ui, sans-serif;
|
||||
--vjs-font-size: 14px;
|
||||
|
||||
/* Timing */
|
||||
--vjs-transition-duration: 0.2s;
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Controls Layout
|
||||
|
||||
```html
|
||||
<vjs-media-provider>
|
||||
<vjs-video src="video.mp4"></vjs-video>
|
||||
|
||||
<!-- Custom control bar layout -->
|
||||
<div class="custom-controls">
|
||||
<div class="left-controls">
|
||||
<vjs-play-button></vjs-play-button>
|
||||
<vjs-current-time-display></vjs-current-time-display>
|
||||
<vjs-duration-display></vjs-duration-display>
|
||||
</div>
|
||||
|
||||
<div class="center-controls">
|
||||
<vjs-time-slider></vjs-time-slider>
|
||||
</div>
|
||||
|
||||
<div class="right-controls">
|
||||
<vjs-mute-button></vjs-mute-button>
|
||||
<vjs-volume-slider></vjs-volume-slider>
|
||||
<vjs-fullscreen-button></vjs-fullscreen-button>
|
||||
</div>
|
||||
</div>
|
||||
</vjs-media-provider>
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Programmatic Control
|
||||
|
||||
```typescript
|
||||
import { VideoPlayer } from '@vjs-10/html';
|
||||
|
||||
const player = new VideoPlayer({ src: 'video.mp4' });
|
||||
document.body.appendChild(player);
|
||||
|
||||
// Playback control
|
||||
await player.play();
|
||||
player.pause();
|
||||
|
||||
// Seeking
|
||||
player.currentTime = 30;
|
||||
player.fastSeek(60);
|
||||
|
||||
// Volume
|
||||
player.volume = 0.8;
|
||||
player.muted = true;
|
||||
|
||||
// Fullscreen
|
||||
await player.requestFullscreen();
|
||||
await player.exitFullscreen();
|
||||
|
||||
// Playback rate
|
||||
player.playbackRate = 1.5;
|
||||
|
||||
// Event listeners
|
||||
player.addEventListener('play', () => console.log('Playing'));
|
||||
player.addEventListener('pause', () => console.log('Paused'));
|
||||
player.addEventListener('ended', () => console.log('Ended'));
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
```typescript
|
||||
import { VideoPlayer } from '@vjs-10/html';
|
||||
import { createMediaStore } from '@vjs-10/media-store';
|
||||
|
||||
// Access player's media store
|
||||
const player = new VideoPlayer({ src: 'video.mp4' });
|
||||
const store = player.store;
|
||||
|
||||
// Subscribe to state changes
|
||||
store.currentTime.subscribe((time) => {
|
||||
console.log('Time:', time);
|
||||
});
|
||||
|
||||
store.paused.subscribe((paused) => {
|
||||
console.log('Paused:', paused);
|
||||
});
|
||||
|
||||
// Update state programmatically
|
||||
store.volume.set(0.5);
|
||||
store.currentTime.set(30);
|
||||
```
|
||||
|
||||
### Custom Components
|
||||
|
||||
```typescript
|
||||
import { MediaProvider } from '@vjs-10/html';
|
||||
|
||||
// Define custom control component
|
||||
class CustomButton extends HTMLElement {
|
||||
connectedCallback() {
|
||||
// Access shared media store via context
|
||||
const provider = this.closest('vjs-media-provider') as MediaProvider;
|
||||
const store = provider.store;
|
||||
|
||||
this.addEventListener('click', () => {
|
||||
// Toggle play/pause
|
||||
store.paused.set(!store.paused.get());
|
||||
});
|
||||
|
||||
// Update UI based on state
|
||||
store.paused.subscribe((paused) => {
|
||||
this.textContent = paused ? '▶' : '⏸';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('custom-play-button', CustomButton);
|
||||
```
|
||||
|
||||
### Playlist Support
|
||||
|
||||
```typescript
|
||||
import { VideoPlayer } from '@vjs-10/html';
|
||||
|
||||
const playlist = [
|
||||
{ src: 'video1.mp4', title: 'Video 1' },
|
||||
{ src: 'video2.mp4', title: 'Video 2' },
|
||||
{ src: 'video3.mp4', title: 'Video 3' },
|
||||
];
|
||||
|
||||
const player = new VideoPlayer({ src: playlist[0].src });
|
||||
let currentIndex = 0;
|
||||
|
||||
// Play next video when current ends
|
||||
player.addEventListener('ended', () => {
|
||||
currentIndex = (currentIndex + 1) % playlist.length;
|
||||
player.src = playlist[currentIndex].src;
|
||||
player.load();
|
||||
player.play();
|
||||
});
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
All components include:
|
||||
|
||||
- **ARIA attributes** - Screen reader support
|
||||
- **Keyboard navigation** - Complete keyboard control
|
||||
- **Focus management** - Visible focus indicators
|
||||
- **Semantic HTML** - Proper element roles
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
- `Space` - Play/pause
|
||||
- `←/→` - Seek backward/forward 5 seconds
|
||||
- `↑/↓` - Volume up/down
|
||||
- `M` - Toggle mute
|
||||
- `F` - Toggle fullscreen
|
||||
- `0-9` - Seek to 0-90% of video
|
||||
|
||||
## Browser Support
|
||||
|
||||
- Chrome 54+
|
||||
- Firefox 63+
|
||||
- Safari 10.1+
|
||||
- Edge 79+
|
||||
|
||||
## Package Dependencies
|
||||
|
||||
- **Dependencies:**
|
||||
- `@vjs-10/core` - Core components
|
||||
- `@vjs-10/media-store` - State management
|
||||
- `@vjs-10/html-icons` - Icon components
|
||||
- `@vjs-10/html-media-elements` - Media elements
|
||||
- `@vjs-10/html-media-store` - Store integration
|
||||
- `@floating-ui/dom` - Tooltip/popover positioning
|
||||
- `@open-wc/context-protocol` - State sharing
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Build the package
|
||||
pnpm build
|
||||
|
||||
# Watch mode for development
|
||||
pnpm dev
|
||||
|
||||
# Run tests
|
||||
pnpm test
|
||||
|
||||
# Clean build artifacts
|
||||
pnpm clean
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
See the HTML demo for complete examples:
|
||||
|
||||
```bash
|
||||
# Run the demo
|
||||
pnpm dev:html
|
||||
```
|
||||
|
||||
## Related Packages
|
||||
|
||||
- **[@vjs-10/react](../../react/react)** - React alternative
|
||||
- **[@vjs-10/media-store](../../core/media-store)** - Core state management
|
||||
- **[@vjs-10/html-media-elements](../html-media-elements)** - Media elements
|
||||
|
||||
## Migrating from Video.js 8.x
|
||||
|
||||
Coming soon - migration guide for Video.js 8.x users.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
Reference in New Issue
Block a user