Added extensive component library with feedback components (empty state, loading spinner, skeleton loader), enhanced form components (autocomplete, date picker, file upload, form field, time picker), navigation components (pagination), and overlay components (backdrop, drawer, modal, overlay container). Updated demo application with comprehensive showcase components and enhanced styling throughout the project. Excluded font files from repository to reduce size. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
543 lines
18 KiB
TypeScript
543 lines
18 KiB
TypeScript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { VideoPlayerComponent, VideoSource, VideoTrack, VideoPlayerSize, VideoPlayerVariant } from '../../../../../ui-essentials/src/lib/components/media/video-player/video-player.component';
|
|
|
|
@Component({
|
|
selector: 'ui-video-player-demo',
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
VideoPlayerComponent
|
|
],
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
template: `
|
|
<div class="video-player-demo">
|
|
<h2>Video Player Component Showcase</h2>
|
|
|
|
<!-- Video Player Variants -->
|
|
<section class="demo-section">
|
|
<h3>Video Player Variants</h3>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Default Player</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
[tracks]="videoTracks()"
|
|
poster="https://images.unsplash.com/photo-1574717024653-61fd2cf4d44d?w=800&h=450&fit=crop"
|
|
variant="default"
|
|
size="md"
|
|
[showControls]="true"
|
|
[showVolumeControl]="true"
|
|
[allowFullscreen]="true"
|
|
ariaLabel="Demo video player"
|
|
(play)="handleVideoEvent('Default player started playing')"
|
|
(pause)="handleVideoEvent('Default player paused')"
|
|
(ended)="handleVideoEvent('Default player ended')"
|
|
(timeUpdate)="handleTimeUpdate($event)"
|
|
(volumeChange)="handleVolumeChange($event)"
|
|
(fullscreenChange)="handleFullscreenChange($event)"
|
|
(error)="handleVideoError($event)">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Minimal Player</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=800&h=450&fit=crop"
|
|
variant="minimal"
|
|
size="sm"
|
|
[showControls]="true"
|
|
[showVolumeControl]="false"
|
|
[allowFullscreen]="false"
|
|
ariaLabel="Minimal video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Theater Player</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1440404653325-ab127d49abc1?w=800&h=450&fit=crop"
|
|
variant="theater"
|
|
size="lg"
|
|
[showControls]="true"
|
|
[showVolumeControl]="true"
|
|
[allowFullscreen]="true"
|
|
[autoplay]="false"
|
|
[muted]="true"
|
|
ariaLabel="Theater video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Size Variants -->
|
|
<section class="demo-section">
|
|
<h3>Size Variants</h3>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Small (sm)</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1598300042247-d088f8ab3a91?w=400&h=225&fit=crop"
|
|
size="sm"
|
|
[showControls]="true"
|
|
ariaLabel="Small video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Medium (md) - Default</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1574717024653-61fd2cf4d44d?w=600&h=337&fit=crop"
|
|
size="md"
|
|
[showControls]="true"
|
|
ariaLabel="Medium video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Large (lg)</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1440404653325-ab127d49abc1?w=800&h=450&fit=crop"
|
|
size="lg"
|
|
[showControls]="true"
|
|
ariaLabel="Large video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Feature Demonstrations -->
|
|
<section class="demo-section">
|
|
<h3>Feature Demonstrations</h3>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Player with Subtitles</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
[tracks]="videoTracks()"
|
|
poster="https://images.unsplash.com/photo-1611162617474-5b21e879e113?w=800&h=450&fit=crop"
|
|
[showControls]="true"
|
|
ariaLabel="Video player with subtitles">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Autoplay Player (Muted)</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1598300042247-d088f8ab3a91?w=800&h=450&fit=crop"
|
|
[autoplay]="true"
|
|
[muted]="true"
|
|
[loop]="true"
|
|
[showControls]="true"
|
|
ariaLabel="Autoplay video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Player without Volume Control</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1574717024653-61fd2cf4d44d?w=800&h=450&fit=crop"
|
|
[showControls]="true"
|
|
[showVolumeControl]="false"
|
|
ariaLabel="Video player without volume control">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Disabled Player</h4>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1440404653325-ab127d49abc1?w=800&h=450&fit=crop"
|
|
[disabled]="true"
|
|
[showControls]="true"
|
|
ariaLabel="Disabled video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Interactive Demo -->
|
|
<section class="demo-section">
|
|
<h3>Interactive Demo</h3>
|
|
<p class="demo-description">
|
|
Customize the video player below by toggling different options:
|
|
</p>
|
|
|
|
<div class="controls-grid">
|
|
<label class="control-label">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="demoConfig().autoplay"
|
|
(change)="updateDemoConfig('autoplay', $event)">
|
|
Autoplay (muted)
|
|
</label>
|
|
<label class="control-label">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="demoConfig().loop"
|
|
(change)="updateDemoConfig('loop', $event)">
|
|
Loop
|
|
</label>
|
|
<label class="control-label">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="demoConfig().showControls"
|
|
(change)="updateDemoConfig('showControls', $event)">
|
|
Show Controls
|
|
</label>
|
|
<label class="control-label">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="demoConfig().showVolumeControl"
|
|
(change)="updateDemoConfig('showVolumeControl', $event)">
|
|
Volume Control
|
|
</label>
|
|
<label class="control-label">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="demoConfig().allowFullscreen"
|
|
(change)="updateDemoConfig('allowFullscreen', $event)">
|
|
Fullscreen
|
|
</label>
|
|
<label class="control-label">
|
|
<input
|
|
type="checkbox"
|
|
[checked]="demoConfig().disabled"
|
|
(change)="updateDemoConfig('disabled', $event)">
|
|
Disabled
|
|
</label>
|
|
</div>
|
|
|
|
<div class="select-controls">
|
|
<label class="select-label">
|
|
Size:
|
|
<select
|
|
[value]="demoConfig().size"
|
|
(change)="updateDemoSize($event)"
|
|
class="demo-select">
|
|
<option value="sm">Small</option>
|
|
<option value="md">Medium</option>
|
|
<option value="lg">Large</option>
|
|
</select>
|
|
</label>
|
|
<label class="select-label">
|
|
Variant:
|
|
<select
|
|
[value]="demoConfig().variant"
|
|
(change)="updateDemoVariant($event)"
|
|
class="demo-select">
|
|
<option value="default">Default</option>
|
|
<option value="minimal">Minimal</option>
|
|
<option value="theater">Theater</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
[tracks]="videoTracks()"
|
|
[poster]="demoConfig().poster"
|
|
[size]="demoConfig().size"
|
|
[variant]="demoConfig().variant"
|
|
[autoplay]="demoConfig().autoplay"
|
|
[loop]="demoConfig().loop"
|
|
[muted]="demoConfig().autoplay"
|
|
[showControls]="demoConfig().showControls"
|
|
[showVolumeControl]="demoConfig().showVolumeControl"
|
|
[allowFullscreen]="demoConfig().allowFullscreen"
|
|
[disabled]="demoConfig().disabled"
|
|
ariaLabel="Interactive demo video player"
|
|
(play)="handleVideoEvent('Interactive player started playing')"
|
|
(pause)="handleVideoEvent('Interactive player paused')"
|
|
(ended)="handleVideoEvent('Interactive player ended')"
|
|
(timeUpdate)="handleTimeUpdate($event)"
|
|
(volumeChange)="handleVolumeChange($event)"
|
|
(fullscreenChange)="handleFullscreenChange($event)"
|
|
(error)="handleVideoError($event)">
|
|
</ui-video-player>
|
|
</div>
|
|
|
|
@if (lastEvent()) {
|
|
<div class="event-display">
|
|
<strong>Last event:</strong> {{ lastEvent() }}
|
|
</div>
|
|
}
|
|
</section>
|
|
|
|
<!-- Real-world Examples -->
|
|
<section class="demo-section">
|
|
<h3>Real-world Examples</h3>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Course Video Player</h4>
|
|
<p class="demo-description">
|
|
Typical setup for educational content with subtitles and chapter markers
|
|
</p>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
[tracks]="videoTracks()"
|
|
poster="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=800&h=450&fit=crop"
|
|
size="lg"
|
|
[showControls]="true"
|
|
[showVolumeControl]="true"
|
|
[allowFullscreen]="true"
|
|
preload="metadata"
|
|
ariaLabel="Course video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Product Demo Player</h4>
|
|
<p class="demo-description">
|
|
Autoplay setup for product demonstrations and marketing content
|
|
</p>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=800&h=450&fit=crop"
|
|
variant="minimal"
|
|
size="md"
|
|
[autoplay]="true"
|
|
[muted]="true"
|
|
[loop]="true"
|
|
[showControls]="true"
|
|
[showVolumeControl]="false"
|
|
[allowFullscreen]="false"
|
|
ariaLabel="Product demo video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Media Gallery Player</h4>
|
|
<p class="demo-description">
|
|
Compact player for media galleries and previews
|
|
</p>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="basicVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1598300042247-d088f8ab3a91?w=800&h=450&fit=crop"
|
|
size="sm"
|
|
[showControls]="true"
|
|
[showVolumeControl]="false"
|
|
[allowFullscreen]="true"
|
|
preload="none"
|
|
ariaLabel="Media gallery video player">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Error Handling Demo -->
|
|
<section class="demo-section">
|
|
<h3>Error Handling</h3>
|
|
|
|
<div class="variant-demo">
|
|
<h4>Invalid Video Source</h4>
|
|
<p class="demo-description">
|
|
Demonstrates error handling when video fails to load
|
|
</p>
|
|
<div class="player-container">
|
|
<ui-video-player
|
|
[sources]="errorVideoSources()"
|
|
poster="https://images.unsplash.com/photo-1574717024653-61fd2cf4d44d?w=800&h=450&fit=crop"
|
|
[showControls]="true"
|
|
ariaLabel="Error demo video player"
|
|
(error)="handleVideoError($event)">
|
|
</ui-video-player>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Usage Examples -->
|
|
<section class="demo-section">
|
|
<h3>Code Examples</h3>
|
|
<div class="code-examples">
|
|
<div class="code-example">
|
|
<h4>Basic Video Player:</h4>
|
|
<pre><code><ui-video-player
|
|
[sources]="videoSources"
|
|
poster="poster-image.jpg"
|
|
[showControls]="true"
|
|
ariaLabel="Demo video player">
|
|
</ui-video-player></code></pre>
|
|
</div>
|
|
|
|
<div class="code-example">
|
|
<h4>Video Player with Subtitles:</h4>
|
|
<pre><code><ui-video-player
|
|
[sources]="videoSources"
|
|
[tracks]="subtitleTracks"
|
|
poster="poster-image.jpg"
|
|
size="lg"
|
|
[showControls]="true"
|
|
[allowFullscreen]="true"
|
|
(play)="onVideoPlay()"
|
|
(pause)="onVideoPause()">
|
|
</ui-video-player></code></pre>
|
|
</div>
|
|
|
|
<div class="code-example">
|
|
<h4>Autoplay Video (Marketing/Demo):</h4>
|
|
<pre><code><ui-video-player
|
|
[sources]="videoSources"
|
|
variant="minimal"
|
|
size="md"
|
|
[autoplay]="true"
|
|
[muted]="true"
|
|
[loop]="true"
|
|
[showVolumeControl]="false"
|
|
[allowFullscreen]="false">
|
|
</ui-video-player></code></pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
`,
|
|
styleUrl: './video-player-demo.component.scss'
|
|
})
|
|
export class VideoPlayerDemoComponent {
|
|
// State signals
|
|
lastEvent = signal('');
|
|
|
|
// Demo video sources - using sample videos from the web
|
|
basicVideoSources = signal<VideoSource[]>([
|
|
{
|
|
src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
|
type: 'video/mp4',
|
|
quality: '1080p',
|
|
label: 'HD'
|
|
},
|
|
{
|
|
src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.webm',
|
|
type: 'video/webm',
|
|
quality: '1080p',
|
|
label: 'HD WebM'
|
|
}
|
|
]);
|
|
|
|
errorVideoSources = signal<VideoSource[]>([
|
|
{
|
|
src: 'https://invalid-url.example.com/nonexistent.mp4',
|
|
type: 'video/mp4',
|
|
quality: '1080p',
|
|
label: 'Invalid Source'
|
|
}
|
|
]);
|
|
|
|
videoTracks = signal<VideoTrack[]>([
|
|
{
|
|
src: 'data:text/vtt;base64,V0VCVlRUCgowMDowMDowMC4wMDAgLS0+IDAwOjAwOjA1LjAwMAo8Zm9udCBjb2xvcj0iI2ZmZmZmZiI+U2FtcGxlIHN1YnRpdGxlIHRleHQ8L2ZvbnQ+CgowMDowMDowNS4wMDAgLS0+IDAwOjAwOjEwLjAwMAo8Zm9udCBjb2xvcj0iI2ZmZmZmZiI+VGhpcyBpcyBhIGRlbW9uc3RyYXRpb24gb2Ygc3VidGl0bGVzPC9mb250Pg==',
|
|
kind: 'subtitles',
|
|
srclang: 'en',
|
|
label: 'English',
|
|
default: true
|
|
}
|
|
]);
|
|
|
|
// Demo configuration
|
|
demoConfig = signal({
|
|
size: 'md' as VideoPlayerSize,
|
|
variant: 'default' as VideoPlayerVariant,
|
|
autoplay: false,
|
|
loop: false,
|
|
showControls: true,
|
|
showVolumeControl: true,
|
|
allowFullscreen: true,
|
|
disabled: false,
|
|
poster: 'https://images.unsplash.com/photo-1574717024653-61fd2cf4d44d?w=800&h=450&fit=crop'
|
|
});
|
|
|
|
handleVideoEvent(event: string): void {
|
|
this.lastEvent.set(`${event} at ${new Date().toLocaleTimeString()}`);
|
|
console.log(`Video event: ${event}`);
|
|
}
|
|
|
|
handleTimeUpdate(time: number): void {
|
|
// Only log significant time updates to avoid console spam
|
|
if (Math.floor(time) % 10 === 0) {
|
|
console.log(`Time update: ${this.formatTime(time)}`);
|
|
}
|
|
}
|
|
|
|
handleVolumeChange(volume: number): void {
|
|
this.lastEvent.set(`Volume changed to ${Math.round(volume * 100)}% at ${new Date().toLocaleTimeString()}`);
|
|
console.log(`Volume changed: ${volume}`);
|
|
}
|
|
|
|
handleFullscreenChange(isFullscreen: boolean): void {
|
|
this.lastEvent.set(`${isFullscreen ? 'Entered' : 'Exited'} fullscreen at ${new Date().toLocaleTimeString()}`);
|
|
console.log(`Fullscreen: ${isFullscreen}`);
|
|
}
|
|
|
|
handleVideoError(event: Event): void {
|
|
this.lastEvent.set(`Video error occurred at ${new Date().toLocaleTimeString()}`);
|
|
console.error('Video error:', event);
|
|
}
|
|
|
|
updateDemoConfig(key: string, event: Event): void {
|
|
const target = event.target as HTMLInputElement;
|
|
const value = target.checked;
|
|
|
|
this.demoConfig.update(config => ({
|
|
...config,
|
|
[key]: value
|
|
}));
|
|
}
|
|
|
|
updateDemoSize(event: Event): void {
|
|
const target = event.target as HTMLSelectElement;
|
|
const size = target.value as VideoPlayerSize;
|
|
|
|
this.demoConfig.update(config => ({
|
|
...config,
|
|
size
|
|
}));
|
|
}
|
|
|
|
updateDemoVariant(event: Event): void {
|
|
const target = event.target as HTMLSelectElement;
|
|
const variant = target.value as VideoPlayerVariant;
|
|
|
|
this.demoConfig.update(config => ({
|
|
...config,
|
|
variant
|
|
}));
|
|
}
|
|
|
|
private formatTime(seconds: number): string {
|
|
if (isNaN(seconds)) return '0:00';
|
|
|
|
const minutes = Math.floor(seconds / 60);
|
|
const secs = Math.floor(seconds % 60);
|
|
|
|
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
}
|
|
} |