@orabana/video-gallery
Version:
A modern, responsive Angular video gallery component with playlist functionality
151 lines (145 loc) • 18 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, Input, Component } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
class VideoGalleryService {
constructor() { }
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VideoGalleryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VideoGalleryService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VideoGalleryService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [] });
class VideoGalleryComponent {
mainTitle = 'Product Videos';
playlistTitle = 'Videos for this product';
videos = [];
currentVideoIndex = 0;
currentVideo = null;
// Default sample videos - used as fallback when no videos input is provided
defaultVideos = [
{
id: '1',
title: 'Replacement foods',
subtitle: 'Giselle',
duration: '0:58',
thumbnail: 'https://placehold.co/160x90/ff6b35/ffffff?text=Video+1',
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
author: 'Giselle',
authorLogo: 'https://placehold.co/40x40/cccccc/333333?text=G',
rating: 4.5,
viewCount: '38028'
},
{
id: '2',
title: 'Customer Review: These are like a photo model of strawberries. WOW',
subtitle: 'Able',
duration: '0:52',
thumbnail: 'https://placehold.co/160x90/ff3366/ffffff?text=Video+2',
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
author: 'Able',
authorLogo: 'https://placehold.co/40x40/cccccc/333333?text=A',
rating: 5.0,
viewCount: '15432'
},
{
id: '3',
title: 'Fresas en mal estado',
subtitle: 'Edith ramos',
duration: '0:10',
thumbnail: 'https://placehold.co/160x90/33ff66/ffffff?text=Video+3',
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
author: 'Edith ramos',
authorLogo: 'https://placehold.co/40x40/cccccc/333333?text=E',
rating: 2.0,
viewCount: '8921'
},
{
id: '4',
title: 'Rotten Organic Strawberries',
subtitle: 'eighth note',
duration: '0:18',
thumbnail: 'https://placehold.co/160x90/3366ff/ffffff?text=Video+4',
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
author: 'eighth note',
authorLogo: 'https://placehold.co/40x40/cccccc/333333?text=8',
rating: 1.5,
viewCount: '5643'
},
{
id: '5',
title: 'Strawberry Up Close Look - Best Way to Cut and Eat',
subtitle: 'Technology Teardown',
duration: '2:10',
thumbnail: 'https://placehold.co/160x90/ffcc33/ffffff?text=Video+5',
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
author: 'Technology Teardown',
authorLogo: 'https://placehold.co/40x40/cccccc/333333?text=T',
rating: 4.8,
viewCount: '42156'
},
{
id: '6',
title: 'Customer Review: WROMS! WARNING!',
subtitle: 'Concerned Customer',
duration: '1:25',
thumbnail: 'https://placehold.co/160x90/ff9933/ffffff?text=Video+6',
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
author: 'Concerned Customer',
authorLogo: 'https://placehold.co/40x40/cccccc/333333?text=C',
rating: 1.0,
viewCount: '12890'
}
];
ngOnInit() {
// Use provided videos or fall back to default sample videos
if (!this.videos || this.videos.length === 0) {
this.videos = this.defaultVideos;
}
// Set current video to first video if available
if (this.videos.length > 0) {
this.currentVideo = this.videos[this.currentVideoIndex];
}
}
selectVideo(index) {
if (index >= 0 && index < this.videos.length) {
this.currentVideoIndex = index;
this.currentVideo = this.videos[index];
}
}
getStars(rating) {
const fullStars = Math.floor(rating);
const hasHalfStar = rating % 1 !== 0;
const stars = Array(fullStars).fill(1);
if (hasHalfStar)
stars.push(0.5);
return stars;
}
onVideoLoaded() {
// Handle video loaded event if needed
console.log('Video loaded:', this.currentVideo?.title);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VideoGalleryComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: VideoGalleryComponent, isStandalone: true, selector: "orabana-video-gallery", inputs: { mainTitle: "mainTitle", playlistTitle: "playlistTitle", videos: "videos" }, ngImport: i0, template: "<div class=\"video-gallery-container\">\n <!-- Main Title -->\n <h1 class=\"main-title\">{{mainTitle}}</h1>\n \n <div class=\"video-gallery-content\">\n <!-- Video Player Section -->\n <div class=\"video-player-section\">\n <div class=\"video-player\">\n <video \n #videoPlayer\n [src]=\"currentVideo?.videoUrl\" \n controls \n class=\"video-element\"\n (loadedmetadata)=\"onVideoLoaded()\">\n Your browser does not support the video tag.\n </video>\n </div>\n \n <!-- Video Info -->\n <div class=\"video-info\" *ngIf=\"currentVideo\">\n <div class=\"video-details\">\n <div class=\"author-info\">\n <img [src]=\"currentVideo.authorLogo\" [alt]=\"currentVideo.author\" class=\"author-logo\">\n <div class=\"video-meta\">\n <h3 class=\"video-title\">{{currentVideo.title}}</h3>\n <p class=\"video-subtitle\">{{currentVideo.subtitle}}</p>\n <div class=\"video-stats\">\n <div class=\"rating\">\n <span class=\"stars\">\n <span *ngFor=\"let star of getStars(currentVideo.rating)\" class=\"star\">\u2605</span>\n </span>\n <span class=\"rating-count\">{{currentVideo.rating}}</span>\n </div>\n <span class=\"view-count\">{{currentVideo.viewCount}}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n \n <!-- Playlist Section -->\n <div class=\"playlist-section\">\n <h2 class=\"playlist-title\">{{playlistTitle}}</h2>\n <div class=\"playlist\">\n <div \n *ngFor=\"let video of videos; let i = index\" \n class=\"playlist-item\"\n [class.active]=\"currentVideoIndex === i\"\n (click)=\"selectVideo(i)\">\n \n <div class=\"thumbnail-container\">\n <img [src]=\"video.thumbnail\" [alt]=\"video.title\" class=\"thumbnail\">\n <span class=\"duration\">{{video.duration}}</span>\n <div class=\"play-overlay\" *ngIf=\"currentVideoIndex !== i\">\n <span class=\"play-icon\">\u25B6</span>\n </div>\n </div>\n \n <div class=\"video-item-info\">\n <h4 class=\"item-title\">{{video.title}}</h4>\n <p class=\"item-subtitle\">{{video.subtitle}}</p>\n <div class=\"now-playing\" *ngIf=\"currentVideoIndex === i\">\n <span class=\"now-playing-text\">Now playing</span>\n <span class=\"playing-icon\">\u25B6</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".video-gallery-container{font-family:Arial,sans-serif;background-color:#f4f4f5;color:#111827;padding:1rem}.main-title{font-size:24px;font-weight:700;margin-bottom:1rem;color:#111827}.video-gallery-content{display:flex;gap:1rem;max-width:1400px}.video-player-section{flex:1;max-width:800px}.video-player{background-color:#000;border-radius:8px;overflow:hidden;margin-bottom:1rem}.video-element{width:100%;height:450px;object-fit:cover}.video-info{background-color:#111;padding:15px;border-radius:8px}.author-info{display:flex;gap:12px;align-items:flex-start}.author-logo{width:40px;height:40px;border-radius:50%;object-fit:cover}.video-meta{flex:1}.video-title{font-size:18px;font-weight:700;margin:0 0 5px;color:#fff}.video-subtitle{color:#aaa;margin:0 0 10px;font-size:14px}.video-stats{display:flex;gap:15px;align-items:center}.rating{display:flex;align-items:center;gap:5px}.stars{color:#ff6b35}.star{font-size:14px}.rating-count,.view-count{font-size:14px;color:#aaa}.playlist-section{width:400px;flex-shrink:0}.playlist-title{font-size:18px;font-weight:700;margin-bottom:15px;color:#111827}.playlist{background-color:#111;border-radius:8px;padding:10px;max-height:600px;overflow-y:auto}.playlist-item{display:flex;gap:12px;padding:10px;border-radius:6px;cursor:pointer;transition:background-color .2s;margin-bottom:8px}.playlist-item:hover{background-color:#222}.playlist-item.active{background-color:#333;border:1px solid #ff6b35}.thumbnail-container{position:relative;width:100px;height:56px;flex-shrink:0}.thumbnail{width:100%;height:100%;object-fit:cover;border-radius:4px}.duration{position:absolute;bottom:4px;right:4px;background-color:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:12px;font-weight:700}.play-overlay{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#000000b3;border-radius:50%;width:30px;height:30px;display:flex;align-items:center;justify-content:center}.play-icon{color:#fff;font-size:12px}.video-item-info{flex:1;min-width:0}.item-title{font-size:14px;font-weight:700;margin:0 0 5px;color:#fff;line-height:1.3;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical}.item-subtitle{font-size:12px;color:#aaa;margin:0 0 8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.now-playing{display:flex;align-items:center;gap:5px}.now-playing-text{font-size:12px;color:#ff6b35;font-weight:700}.playing-icon{color:#ff6b35;font-size:10px}@media (max-width: 1024px){.video-gallery-content{flex-direction:column}.playlist-section{width:100%}.playlist{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px}}@media (max-width: 768px){.video-gallery-container{padding:10px}.video-element{height:250px}.playlist{grid-template-columns:1fr}.thumbnail-container{width:100px;height:56px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: VideoGalleryComponent, decorators: [{
type: Component,
args: [{ selector: 'orabana-video-gallery', standalone: true, imports: [CommonModule], template: "<div class=\"video-gallery-container\">\n <!-- Main Title -->\n <h1 class=\"main-title\">{{mainTitle}}</h1>\n \n <div class=\"video-gallery-content\">\n <!-- Video Player Section -->\n <div class=\"video-player-section\">\n <div class=\"video-player\">\n <video \n #videoPlayer\n [src]=\"currentVideo?.videoUrl\" \n controls \n class=\"video-element\"\n (loadedmetadata)=\"onVideoLoaded()\">\n Your browser does not support the video tag.\n </video>\n </div>\n \n <!-- Video Info -->\n <div class=\"video-info\" *ngIf=\"currentVideo\">\n <div class=\"video-details\">\n <div class=\"author-info\">\n <img [src]=\"currentVideo.authorLogo\" [alt]=\"currentVideo.author\" class=\"author-logo\">\n <div class=\"video-meta\">\n <h3 class=\"video-title\">{{currentVideo.title}}</h3>\n <p class=\"video-subtitle\">{{currentVideo.subtitle}}</p>\n <div class=\"video-stats\">\n <div class=\"rating\">\n <span class=\"stars\">\n <span *ngFor=\"let star of getStars(currentVideo.rating)\" class=\"star\">\u2605</span>\n </span>\n <span class=\"rating-count\">{{currentVideo.rating}}</span>\n </div>\n <span class=\"view-count\">{{currentVideo.viewCount}}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n \n <!-- Playlist Section -->\n <div class=\"playlist-section\">\n <h2 class=\"playlist-title\">{{playlistTitle}}</h2>\n <div class=\"playlist\">\n <div \n *ngFor=\"let video of videos; let i = index\" \n class=\"playlist-item\"\n [class.active]=\"currentVideoIndex === i\"\n (click)=\"selectVideo(i)\">\n \n <div class=\"thumbnail-container\">\n <img [src]=\"video.thumbnail\" [alt]=\"video.title\" class=\"thumbnail\">\n <span class=\"duration\">{{video.duration}}</span>\n <div class=\"play-overlay\" *ngIf=\"currentVideoIndex !== i\">\n <span class=\"play-icon\">\u25B6</span>\n </div>\n </div>\n \n <div class=\"video-item-info\">\n <h4 class=\"item-title\">{{video.title}}</h4>\n <p class=\"item-subtitle\">{{video.subtitle}}</p>\n <div class=\"now-playing\" *ngIf=\"currentVideoIndex === i\">\n <span class=\"now-playing-text\">Now playing</span>\n <span class=\"playing-icon\">\u25B6</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", styles: [".video-gallery-container{font-family:Arial,sans-serif;background-color:#f4f4f5;color:#111827;padding:1rem}.main-title{font-size:24px;font-weight:700;margin-bottom:1rem;color:#111827}.video-gallery-content{display:flex;gap:1rem;max-width:1400px}.video-player-section{flex:1;max-width:800px}.video-player{background-color:#000;border-radius:8px;overflow:hidden;margin-bottom:1rem}.video-element{width:100%;height:450px;object-fit:cover}.video-info{background-color:#111;padding:15px;border-radius:8px}.author-info{display:flex;gap:12px;align-items:flex-start}.author-logo{width:40px;height:40px;border-radius:50%;object-fit:cover}.video-meta{flex:1}.video-title{font-size:18px;font-weight:700;margin:0 0 5px;color:#fff}.video-subtitle{color:#aaa;margin:0 0 10px;font-size:14px}.video-stats{display:flex;gap:15px;align-items:center}.rating{display:flex;align-items:center;gap:5px}.stars{color:#ff6b35}.star{font-size:14px}.rating-count,.view-count{font-size:14px;color:#aaa}.playlist-section{width:400px;flex-shrink:0}.playlist-title{font-size:18px;font-weight:700;margin-bottom:15px;color:#111827}.playlist{background-color:#111;border-radius:8px;padding:10px;max-height:600px;overflow-y:auto}.playlist-item{display:flex;gap:12px;padding:10px;border-radius:6px;cursor:pointer;transition:background-color .2s;margin-bottom:8px}.playlist-item:hover{background-color:#222}.playlist-item.active{background-color:#333;border:1px solid #ff6b35}.thumbnail-container{position:relative;width:100px;height:56px;flex-shrink:0}.thumbnail{width:100%;height:100%;object-fit:cover;border-radius:4px}.duration{position:absolute;bottom:4px;right:4px;background-color:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:12px;font-weight:700}.play-overlay{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#000000b3;border-radius:50%;width:30px;height:30px;display:flex;align-items:center;justify-content:center}.play-icon{color:#fff;font-size:12px}.video-item-info{flex:1;min-width:0}.item-title{font-size:14px;font-weight:700;margin:0 0 5px;color:#fff;line-height:1.3;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical}.item-subtitle{font-size:12px;color:#aaa;margin:0 0 8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.now-playing{display:flex;align-items:center;gap:5px}.now-playing-text{font-size:12px;color:#ff6b35;font-weight:700}.playing-icon{color:#ff6b35;font-size:10px}@media (max-width: 1024px){.video-gallery-content{flex-direction:column}.playlist-section{width:100%}.playlist{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px}}@media (max-width: 768px){.video-gallery-container{padding:10px}.video-element{height:250px}.playlist{grid-template-columns:1fr}.thumbnail-container{width:100px;height:56px}}\n"] }]
}], propDecorators: { mainTitle: [{
type: Input
}], playlistTitle: [{
type: Input
}], videos: [{
type: Input
}] } });
/*
* Public API Surface of video-gallery
*/
/**
* Generated bundle index. Do not edit.
*/
export { VideoGalleryComponent, VideoGalleryService };
//# sourceMappingURL=orabana-video-gallery.mjs.map