UNPKG

@orabana/video-gallery

Version:

A modern, responsive Angular video gallery component with playlist functionality

151 lines (145 loc) 18 kB
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