UNPKG

ng-flyweight-gallery

Version:

`ng-flyweight-gallery` is a lightweight and customizable Angular component library for creating responsive media galleries. It supports images and videos, with features like autoplay, loop for videos and a responsive layout.

194 lines (182 loc) 13.6 kB
import * as i0 from '@angular/core'; import { SecurityContext, Component, Input, NgModule } from '@angular/core'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i1 from '@angular/platform-browser'; class NgFlyweightGalleryComponent { sanitizer; mediaItems = []; selectedItem; lightboxOpen = false; selectedIndex = 0; constructor(sanitizer) { this.sanitizer = sanitizer; } ngOnInit() { this.mediaItems.forEach((item, index) => { item.source = this.sanitizeUrl(item.source); if (!item.thumbnail) { if (item.type === 'image') { item.thumbnail = this.sanitizeUrl(item.source); } else if (item.type === 'video') { this.generateVideoThumbnail(item, index); } } else { item.thumbnail = this.sanitizeUrl(item.thumbnail); } }); } sanitizeUrl(url) { return this.sanitizer.sanitize(SecurityContext.RESOURCE_URL, url); } generateVideoThumbnail(item, index) { const video = document.createElement('video'); video.src = item.source; video.crossOrigin = 'anonymous'; video.load(); video.addEventListener('loadeddata', () => { video.currentTime = 2; }); video.addEventListener('seeked', () => { const canvas = document.createElement('canvas'); canvas.width = video.videoWidth / 4; canvas.height = video.videoHeight / 4; const ctx = canvas.getContext('2d'); if (ctx) { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); item.thumbnail = this.sanitizeUrl(canvas.toDataURL('image/jpeg')); } }); } captureVideoThumbnail(event, index) { const videoElement = event.target; const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); if (context) { // Set canvas dimensions canvas.width = videoElement.videoWidth; canvas.height = videoElement.videoHeight; videoElement.currentTime = 0; videoElement.addEventListener('loadeddata', () => { context.drawImage(videoElement, 0, 0, canvas.width, canvas.height); const thumbnailDataUrl = canvas.toDataURL('image/jpeg'); this.mediaItems[index].thumbnail = thumbnailDataUrl; }); } } openLightbox(item, index) { this.selectedItem = item; this.selectedIndex = index; this.lightboxOpen = true; } closeLightbox(event) { if (event?.target === event?.currentTarget) { this.lightboxOpen = false; this.selectedItem = undefined; } } // Navigation functions prev() { if (this.selectedIndex > 0) { this.selectedIndex--; this.selectedItem = this.mediaItems[this.selectedIndex]; } } next() { if (this.selectedIndex < this.mediaItems.length - 1) { this.selectedIndex++; this.selectedItem = this.mediaItems[this.selectedIndex]; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NgFlyweightGalleryComponent, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.6", type: NgFlyweightGalleryComponent, isStandalone: false, selector: "ng-flyweight-gallery", inputs: { mediaItems: "mediaItems" }, ngImport: i0, template: ` <div class="fwt-gallery-grid"> <div class="fwt-gallery-item" *ngFor="let item of mediaItems; index as i" (click)="openLightbox(item, i)"> <!-- Image thumbnail --> <img (click)="openLightbox(item, i)" *ngIf="item.type === 'image'" [src]="item?.thumbnail || item.source" [alt]="item.title"> <!-- Video thumbnail --> <video (click)="openLightbox(item, i)" *ngIf="item.type === 'video' && !item?.thumbnail" [src]="item.source" (loadedmetadata)="captureVideoThumbnail($event, i)" muted ></video> <img (click)="openLightbox(item, i)" *ngIf="item.type === 'video' && item?.thumbnail" [src]="item.thumbnail" [alt]="item.title"> </div> </div> <div class="fwt-lightbox" *ngIf="lightboxOpen"> <div class="fwt-lightbox-backdrop"></div> <button class="fwt-nav-btn fwt-prev" (click)="prev()">&#10094;</button> <button class="fwt-nav-btn fwt-next" (click)="next()">&#10095;</button> <div class="fwt-lightbox-content"> <div class="fwt-media-container" *ngIf="selectedItem"> <img *ngIf="selectedItem.type === 'image'" [src]="selectedItem.source" [alt]="selectedItem.title"> <video *ngIf="selectedItem.type === 'video'" [src]="selectedItem.source" [autoplay]="selectedItem.autoplay" [loop]="selectedItem.loop" controls> </video> </div> </div> <button class="fwt-close-btn" (click)="closeLightbox()">&times;</button> </div> `, isInline: true, styles: [".fwt-gallery-grid{background-color:transparent;display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;max-height:450px;overflow-y:auto;padding-right:10px;scrollbar-width:thin;scrollbar-color:#888 #f1f1f1}.fwt-gallery-grid ::-webkit-scrollbar{width:8px}.fwt-gallery-grid ::-webkit-scrollbar-thumb{background-color:#888;border-radius:4px;border:2px solid transparent;background-clip:content-box}.fwt-gallery-grid ::-webkit-scrollbar-thumb:hover{background-color:#555}.fwt-gallery-grid ::-webkit-scrollbar-track{background-color:#f1f1f1;border-radius:4px}@media (max-width: 768px){.fwt-gallery-grid{grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}}@media (max-width: 480px){.fwt-gallery-grid{grid-template-columns:1fr}}.fwt-gallery-item{cursor:pointer;transition:transform .3s;border:2px solid lightsteelblue;border-radius:5px;object-fit:cover;-webkit-object-fit:cover;-moz-object-fit:cover}.fwt-gallery-item:hover{transform:scale(1.05);-webkit-transform:scale(1.05);-ms-transform:scale(1.05)}.fwt-gallery-item :is(img,video){width:100%;height:100%}.fwt-lightbox{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:1000}.fwt-lightbox .fwt-lightbox-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background:#000c;z-index:999}.fwt-lightbox .fwt-lightbox-content{position:relative;max-width:70%;max-height:70%;display:flex;justify-content:center;align-items:center;text-align:center;z-index:1001}.fwt-lightbox .fwt-lightbox-content .fwt-media-container :is(img,video){width:100%;height:auto}@media (max-width: 768px){.fwt-lightbox .fwt-lightbox-content{max-width:90%;max-height:90%}}@media (max-width: 480px){.fwt-lightbox .fwt-lightbox-content{max-width:95%;max-height:95%}}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn){position:absolute;top:50%;transform:translateY(-50%);-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);background:transparent;border:none;color:#fff;font-size:2rem;cursor:pointer;z-index:1002}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn).fwt-prev{left:10px}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn).fwt-next{right:10px}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn).fwt-close-btn{top:10px;right:10px;transform:none}@media (max-width: 480px){.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn){font-size:1.5rem}}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NgFlyweightGalleryComponent, decorators: [{ type: Component, args: [{ selector: 'ng-flyweight-gallery', standalone: false, template: ` <div class="fwt-gallery-grid"> <div class="fwt-gallery-item" *ngFor="let item of mediaItems; index as i" (click)="openLightbox(item, i)"> <!-- Image thumbnail --> <img (click)="openLightbox(item, i)" *ngIf="item.type === 'image'" [src]="item?.thumbnail || item.source" [alt]="item.title"> <!-- Video thumbnail --> <video (click)="openLightbox(item, i)" *ngIf="item.type === 'video' && !item?.thumbnail" [src]="item.source" (loadedmetadata)="captureVideoThumbnail($event, i)" muted ></video> <img (click)="openLightbox(item, i)" *ngIf="item.type === 'video' && item?.thumbnail" [src]="item.thumbnail" [alt]="item.title"> </div> </div> <div class="fwt-lightbox" *ngIf="lightboxOpen"> <div class="fwt-lightbox-backdrop"></div> <button class="fwt-nav-btn fwt-prev" (click)="prev()">&#10094;</button> <button class="fwt-nav-btn fwt-next" (click)="next()">&#10095;</button> <div class="fwt-lightbox-content"> <div class="fwt-media-container" *ngIf="selectedItem"> <img *ngIf="selectedItem.type === 'image'" [src]="selectedItem.source" [alt]="selectedItem.title"> <video *ngIf="selectedItem.type === 'video'" [src]="selectedItem.source" [autoplay]="selectedItem.autoplay" [loop]="selectedItem.loop" controls> </video> </div> </div> <button class="fwt-close-btn" (click)="closeLightbox()">&times;</button> </div> `, styles: [".fwt-gallery-grid{background-color:transparent;display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;max-height:450px;overflow-y:auto;padding-right:10px;scrollbar-width:thin;scrollbar-color:#888 #f1f1f1}.fwt-gallery-grid ::-webkit-scrollbar{width:8px}.fwt-gallery-grid ::-webkit-scrollbar-thumb{background-color:#888;border-radius:4px;border:2px solid transparent;background-clip:content-box}.fwt-gallery-grid ::-webkit-scrollbar-thumb:hover{background-color:#555}.fwt-gallery-grid ::-webkit-scrollbar-track{background-color:#f1f1f1;border-radius:4px}@media (max-width: 768px){.fwt-gallery-grid{grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}}@media (max-width: 480px){.fwt-gallery-grid{grid-template-columns:1fr}}.fwt-gallery-item{cursor:pointer;transition:transform .3s;border:2px solid lightsteelblue;border-radius:5px;object-fit:cover;-webkit-object-fit:cover;-moz-object-fit:cover}.fwt-gallery-item:hover{transform:scale(1.05);-webkit-transform:scale(1.05);-ms-transform:scale(1.05)}.fwt-gallery-item :is(img,video){width:100%;height:100%}.fwt-lightbox{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:1000}.fwt-lightbox .fwt-lightbox-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background:#000c;z-index:999}.fwt-lightbox .fwt-lightbox-content{position:relative;max-width:70%;max-height:70%;display:flex;justify-content:center;align-items:center;text-align:center;z-index:1001}.fwt-lightbox .fwt-lightbox-content .fwt-media-container :is(img,video){width:100%;height:auto}@media (max-width: 768px){.fwt-lightbox .fwt-lightbox-content{max-width:90%;max-height:90%}}@media (max-width: 480px){.fwt-lightbox .fwt-lightbox-content{max-width:95%;max-height:95%}}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn){position:absolute;top:50%;transform:translateY(-50%);-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);background:transparent;border:none;color:#fff;font-size:2rem;cursor:pointer;z-index:1002}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn).fwt-prev{left:10px}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn).fwt-next{right:10px}.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn).fwt-close-btn{top:10px;right:10px;transform:none}@media (max-width: 480px){.fwt-lightbox :is(.fwt-nav-btn,.fwt-close-btn){font-size:1.5rem}}\n"] }] }], ctorParameters: () => [{ type: i1.DomSanitizer }], propDecorators: { mediaItems: [{ type: Input }] } }); class NgFlyweightGalleryModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NgFlyweightGalleryModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.6", ngImport: i0, type: NgFlyweightGalleryModule, declarations: [NgFlyweightGalleryComponent], imports: [CommonModule], exports: [NgFlyweightGalleryComponent] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NgFlyweightGalleryModule, imports: [CommonModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.6", ngImport: i0, type: NgFlyweightGalleryModule, decorators: [{ type: NgModule, args: [{ declarations: [NgFlyweightGalleryComponent], imports: [CommonModule], exports: [NgFlyweightGalleryComponent], }] }] }); /* * Public API Surface of ng-flyweight-gallery */ /** * Generated bundle index. Do not edit. */ export { NgFlyweightGalleryComponent, NgFlyweightGalleryModule }; //# sourceMappingURL=ng-flyweight-gallery.mjs.map