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.

1 lines 14.7 kB
{"version":3,"file":"ng-flyweight-gallery.mjs","sources":["../../../projects/ng-flyweight-gallery/src/lib/ng-flyweight-gallery.component.ts","../../../projects/ng-flyweight-gallery/src/lib/ng-flyweight-gallery.module.ts","../../../projects/ng-flyweight-gallery/src/public-api.ts","../../../projects/ng-flyweight-gallery/src/ng-flyweight-gallery.ts"],"sourcesContent":["import { Component, Input, SecurityContext } from '@angular/core';\r\nimport { DomSanitizer } from '@angular/platform-browser';\r\n\r\n\r\nexport interface MediaItem {\r\n id?: number; // optional ID\r\n type: 'image' | 'video'; // media type\r\n thumbnail?: string; // optional thumbnail URL\r\n source: string; // media source (URL)\r\n title?: string; // optional title\r\n autoplay?: boolean; // optional autoplay for videos\r\n loop?: boolean; // optional loop for videos\r\n}\r\n\r\n@Component({\r\n selector: 'ng-flyweight-gallery',\r\n standalone: false,\r\n template: `\r\n <div class=\"fwt-gallery-grid\">\r\n <div class=\"fwt-gallery-item\" *ngFor=\"let item of mediaItems; index as i\" (click)=\"openLightbox(item, i)\">\r\n <!-- Image thumbnail -->\r\n <img (click)=\"openLightbox(item, i)\" *ngIf=\"item.type === 'image'\" [src]=\"item?.thumbnail || item.source\" [alt]=\"item.title\">\r\n\r\n <!-- Video thumbnail -->\r\n <video \r\n (click)=\"openLightbox(item, i)\" \r\n *ngIf=\"item.type === 'video' && !item?.thumbnail\" \r\n [src]=\"item.source\" \r\n (loadedmetadata)=\"captureVideoThumbnail($event, i)\" \r\n muted \r\n ></video>\r\n \r\n <img (click)=\"openLightbox(item, i)\" *ngIf=\"item.type === 'video' && item?.thumbnail\" [src]=\"item.thumbnail\" [alt]=\"item.title\">\r\n </div>\r\n</div>\r\n\r\n<div class=\"fwt-lightbox\" *ngIf=\"lightboxOpen\">\r\n <div class=\"fwt-lightbox-backdrop\"></div>\r\n <button class=\"fwt-nav-btn fwt-prev\" (click)=\"prev()\">&#10094;</button>\r\n <button class=\"fwt-nav-btn fwt-next\" (click)=\"next()\">&#10095;</button>\r\n <div class=\"fwt-lightbox-content\">\r\n <div class=\"fwt-media-container\" *ngIf=\"selectedItem\">\r\n <img *ngIf=\"selectedItem.type === 'image'\" [src]=\"selectedItem.source\" [alt]=\"selectedItem.title\">\r\n <video *ngIf=\"selectedItem.type === 'video'\" [src]=\"selectedItem.source\" [autoplay]=\"selectedItem.autoplay\"\r\n [loop]=\"selectedItem.loop\" controls>\r\n </video>\r\n </div>\r\n </div>\r\n <button class=\"fwt-close-btn\" (click)=\"closeLightbox()\">&times;</button>\r\n</div>\r\n `,\r\n styles: `.fwt-gallery-grid {\r\n background-color: transparent;\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));\r\n gap: 10px;\r\n max-height: 450px; /* Set max height */\r\n overflow-y: auto; /* Enable vertical scroll if content exceeds max-height */\r\n padding-right: 10px; /* Add padding to avoid overlap with scrollbar */\r\n \r\n /* Scrollbar styling for Webkit browsers */\r\n ::-webkit-scrollbar {\r\n width: 8px;\r\n }\r\n \r\n ::-webkit-scrollbar-thumb {\r\n background-color: #888;\r\n border-radius: 4px;\r\n border: 2px solid transparent;\r\n background-clip: content-box;\r\n }\r\n\r\n ::-webkit-scrollbar-thumb:hover {\r\n background-color: #555;\r\n }\r\n\r\n ::-webkit-scrollbar-track {\r\n background-color: #f1f1f1;\r\n border-radius: 4px;\r\n }\r\n\r\n /* Firefox scrollbar styling */\r\n scrollbar-width: thin;\r\n scrollbar-color: #888 #f1f1f1;\r\n\r\n @media (max-width: 768px) {\r\n grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));\r\n }\r\n\r\n @media (max-width: 480px) {\r\n grid-template-columns: 1fr;\r\n }\r\n}\r\n\r\n.fwt-gallery-item {\r\n cursor: pointer;\r\n transition: transform 0.3s;\r\n border: 2px solid lightsteelblue;\r\n border-radius: 5px;\r\n object-fit: cover;\r\n\r\n /* Fallback for browsers that don't support object-fit */\r\n -webkit-object-fit: cover;\r\n -moz-object-fit: cover;\r\n\r\n &:hover {\r\n transform: scale(1.05);\r\n -webkit-transform: scale(1.05); /* Safari */\r\n -ms-transform: scale(1.05); /* IE */\r\n }\r\n\r\n img, video {\r\n width: 100%;\r\n height: 100%;\r\n }\r\n}\r\n\r\n.fwt-lightbox {\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n z-index: 1000;\r\n\r\n .fwt-lightbox-backdrop {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background: rgba(0, 0, 0, 0.8);\r\n z-index: 999;\r\n }\r\n\r\n .fwt-lightbox-content {\r\n position: relative;\r\n max-width: 70%;\r\n max-height: 70%;\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n text-align: center;\r\n z-index: 1001;\r\n\r\n .fwt-media-container {\r\n img,\r\n video {\r\n width: 100%;\r\n height: auto;\r\n }\r\n }\r\n\r\n @media (max-width: 768px) {\r\n max-width: 90%;\r\n max-height: 90%;\r\n }\r\n\r\n @media (max-width: 480px) {\r\n max-width: 95%;\r\n max-height: 95%;\r\n }\r\n }\r\n\r\n .fwt-nav-btn,\r\n .fwt-close-btn {\r\n position: absolute;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n -webkit-transform: translateY(-50%); /* Safari */\r\n -ms-transform: translateY(-50%); /* IE */\r\n background: transparent;\r\n border: none;\r\n color: white;\r\n font-size: 2rem;\r\n cursor: pointer;\r\n z-index: 1002;\r\n\r\n &.fwt-prev {\r\n left: 10px;\r\n }\r\n\r\n &.fwt-next {\r\n right: 10px;\r\n }\r\n\r\n &.fwt-close-btn {\r\n top: 10px;\r\n right: 10px;\r\n transform: none;\r\n }\r\n\r\n @media (max-width: 480px) {\r\n font-size: 1.5rem;\r\n }\r\n }\r\n}\r\n`\r\n})\r\nexport class NgFlyweightGalleryComponent {\r\n @Input() mediaItems: MediaItem[] = [];\r\n selectedItem?: MediaItem;\r\n lightboxOpen = false;\r\n selectedIndex: number = 0;\r\n\r\n constructor(private sanitizer: DomSanitizer) { }\r\n\r\n ngOnInit() {\r\n this.mediaItems.forEach((item, index) => {\r\n item.source = this.sanitizeUrl(item.source);\r\n if (!item.thumbnail) {\r\n if (item.type === 'image') {\r\n item.thumbnail = this.sanitizeUrl(item.source);\r\n } else if (item.type === 'video') {\r\n this.generateVideoThumbnail(item, index);\r\n }\r\n } else {\r\n item.thumbnail = this.sanitizeUrl(item.thumbnail);\r\n }\r\n });\r\n }\r\n\r\n sanitizeUrl(url: string): string {\r\n return this.sanitizer.sanitize(SecurityContext.RESOURCE_URL,url) as string;\r\n }\r\n\r\n generateVideoThumbnail(item: MediaItem, index: number) {\r\n const video = document.createElement('video');\r\n video.src = item.source as string;\r\n video.crossOrigin = 'anonymous';\r\n video.load();\r\n\r\n\r\n video.addEventListener('loadeddata', () => {\r\n video.currentTime = 2;\r\n });\r\n\r\n\r\n video.addEventListener('seeked', () => {\r\n const canvas = document.createElement('canvas');\r\n canvas.width = video.videoWidth / 4;\r\n canvas.height = video.videoHeight / 4;\r\n const ctx = canvas.getContext('2d');\r\n if (ctx) {\r\n ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\r\n item.thumbnail = this.sanitizeUrl(canvas.toDataURL('image/jpeg')) as string;\r\n }\r\n });\r\n }\r\n\r\n captureVideoThumbnail(event: Event, index: number) {\r\n const videoElement = event.target as HTMLVideoElement;\r\n const canvas = document.createElement('canvas');\r\n const context = canvas.getContext('2d');\r\n\r\n if (context) {\r\n // Set canvas dimensions\r\n canvas.width = videoElement.videoWidth;\r\n canvas.height = videoElement.videoHeight;\r\n videoElement.currentTime = 0;\r\n videoElement.addEventListener('loadeddata', () => {\r\n context.drawImage(videoElement, 0, 0, canvas.width, canvas.height);\r\n const thumbnailDataUrl = canvas.toDataURL('image/jpeg');\r\n this.mediaItems[index].thumbnail = thumbnailDataUrl;\r\n });\r\n }\r\n }\r\n\r\n openLightbox(item: MediaItem, index: number) {\r\n this.selectedItem = item;\r\n this.selectedIndex = index;\r\n this.lightboxOpen = true;\r\n }\r\n\r\n closeLightbox(event?: any) {\r\n if (event?.target === event?.currentTarget) {\r\n this.lightboxOpen = false;\r\n this.selectedItem = undefined;\r\n }\r\n }\r\n\r\n // Navigation functions\r\n prev() {\r\n if (this.selectedIndex > 0) {\r\n this.selectedIndex--;\r\n this.selectedItem = this.mediaItems[this.selectedIndex];\r\n }\r\n }\r\n\r\n next() {\r\n if (this.selectedIndex < this.mediaItems.length - 1) {\r\n this.selectedIndex++;\r\n this.selectedItem = this.mediaItems[this.selectedIndex];\r\n }\r\n }\r\n}\r\n","import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { NgFlyweightGalleryComponent } from './ng-flyweight-gallery.component';\r\n\r\n\r\n@NgModule({\r\n declarations: [NgFlyweightGalleryComponent],\r\n imports: [CommonModule],\r\n exports: [NgFlyweightGalleryComponent],\r\n})\r\nexport class NgFlyweightGalleryModule {}\r\n","/*\r\n * Public API Surface of ng-flyweight-gallery\r\n */\r\n\r\nexport * from './lib/ng-flyweight-gallery.module';\r\nexport * from './lib/ng-flyweight-gallery.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}