@vinlos/ngx-gallery
Version:
A simple responsive native gallery component for Angular 18.
1 lines • 145 kB
Source Map (JSON)
{"version":3,"file":"vinlos-ngx-gallery.mjs","sources":["../../../projects/gallery/src/lib/ngx-gallery.service.ts","../../../projects/gallery/src/lib/ngx-gallery-arrows/ngx-gallery-arrows.component.ts","../../../projects/gallery/src/lib/ngx-gallery-arrows/ngx-gallery-arrows.component.html","../../../projects/gallery/src/lib/ngx-gallery-action/ngx-gallery-action.component.ts","../../../projects/gallery/src/lib/ngx-gallery-action/ngx-gallery-action.component.html","../../../projects/gallery/src/lib/ngx-gallery-bullets/ngx-gallery-bullets.component.ts","../../../projects/gallery/src/lib/ngx-gallery-bullets/ngx-gallery-bullets.component.html","../../../projects/gallery/src/lib/ngx-gallery-preview/ngx-gallery-preview.component.ts","../../../projects/gallery/src/lib/ngx-gallery-preview/ngx-gallery-preview.component.html","../../../projects/gallery/src/lib/ngx-gallery-animation.ts","../../../projects/gallery/src/lib/ngx-gallery-image/ngx-gallery-image.component.ts","../../../projects/gallery/src/lib/ngx-gallery-image/ngx-gallery-image.component.html","../../../projects/gallery/src/lib/ngx-gallery-order.ts","../../../projects/gallery/src/lib/ngx-gallery-thumbnails/ngx-gallery-thumbnails.component.ts","../../../projects/gallery/src/lib/ngx-gallery-thumbnails/ngx-gallery-thumbnails.component.html","../../../projects/gallery/src/lib/ngx-gallery-action.ts","../../../projects/gallery/src/lib/ngx-gallery-layout.ts","../../../projects/gallery/src/lib/ngx-gallery-image-size.ts","../../../projects/gallery/src/lib/ngx-gallery-options.ts","../../../projects/gallery/src/lib/ngx-gallery-ordered-image.ts","../../../projects/gallery/src/lib/ngx-gallery.component.ts","../../../projects/gallery/src/lib/ngx-gallery.component.html","../../../projects/gallery/src/lib/ngx-gallery.module.ts","../../../projects/gallery/src/lib/ngx-gallery-image.ts","../../../projects/gallery/src/public-api.ts","../../../projects/gallery/src/vinlos-ngx-gallery.ts"],"sourcesContent":["import { ElementRef, Injectable, Renderer2, inject } from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class NgxGalleryService {\n private renderer = inject(Renderer2);\n\n private swipeHandlers: Map<string, (() => void)[]> = new Map<string, (() => void)[]>();\n\n manageSwipe(status: boolean, element: ElementRef, id: string, nextHandler: () => void, prevHandler: () => void): void {\n\n const handlers = this.getSwipeHandlers(id);\n\n // swipeleft and swiperight are available only if hammerjs is included\n try {\n if (status && !handlers) {\n this.swipeHandlers.set(id, [\n this.renderer.listen(element.nativeElement, 'swipeleft', () => nextHandler()),\n this.renderer.listen(element.nativeElement, 'swiperight', () => prevHandler())\n ]);\n } else if (!status && handlers) {\n handlers.map((handler) => handler());\n this.removeSwipeHandlers(id);\n }\n } catch (e) {\n }\n }\n\n validateUrl(url: string): string {\n if (url.replace) {\n return url.replace(new RegExp(' ', 'g'), '%20')\n .replace(new RegExp('\\'', 'g'), '%27');\n } else {\n return url;\n }\n }\n\n getBackgroundUrl(image: string) {\n return 'url(\\'' + this.validateUrl(image) + '\\')';\n }\n\n getFileType (fileSource: string): string {\n const fileExtension = fileSource.split('.').pop().toLowerCase();\n if (fileExtension === 'avi' || fileExtension === 'flv'\n || fileExtension === 'wmv' || fileExtension === 'mov'\n || fileExtension === 'mp4') {\n return 'video';\n }\n return 'image';\n}\n\n private getSwipeHandlers(id: string): (() => void)[] | undefined {\n return this.swipeHandlers.get(id);\n }\n\n private removeSwipeHandlers(id: string): void {\n this.swipeHandlers.delete(id);\n }\n}\n","import {ChangeDetectionStrategy, Component, OnInit, input, output} from '@angular/core';\n\n@Component({\n selector: 'ngx-gallery-arrows',\n templateUrl: './ngx-gallery-arrows.component.html',\n styleUrls: ['./ngx-gallery-arrows.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class NgxGalleryArrowsComponent {\n readonly prevDisabled = input<boolean>(undefined);\n readonly nextDisabled = input<boolean>(undefined);\n readonly arrowPrevIcon = input<string>(undefined);\n readonly arrowNextIcon = input<string>(undefined);\n\n readonly prevClick = output();\n readonly nextClick = output();\n\n constructor() { }\n\n handlePrevClick(): void {\n this.prevClick.emit();\n }\n\n handleNextClick(): void {\n this.nextClick.emit();\n }\n}\n","<div class=\"ngx-gallery-arrows-wrapper ngx-gallery-arrow-left\">\n <div class=\"ngx-gallery-icon ngx-gallery-arrow\" aria-hidden=\"true\" (click)=\"handlePrevClick()\" [class.ngx-gallery-disabled]=\"prevDisabled()\">\n <i class=\"ngx-gallery-icon-content {{arrowPrevIcon()}}\"></i>\n </div>\n</div>\n<div class=\"ngx-gallery-arrows-wrapper ngx-gallery-arrow-right\">\n <div class=\"ngx-gallery-icon ngx-gallery-arrow\" aria-hidden=\"true\" (click)=\"handleNextClick()\" [class.ngx-gallery-disabled]=\"nextDisabled()\">\n <i class=\"ngx-gallery-icon-content {{arrowNextIcon()}}\"></i>\n </div>\n</div>\n","import {ChangeDetectionStrategy, Component, OnInit, input, output} from '@angular/core';\n\n@Component({\n selector: 'ngx-gallery-action',\n templateUrl: './ngx-gallery-action.component.html',\n styleUrls: ['./ngx-gallery-action.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class NgxGalleryActionComponent {\n readonly icon = input<string>(undefined);\n readonly disabled = input(false);\n readonly titleText = input('');\n\n readonly closeClick = output<Event>();\n\n constructor() {\n }\n\n handleClick(event: Event) {\n if (!this.disabled()) {\n this.closeClick.emit(event);\n }\n\n event.stopPropagation();\n event.preventDefault();\n }\n}\n","<div class=\"ngx-gallery-icon\" [class.ngx-gallery-icon-disabled]=\"disabled()\"\n aria-hidden=\"true\"\n title=\"{{ titleText() }}\"\n (click)=\"handleClick($event)\">\n <i class=\"ngx-gallery-icon-content {{ icon() }}\"></i>\n</div>\n","import {ChangeDetectionStrategy, Component, OnInit, input, output} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\n@Component({\n selector: 'ngx-gallery-bullets',\n templateUrl: './ngx-gallery-bullets.component.html',\n styleUrls: ['./ngx-gallery-bullets.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgClass]\n})\nexport class NgxGalleryBulletsComponent {\n readonly count = input<number>(undefined);\n readonly active = input(0);\n\n readonly bulletChange = output<number>();\n\n constructor() { }\n\n getBullets(): number[] {\n return Array(this.count());\n }\n\n handleChange(event: Event, index: number): void {\n this.bulletChange.emit(index);\n }\n}\n","@for (bullet of getBullets(); track bullet; let i = $index) {\n <div class=\"ngx-gallery-bullet\" (click)=\"handleChange($event, i)\"\n [ngClass]=\"{ 'ngx-gallery-active': i === active() }\"></div>\n}\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, Renderer2, SimpleChanges, inject, input, viewChild, output } from '@angular/core';\nimport {DomSanitizer, SafeResourceUrl, SafeStyle, SafeUrl} from '@angular/platform-browser';\nimport {NgxGalleryService} from '../ngx-gallery.service';\nimport {NgxGalleryAction} from '../ngx-gallery-action';\nimport { NgxGalleryArrowsComponent } from '../ngx-gallery-arrows/ngx-gallery-arrows.component';\nimport { NgxGalleryActionComponent } from '../ngx-gallery-action/ngx-gallery-action.component';\nimport { NgxGalleryBulletsComponent } from '../ngx-gallery-bullets/ngx-gallery-bullets.component';\n\n\n@Component({\n selector: 'ngx-gallery-preview',\n templateUrl: './ngx-gallery-preview.component.html',\n styleUrls: ['./ngx-gallery-preview.component.scss'],\n // encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgxGalleryArrowsComponent, NgxGalleryActionComponent, NgxGalleryBulletsComponent]\n})\nexport class NgxGalleryPreviewComponent implements OnInit, OnDestroy, OnChanges {\n private sanitization = inject(DomSanitizer);\n private elementRef = inject(ElementRef);\n private helperService = inject(NgxGalleryService);\n private renderer = inject(Renderer2);\n private changeDetectorRef = inject(ChangeDetectorRef);\n\n src: SafeUrl;\n srcIndex: number;\n description: string;\n type: string;\n showSpinner = false;\n positionLeft = 0;\n positionTop = 0;\n zoomValue = 1;\n loading = false;\n rotateValue = 0;\n index = 0;\n\n readonly images = input<string[] | SafeResourceUrl[]>(undefined);\n readonly descriptions = input<string[]>(undefined);\n readonly showDescription = input<boolean>(undefined);\n @Input() arrows: boolean;\n readonly arrowsAutoHide = input<boolean>(undefined);\n readonly swipe = input<boolean>(undefined);\n readonly fullscreen = input<boolean>(undefined);\n readonly forceFullscreen = input<boolean>(undefined);\n readonly closeOnClick = input<boolean>(undefined);\n readonly closeOnEsc = input<boolean>(undefined);\n readonly keyboardNavigation = input<boolean>(undefined);\n readonly arrowPrevIcon = input<string>(undefined);\n readonly arrowNextIcon = input<string>(undefined);\n readonly closeIcon = input<string>(undefined);\n readonly fullscreenIcon = input<string>(undefined);\n readonly spinnerIcon = input<string>(undefined);\n readonly autoPlay = input<boolean>(undefined);\n readonly autoPlayInterval = input<number>(undefined);\n readonly autoPlayPauseOnHover = input<boolean>(undefined);\n readonly infinityMove = input<boolean>(undefined);\n readonly zoom = input<boolean>(undefined);\n readonly zoomStep = input<number>(undefined);\n readonly zoomMax = input<number>(undefined);\n readonly zoomMin = input<number>(undefined);\n readonly zoomInIcon = input<string>(undefined);\n readonly zoomOutIcon = input<string>(undefined);\n readonly animation = input<boolean>(undefined);\n readonly actions = input<NgxGalleryAction[]>(undefined);\n readonly rotate = input<boolean>(undefined);\n readonly rotateLeftIcon = input<string>(undefined);\n readonly rotateRightIcon = input<string>(undefined);\n readonly download = input<boolean>(undefined);\n readonly downloadIcon = input<string>(undefined);\n readonly bullets = input<boolean>(undefined);\n\n readonly previewOpen = output();\n readonly previewClose = output();\n readonly activeChange = output<number>();\n\n readonly previewImage = viewChild<any>('previewImage');\n\n private isOpen = false;\n private timer;\n private initialX = 0;\n private initialY = 0;\n private initialLeft = 0;\n private initialTop = 0;\n private isMove = false;\n\n private keyDownListener: () => void;\n\n ngOnInit() {\n if (this.arrows && this.arrowsAutoHide()) {\n this.arrows = false;\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['swipe']) {\n this.helperService.manageSwipe(this.swipe(), this.elementRef,\n 'preview', () => this.showNext(), () => this.showPrev());\n }\n }\n\n ngOnDestroy() {\n if (this.keyDownListener) {\n this.keyDownListener();\n }\n }\n\n @HostListener('mouseenter') onMouseEnter() {\n if (this.arrowsAutoHide() && !this.arrows) {\n this.arrows = true;\n }\n }\n\n @HostListener('mouseleave') onMouseLeave() {\n if (this.arrowsAutoHide() && this.arrows) {\n this.arrows = false;\n }\n }\n\n onKeyDown(e) {\n if (this.isOpen) {\n if (this.keyboardNavigation()) {\n if (this.isKeyboardPrev(e)) {\n this.showPrev();\n } else if (this.isKeyboardNext(e)) {\n this.showNext();\n }\n }\n if (this.closeOnEsc() && this.isKeyboardEsc(e)) {\n this.close();\n }\n }\n }\n\n open(index: number): void {\n this.previewOpen.emit();\n\n this.index = index;\n this.isOpen = true;\n this.show(true);\n\n if (this.forceFullscreen()) {\n this.manageFullscreen();\n }\n\n this.keyDownListener = this.renderer.listen('window', 'keydown', (e) => this.onKeyDown(e));\n }\n\n close(): void {\n this.isOpen = false;\n const video = this.previewImage().nativeElement;\n if (\n video.currentTime > 0 &&\n !video.paused &&\n !video.ended &&\n video.readyState > 2\n ) {\n video.pause();\n }\n this.closeFullscreen();\n this.previewClose.emit();\n\n this.stopAutoPlay();\n\n if (this.keyDownListener) {\n this.keyDownListener();\n }\n }\n\n imageMouseEnter(): void {\n if (this.autoPlay() && this.autoPlayPauseOnHover()) {\n this.stopAutoPlay();\n }\n }\n\n imageMouseLeave(): void {\n if (this.autoPlay() && this.autoPlayPauseOnHover()) {\n this.startAutoPlay();\n }\n }\n\n startAutoPlay(): void {\n if (this.autoPlay()) {\n this.stopAutoPlay();\n\n this.timer = setTimeout(() => {\n if (!this.showNext()) {\n this.index = -1;\n this.showNext();\n }\n }, this.autoPlayInterval());\n }\n }\n\n stopAutoPlay(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n }\n }\n\n showAtIndex(index: number): void {\n this.index = index;\n this.show();\n }\n\n showNext(): boolean {\n if (this.canShowNext()) {\n this.index++;\n\n if (this.index === this.images().length) {\n this.index = 0;\n }\n\n this.show();\n return true;\n } else {\n return false;\n }\n }\n\n showPrev(): void {\n if (this.canShowPrev()) {\n this.index--;\n\n if (this.index < 0) {\n this.index = this.images().length - 1;\n }\n\n this.show();\n }\n }\n\n canShowNext(): boolean {\n const images = this.images();\n if (this.loading) {\n return false;\n } else if (images) {\n return this.infinityMove() || this.index < images.length - 1;\n } else {\n return false;\n }\n }\n\n canShowPrev(): boolean {\n if (this.loading) {\n return false;\n } else if (this.images()) {\n return this.infinityMove() || this.index > 0;\n } else {\n return false;\n }\n }\n\n manageFullscreen(): void {\n if (this.fullscreen() || this.forceFullscreen()) {\n const doc = document as any;\n\n if (!doc.fullscreenElement && !doc.mozFullScreenElement\n && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {\n this.openFullscreen();\n } else {\n this.closeFullscreen();\n }\n }\n }\n\n getSafeUrl(image: string): SafeUrl {\n return this.sanitization.bypassSecurityTrustUrl(image);\n }\n\n getFileType(fileSource: string): string {\n return this.helperService.getFileType(fileSource);\n }\n\n zoomIn(): void {\n if (this.canZoomIn()) {\n this.zoomValue += this.zoomStep();\n\n if (this.zoomValue > this.zoomMax()) {\n this.zoomValue = this.zoomMax();\n }\n }\n }\n\n zoomOut(): void {\n if (this.canZoomOut()) {\n this.zoomValue -= this.zoomStep();\n\n if (this.zoomValue < this.zoomMin()) {\n this.zoomValue = this.zoomMin();\n }\n\n if (this.zoomValue <= 1) {\n this.resetPosition();\n }\n }\n }\n\n rotateLeft(): void {\n this.rotateValue -= 90;\n }\n\n rotateRight(): void {\n this.rotateValue += 90;\n }\n\n getTransform(): SafeStyle {\n return this.sanitization.bypassSecurityTrustStyle('scale(' + this.zoomValue + ') rotate(' + this.rotateValue + 'deg)');\n }\n\n canZoomIn(): boolean {\n return this.zoomValue < this.zoomMax();\n }\n\n canZoomOut(): boolean {\n return this.zoomValue > this.zoomMin();\n }\n\n canDragOnZoom() {\n return this.zoom() && this.zoomValue > 1;\n }\n\n mouseDownHandler(e): void {\n if (this.canDragOnZoom()) {\n this.initialX = this.getClientX(e);\n this.initialY = this.getClientY(e);\n this.initialLeft = this.positionLeft;\n this.initialTop = this.positionTop;\n this.isMove = true;\n\n e.preventDefault();\n }\n }\n\n mouseUpHandler(e): void {\n this.isMove = false;\n }\n\n mouseMoveHandler(e) {\n if (this.isMove) {\n this.positionLeft = this.initialLeft + (this.getClientX(e) - this.initialX);\n this.positionTop = this.initialTop + (this.getClientY(e) - this.initialY);\n }\n }\n\n private getClientX(e): number {\n return e.touches && e.touches.length ? e.touches[0].clientX : e.clientX;\n }\n\n private getClientY(e): number {\n return e.touches && e.touches.length ? e.touches[0].clientY : e.clientY;\n }\n\n private resetPosition() {\n if (this.zoom()) {\n this.positionLeft = 0;\n this.positionTop = 0;\n }\n }\n\n private isKeyboardNext(e): boolean {\n return e.keyCode === 39;\n }\n\n private isKeyboardPrev(e): boolean {\n return e.keyCode === 37;\n }\n\n private isKeyboardEsc(e): boolean {\n return e.keyCode === 27;\n }\n\n private openFullscreen(): void {\n const element = document.documentElement as any;\n\n if (element.requestFullscreen) {\n element.requestFullscreen();\n } else if (element.msRequestFullscreen) {\n element.msRequestFullscreen();\n } else if (element.mozRequestFullScreen) {\n element.mozRequestFullScreen();\n } else if (element.webkitRequestFullscreen) {\n element.webkitRequestFullscreen();\n }\n }\n\n private closeFullscreen(): void {\n if (this.isFullscreen()) {\n const doc = document as any;\n\n if (doc.exitFullscreen) {\n doc.exitFullscreen();\n } else if (doc.msExitFullscreen) {\n doc.msExitFullscreen();\n } else if (doc.mozCancelFullScreen) {\n doc.mozCancelFullScreen();\n } else if (doc.webkitExitFullscreen) {\n doc.webkitExitFullscreen();\n }\n }\n }\n\n private isFullscreen() {\n const doc = document as any;\n\n return doc.fullscreenElement || doc.webkitFullscreenElement\n || doc.mozFullScreenElement || doc.msFullscreenElement;\n }\n\n\n private show(first = false) {\n this.loading = true;\n this.stopAutoPlay();\n\n this.activeChange.emit(this.index);\n\n if (first || !this.animation()) {\n this._show();\n } else {\n setTimeout(() => this._show(), 600);\n }\n }\n\n private _show() {\n this.zoomValue = 1;\n this.rotateValue = 0;\n this.resetPosition();\n\n this.src = this.getSafeUrl(this.images()[this.index] as string);\n this.type = this.getFileType(this.images()[this.index] as string);\n this.srcIndex = this.index;\n this.description = this.descriptions()[this.index];\n this.changeDetectorRef.markForCheck();\n\n setTimeout(() => {\n const previewImage = this.previewImage();\n if (this.isLoaded(previewImage.nativeElement) || this.type === 'video') {\n this.loading = false;\n this.startAutoPlay();\n this.changeDetectorRef.markForCheck();\n } else if (this.type === 'video') {\n\n }\n else {\n setTimeout(() => {\n if (this.loading) {\n this.showSpinner = true;\n this.changeDetectorRef.markForCheck();\n }\n });\n\n previewImage.nativeElement.onload = () => {\n this.loading = false;\n this.showSpinner = false;\n this.previewImage().nativeElement.onload = null;\n this.startAutoPlay();\n this.changeDetectorRef.markForCheck();\n };\n }\n });\n }\n\n private isLoaded(img): boolean {\n if (!img.complete) {\n return false;\n }\n\n return !(typeof img.naturalWidth !== 'undefined' && img.naturalWidth === 0);\n }\n\n}\n","@if (arrows) {\n <ngx-gallery-arrows (prevClick)=\"showPrev()\" (nextClick)=\"showNext()\" [prevDisabled]=\"!canShowPrev()\"\n [nextDisabled]=\"!canShowNext()\" [arrowPrevIcon]=\"arrowPrevIcon()\"\n [arrowNextIcon]=\"arrowNextIcon()\" />\n}\n<div class=\"ngx-gallery-preview-top\">\n <div class=\"ngx-gallery-preview-icons\">\n @for (action of actions(); track action) {\n <ngx-gallery-action [icon]=\"action.icon\" [disabled]=\"action.disabled\"\n [titleText]=\"action.titleText\" (closeClick)=\"action.onClick($event, index)\" />\n }\n @if (download() && src) {\n <a [href]=\"src\" class=\"ngx-gallery-icon\" aria-hidden=\"true\" download>\n <i class=\"ngx-gallery-icon-content {{ downloadIcon() }}\"></i>\n </a>\n }\n @if (zoom()) {\n <ngx-gallery-action [icon]=\"zoomOutIcon()\" [disabled]=\"!canZoomOut()\"\n (closeClick)=\"zoomOut()\" />\n }\n @if (zoom()) {\n <ngx-gallery-action [icon]=\"zoomInIcon()\" [disabled]=\"!canZoomIn()\"\n (closeClick)=\"zoomIn()\" />\n }\n @if (rotate()) {\n <ngx-gallery-action [icon]=\"rotateLeftIcon()\" (closeClick)=\"rotateLeft()\" />\n }\n @if (rotate()) {\n <ngx-gallery-action [icon]=\"rotateRightIcon()\" (closeClick)=\"rotateRight()\" />\n }\n @if (fullscreen()) {\n <ngx-gallery-action [icon]=\"'ngx-gallery-fullscreen ' + fullscreenIcon()\"\n (closeClick)=\"manageFullscreen()\" />\n }\n <ngx-gallery-action [icon]=\"'ngx-gallery-close ' + closeIcon()\" (closeClick)=\"close()\" /> \n </div>\n</div>\n<div class=\"ngx-spinner-wrapper ngx-gallery-center\" [class.ngx-gallery-active]=\"showSpinner\">\n <i class=\"ngx-gallery-icon ngx-gallery-spinner {{spinnerIcon()}}\" aria-hidden=\"true\"></i>\n</div>\n<div class=\"ngx-gallery-preview-wrapper\" (click)=\"closeOnClick() && close()\" (mouseup)=\"mouseUpHandler($event)\"\n (mousemove)=\"mouseMoveHandler($event)\" (touchend)=\"mouseUpHandler($event)\" (touchmove)=\"mouseMoveHandler($event)\">\n <div class=\"ngx-gallery-preview-img-wrapper\">\n @if (src && type === 'image') {\n <img #previewImage class=\"ngx-gallery-preview-img ngx-gallery-center\" [src]=\"src\"\n (click)=\"$event.stopPropagation()\" (mouseenter)=\"imageMouseEnter()\" (mouseleave)=\"imageMouseLeave()\"\n (mousedown)=\"mouseDownHandler($event)\" (touchstart)=\"mouseDownHandler($event)\"\n [class.ngx-gallery-active]=\"!loading\" [class.animation]=\"animation()\" [class.ngx-gallery-grab]=\"canDragOnZoom()\"\n [style.transform]=\"getTransform()\" [style.left]=\"positionLeft + 'px'\" [style.top]=\"positionTop + 'px'\"/>\n }\n @if (src && type === 'video') {\n <video #previewImage controls style=\"width: 100%; height: 100%;\"\n class=\"ngx-gallery-preview-img ngx-gallery-center\"\n (click)=\"$event.stopPropagation()\" (mouseenter)=\"imageMouseEnter()\" (mouseleave)=\"imageMouseLeave()\" (mousedown)=\"mouseDownHandler($event)\" (touchstart)=\"mouseDownHandler($event)\"\n [class.ngx-gallery-active]=\"!loading\" [class.animation]=\"animation()\" [class.ngx-gallery-grab]=\"canDragOnZoom()\" [style.transform]=\"getTransform()\" [style.left]=\"positionLeft + 'px'\" [style.top]=\"positionTop + 'px'\">\n <source [src]=\"src\">\n Your browser does not support the video tag.\n </video>\n }\n @if (bullets()) {\n <ngx-gallery-bullets [count]=\"images().length\" [active]=\"index\"\n (bulletChange)=\"showAtIndex($event)\" />\n }\n </div>\n @if (showDescription() && description) {\n <div class=\"ngx-gallery-preview-text\" [innerHTML]=\"description\"\n (click)=\"$event.stopPropagation()\"></div>\n }\n</div>\n","export class NgxGalleryAnimation {\n static Fade = 'fade';\n static Slide = 'slide';\n static Rotate = 'rotate';\n static Zoom = 'zoom';\n}\n","import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnChanges, OnInit, SimpleChanges, inject, input, output } from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl, SafeStyle } from '@angular/platform-browser';\nimport { NgxGalleryService } from '../ngx-gallery.service';\nimport { NgxGalleryOrderedImage } from '../ngx-gallery-ordered-image';\nimport { NgxGalleryAction } from '../ngx-gallery-action';\nimport { NgxGalleryAnimation } from '../ngx-gallery-animation';\nimport { animate, AnimationEvent, state, style, transition, trigger } from '@angular/animations';\nimport { NgClass } from '@angular/common';\nimport { NgxGalleryActionComponent } from '../ngx-gallery-action/ngx-gallery-action.component';\nimport { NgxGalleryBulletsComponent } from '../ngx-gallery-bullets/ngx-gallery-bullets.component';\nimport { NgxGalleryArrowsComponent } from '../ngx-gallery-arrows/ngx-gallery-arrows.component';\n\ntype Orientation = ('slideLeft' | 'slideRight' | 'fade' | 'rotateLeft' | 'rotateRight' | 'zoom' | 'none');\n\n@Component({\n selector: 'ngx-gallery-image',\n templateUrl: './ngx-gallery-image.component.html',\n styleUrls: ['./ngx-gallery-image.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n animations: [\n trigger('animation', [\n // ...\n state('slideRight', style({})),\n state('slideLeft', style({})),\n state('fade', style({})),\n state('rotateLeft', style({})),\n state('rotateRight', style({})),\n state('zoom', style({})),\n transition('slideRight => void', [\n animate('500ms ease-in-out', style({ transform: 'translateX(-100%)' }))\n ]),\n transition('void => slideRight', [\n style({ transform: 'translateX(100%)' }),\n animate('500ms ease-in-out', style({ transform: 'translateX(0)' }))\n ]),\n transition('slideLeft => void', [\n animate('500ms ease-in-out', style({ transform: 'translateX(100%)' }))\n ]),\n transition('void => slideLeft', [\n style({ transform: 'translateX(-100%)' }),\n animate('500ms ease-in-out', style({ transform: 'translateX(0)' }))\n ]),\n transition('fade => void', [\n animate('500ms ease-in-out', style({ opacity: '0' }))\n ]),\n transition('void => fade', [\n style({ opacity: '0' }),\n animate('500ms ease-in-out', style({ opacity: '1' }))\n ]),\n transition('rotateLeft => void', [\n animate('500ms ease-in-out', style({ transform: 'scale(1, 1) rotate(-90deg)', opacity: '0' }))\n ]),\n transition('void => rotateLeft', [\n style({ transform: 'scale(1, 1) rotate(-90deg)', opacity: '0' }),\n animate('500ms ease-in-out', style({ transform: 'scale(1, 1) rotate(0deg)', opacity: '1' }))\n ]),\n transition('rotateRight => void', [\n animate('500ms ease-in-out', style({ transform: 'scale(1, 1) rotate(90deg)', opacity: '0' }))\n ]),\n transition('void => rotateRight', [\n style({ transform: 'scale(1, 1) rotate(90deg)', opacity: '0' }),\n animate('500ms ease-in-out', style({ transform: 'scale(1, 1) rotate(0deg)', opacity: '1' }))\n ]),\n transition('zoom => void', [\n animate('500ms ease-in-out', style({ transform: 'scale(2.5,2.5)', opacity: '0' }))\n ]),\n transition('void => zoom', [\n style({ transform: 'scale(2.5,2.5)', opacity: '0' }),\n animate('500ms ease-in-out', style({ transform: 'scale(1, 1)', opacity: '1' }))\n ]),\n ]),\n ],\n imports: [NgClass, NgxGalleryActionComponent, NgxGalleryBulletsComponent, NgxGalleryArrowsComponent]\n})\nexport class NgxGalleryImageComponent implements OnInit, OnChanges {\n private sanitization = inject(DomSanitizer);\n private changeDetectorRef = inject(ChangeDetectorRef);\n private elementRef = inject(ElementRef);\n private helperService = inject(NgxGalleryService);\n\n readonly images = input<NgxGalleryOrderedImage[]>(undefined);\n readonly clickable = input<boolean>(undefined);\n // eslint-disable-next-line no-underscore-dangle, id-blacklist, id-match\n _selectedIndex;\n @Input()\n set selectedIndex(index: number) {\n if (index > this._selectedIndex) {\n let action;\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide) {\n action = 'slideRight';\n } else if (animation === NgxGalleryAnimation.Fade) {\n action = 'fade';\n } else if (animation === NgxGalleryAnimation.Rotate) {\n action = 'rotateRight';\n } else if (animation === NgxGalleryAnimation.Zoom) {\n action = 'zoom';\n }\n this.setAction(action);\n } else if (index < this._selectedIndex) {\n let action;\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide) {\n action = 'slideLeft';\n } else if (animation === NgxGalleryAnimation.Fade) {\n action = 'fade';\n } else if (animation === NgxGalleryAnimation.Rotate) {\n action = 'rotateLeft';\n } else if (animation === NgxGalleryAnimation.Zoom) {\n action = 'zoom';\n }\n this.setAction(action);\n }\n\n this._selectedIndex = index;\n }\n\n @Input() arrows: boolean;\n readonly arrowsAutoHide = input<boolean>(undefined);\n readonly swipe = input<boolean>(undefined);\n readonly animation = input<string>(undefined);\n readonly size = input<string>(undefined);\n readonly arrowPrevIcon = input<string>(undefined);\n readonly arrowNextIcon = input<string>(undefined);\n readonly autoPlay = input<boolean>(undefined);\n readonly autoPlayInterval = input<number>(undefined);\n readonly autoPlayPauseOnHover = input<boolean>(undefined);\n readonly infinityMove = input<boolean>(undefined);\n readonly lazyLoading = input<boolean>(undefined);\n readonly actions = input<NgxGalleryAction[]>(undefined);\n @Input() descriptions: string[];\n readonly showDescription = input<boolean>(undefined);\n readonly bullets = input<boolean>(undefined);\n\n readonly imageClick = output<number>();\n readonly activeChange = output<number>();\n readonly animating = output<boolean>();\n\n canChangeImage = true;\n public action: Orientation;\n\n isAnimating = false;\n\n private timer;\n\n constructor() {\n const changeDetectorRef = this.changeDetectorRef;\n\n this.changeDetectorRef = changeDetectorRef;\n this.action = 'none';\n }\n\n // @HostBinding('style.display') public display = 'inline-block';\n // @HostBinding('style.background-color') public color = 'lime';\n\n ngOnInit() {\n if (this.arrows && this.arrowsAutoHide()) {\n this.arrows = false;\n }\n\n if (this.autoPlay()) {\n this.startAutoPlay();\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['swipe']) {\n this.helperService.manageSwipe(this.swipe(), this.elementRef, 'image', () => this.showNext(), () => this.showPrev());\n }\n }\n\n @HostListener('mouseenter') onMouseEnter() {\n if (this.arrowsAutoHide() && !this.arrows) {\n this.arrows = true;\n }\n\n if (this.autoPlay() && this.autoPlayPauseOnHover()) {\n this.stopAutoPlay();\n }\n }\n\n @HostListener('mouseleave') onMouseLeave() {\n if (this.arrowsAutoHide() && this.arrows) {\n this.arrows = false;\n }\n\n if (this.autoPlay() && this.autoPlayPauseOnHover()) {\n this.startAutoPlay();\n }\n }\n\n reset(index: number): void {\n this._selectedIndex = index;\n this.action = 'none';\n }\n\n getImages(): NgxGalleryOrderedImage[] {\n const images = this.images();\n if (!images) {\n return [];\n }\n\n if (this.lazyLoading()) {\n const indexes = [this._selectedIndex];\n const prevIndex = this._selectedIndex - 1;\n\n const infinityMove = this.infinityMove();\n if (prevIndex === -1 && infinityMove) {\n indexes.push(images.length - 1);\n } else if (prevIndex >= 0) {\n indexes.push(prevIndex);\n }\n\n const nextIndex = this._selectedIndex + 1;\n\n if (nextIndex === images.length && infinityMove) {\n indexes.push(0);\n } else if (nextIndex < images.length) {\n indexes.push(nextIndex);\n }\n\n return images.filter((img, i) => indexes.indexOf(i) !== -1);\n } else {\n return images;\n }\n }\n\n startAutoPlay(): void {\n this.stopAutoPlay();\n\n this.timer = setInterval(() => {\n if (!this.showNext()) {\n this._selectedIndex = -1;\n this.showNext();\n }\n }, this.autoPlayInterval());\n }\n\n stopAutoPlay() {\n if (this.timer) {\n clearInterval(this.timer);\n }\n }\n\n handleClick(event: Event, index: number): void {\n if (this.clickable()) {\n this.imageClick.emit(index);\n\n event.stopPropagation();\n event.preventDefault();\n }\n }\n\n show(index: number) {\n if (this.isAnimating) {\n return;\n }\n if (index > this._selectedIndex) {\n let action;\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide) {\n action = 'slideRight';\n } else if (animation === NgxGalleryAnimation.Fade) {\n action = 'fade';\n } else if (animation === NgxGalleryAnimation.Rotate) {\n action = 'rotateRight';\n } else if (animation === NgxGalleryAnimation.Zoom) {\n action = 'zoom';\n }\n this.setAction(action);\n } else {\n let action;\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide) {\n action = 'slideLeft';\n } else if (animation === NgxGalleryAnimation.Fade) {\n action = 'fade';\n } else if (animation === NgxGalleryAnimation.Rotate) {\n action = 'rotateLeft';\n } else if (animation === NgxGalleryAnimation.Zoom) {\n action = 'zoom';\n }\n this.setAction(action);\n }\n\n this._selectedIndex = index;\n this.activeChange.emit(this._selectedIndex);\n this.setChangeTimeout();\n }\n\n setAction(action: Orientation) {\n this.action = action;\n this.changeDetectorRef.detectChanges();\n }\n\n showNext(): boolean {\n if (this.isAnimating) {\n return false;\n }\n if (this.canShowNext() && this.canChangeImage) {\n let action;\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide) {\n action = 'slideRight';\n } else if (animation === NgxGalleryAnimation.Fade) {\n action = 'fade';\n } else if (animation === NgxGalleryAnimation.Rotate) {\n action = 'rotateRight';\n } else if (animation === NgxGalleryAnimation.Zoom) {\n action = 'zoom';\n }\n this.setAction(action);\n this._selectedIndex++;\n if (this._selectedIndex === this.images().length) {\n this._selectedIndex = 0;\n }\n\n this.activeChange.emit(this._selectedIndex);\n this.setChangeTimeout();\n\n return true;\n } else {\n return false;\n }\n }\n\n showPrev(): void {\n if (this.isAnimating) {\n return;\n }\n if (this.canShowPrev() && this.canChangeImage) {\n let action;\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide) {\n action = 'slideLeft';\n } else if (animation === NgxGalleryAnimation.Fade) {\n action = 'fade';\n } else if (animation === NgxGalleryAnimation.Rotate) {\n action = 'rotateLeft';\n } else if (animation === NgxGalleryAnimation.Zoom) {\n action = 'zoom';\n }\n this.setAction(action);\n this._selectedIndex--;\n if (this._selectedIndex < 0) {\n this._selectedIndex = this.images().length - 1;\n }\n\n this.activeChange.emit(this._selectedIndex);\n this.setChangeTimeout();\n }\n }\n\n setChangeTimeout() {\n this.canChangeImage = false;\n let timeout = 1000;\n\n const animation = this.animation();\n if (animation === NgxGalleryAnimation.Slide\n || animation === NgxGalleryAnimation.Fade) {\n timeout = 500;\n }\n\n setTimeout(() => {\n this.canChangeImage = true;\n }, timeout);\n }\n\n canShowNext(): boolean {\n const images = this.images();\n if (images) {\n return this.infinityMove() || this._selectedIndex < images.length - 1;\n } else {\n return false;\n }\n }\n\n canShowPrev(): boolean {\n if (this.images()) {\n return this.infinityMove() || this._selectedIndex > 0;\n } else {\n return false;\n }\n }\n\n getSafeUrl(image: string | SafeResourceUrl): SafeStyle {\n return this.sanitization.bypassSecurityTrustStyle(this.helperService.getBackgroundUrl(image.toString()));\n }\n\n getFileType(fileSource: string) {\n return this.helperService.getFileType(fileSource);\n }\n\n onStart(event: AnimationEvent) {\n this.isAnimating = true;\n this.animating.emit(true);\n }\n\n onDone(event: AnimationEvent) {\n this.isAnimating = false;\n this.animating.emit(false);\n }\n}\n","<div class=\"ngx-gallery-image-wrapper ngx-gallery-animation-{{animation()}} ngx-gallery-image-size-{{size()}}\">\n @for (image of getImages(); track image; let i = $index) {\n @if (image.type === 'image' && image.index === _selectedIndex) {\n <div class=\"ngx-gallery-image\"\n [ngClass]=\"{'ngx-gallery-clickable': clickable() }\"\n [style.background-image]=\"getSafeUrl(image.src)\"\n (click)=\"handleClick($event, image.index)\"\n [@animation]=\"action\"\n (@animation.start)=\"onStart($event)\"\n (@animation.done)=\"onDone($event)\">\n <div class=\"ngx-gallery-icons-wrapper\">\n @for (action of actions(); track action) {\n <ngx-gallery-action [icon]=\"action.icon\" [disabled]=\"action.disabled\"\n [titleText]=\"action.titleText\"\n (closeClick)=\"action.onClick($event, image.index)\" />\n }\n </div>\n @if (showDescription() && descriptions[image.index]) {\n <div class=\"ngx-gallery-image-text\"\n [innerHTML]=\"descriptions[image.index]\" (click)=\"$event.stopPropagation()\"></div>\n }\n </div>\n }\n @if (image.type === 'video' && image.index === _selectedIndex) {\n <div class=\"ngx-gallery-image\"\n [ngClass]=\"{'ngx-gallery-clickable': clickable() }\"\n [style.background-image]=\"getSafeUrl(image.src)\"\n (click)=\"handleClick($event, image.index)\"\n [@animation]=\"action\"\n (@animation.start)=\"onStart($event)\"\n (@animation.done)=\"onDone($event)\">\n <video controls style=\"width:100%; height:100%; background: #000;\">\n <source [src]=\"image.src\">\n Your browser does not support the video tag.\n </video>\n <div class=\"ngx-gallery-icons-wrapper\">\n @for (action of actions(); track action) {\n <ngx-gallery-action [icon]=\"action.icon\" [disabled]=\"action.disabled\"\n [titleText]=\"action.titleText\"\n (closeClick)=\"action.onClick($event, image.index)\" />\n }\n </div>\n @if (showDescription() && descriptions[image.index]) {\n <div class=\"ngx-gallery-image-text\"\n [innerHTML]=\"descriptions[image.index]\" (click)=\"$event.stopPropagation()\"></div>\n }\n </div>\n }\n }\n @if (bullets()) {\n <ngx-gallery-bullets [count]=\"images().length\" [active]=\"_selectedIndex\"\n (bulletChange)=\"show($event)\" />\n }\n @if (arrows) {\n <ngx-gallery-arrows class=\"ngx-gallery-image-size-{{size()}}\" (prevClick)=\"showPrev()\"\n (nextClick)=\"showNext()\" [prevDisabled]=\"!canShowPrev()\" [nextDisabled]=\"!canShowNext()\"\n [arrowPrevIcon]=\"arrowPrevIcon()\" [arrowNextIcon]=\"arrowNextIcon()\" />\n }\n</div>\n","export class NgxGalleryOrder {\n static Column = 1;\n static Row = 2;\n static Page = 3;\n}\n","import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Input, OnChanges, OnInit, SimpleChanges, inject, input, output } from '@angular/core';\nimport {DomSanitizer, SafeResourceUrl, SafeStyle} from '@angular/platform-browser';\nimport {NgxGalleryService} from '../ngx-gallery.service';\nimport {NgxGalleryAction} from '../ngx-gallery-action';\nimport {NgxGalleryOrder} from '../ngx-gallery-order';\nimport { NgClass } from '@angular/common';\nimport { NgxGalleryActionComponent } from '../ngx-gallery-action/ngx-gallery-action.component';\nimport { NgxGalleryArrowsComponent } from '../ngx-gallery-arrows/ngx-gallery-arrows.component';\n\n@Component({\n selector: 'ngx-gallery-thumbnails',\n templateUrl: './ngx-gallery-thumbnails.component.html',\n styleUrls: ['./ngx-gallery-thumbnails.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgClass, NgxGalleryActionComponent, NgxGalleryArrowsComponent]\n})\nexport class NgxGalleryThumbnailsComponent implements OnChanges {\n private sanitization = inject(DomSanitizer);\n private elementRef = inject(ElementRef);\n private helperService = inject(NgxGalleryService);\n\n thumbnailsLeft: string;\n thumbnailsMarginLeft: string;\n mouseenter: boolean;\n remainingCountValue: number;\n\n minStopIndex = 0;\n\n readonly images = input<string[] | SafeResourceUrl[]>(undefined);\n readonly isAnimating = input<boolean>(undefined);\n readonly links = input<string[]>(undefined);\n readonly labels = input<string[]>(undefined);\n readonly linkTarget = input<string>(undefined);\n readonly columns = input<number>(undefined);\n readonly rows = input<number>(undefined);\n readonly arrows = input<boolean>(undefined);\n readonly arrowsAutoHide = input<boolean>(undefined);\n readonly margin = input<number>(undefined);\n @Input() selectedIndex: number;\n readonly clickable = input<boolean>(undefined);\n readonly swipe = input<boolean>(undefined);\n readonly size = input<string>(undefined);\n readonly arrowPrevIcon = input<string>(undefined);\n readonly arrowNextIcon = input<string>(undefined);\n readonly moveSize = input<number>(undefined);\n readonly order = input<number>(undefined);\n readonly remainingCount = input<boolean>(undefined);\n readonly lazyLoading = input<boolean>(undefined);\n readonly actions = input<NgxGalleryAction[]>(undefined);\n\n readonly activeChange = output<number>();\n\n private index = 0;\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['selectedIndex']) {\n this.validateIndex();\n }\n\n if (changes['swipe']) {\n this.helperService.manageSwipe(this.swipe(), this.elementRef,\n 'thumbnails', () => this.moveRight(), () => this.moveLeft());\n }\n\n const images = this.images();\n if (images) {\n this.remainingCountValue = images.length - (this.rows() * this.columns());\n }\n }\n\n @HostListener('mouseenter') onMouseEnter() {\n this.mouseenter = true;\n }\n\n @HostListener('mouseleave') onMouseLeave() {\n this.mouseenter = false;\n }\n\n reset(index: number): void {\n this.selectedIndex = index;\n this.setDefaultPosition();\n\n this.index = 0;\n this.validateIndex();\n }\n\n getImages(): string[] | SafeResourceUrl[] {\n const images = this.images();\n if (!images) {\n return [];\n }\n\n const order = this.order();\n if (this.remainingCount()) {\n return images.slice(0, this.rows() * this.columns());\n } else if (this.lazyLoading() && order !== NgxGalleryOrder.Row) {\n let stopIndex = 0;\n\n if (order === NgxGalleryOrder.Column) {\n stopIndex = (this.index + this.columns() + this.moveSize()) * this.rows();\n } else if (order === NgxGalleryOrder.Page) {\n stopIndex = this.index + ((this.columns() * this.rows()) * 2);\n }\n\n if (stopIndex <= this.minStopIndex) {\n stopIndex = this.minStopIndex;\n } else {\n this.minStopIndex = stopIndex;\n }\n\n return images.slice(0, stopIndex);\n } else {\n return images;\n }\n }\n\n handleClick(event: Event, index: number): void {\n if (!this.hasLink(index) && !this.isAnimating()) {\n this.selectedIndex = index;\n this.activeChange.emit(index);\n }\n event.stopPropagation();\n event.preventDefault();\n }\n\n hasLink(index: number): boolean {\n const links = this.links();\n return !!(links && links.length && links[index]);\n }\n\n moveRight(): void {\n if (this.canMoveRight()) {\n this.index += this.moveSize();\n const maxIndex = this.getMaxIndex() - this.columns();\n\n if (this.index > maxIndex) {\n this.index = maxIndex;\n }\n\n this.setThumbnailsPosition();\n }\n }\n\n moveLeft(): void {\n if (this.canMoveLeft()) {\n this.index -= this.moveSize();\n\n if (this.index < 0) {\n this.index = 0;\n }\n\n this.setThumbnailsPosition();\n }\n }\n\n canMoveRight(): boolean {\n return this.index + this.columns() < this.getMaxIndex();\n }\n\n canMoveLeft(): boolean {\n return this.index !== 0;\n }\n\n getThumbnailLeft(index: number): SafeStyle {\n let calculatedIndex;\n\n const order = this.order();\n if (order === NgxGalleryOrder.Column) {\n calculatedIndex = Math.floor(index / this.rows());\n } else if (order === NgxGalleryOrder.Page) {\n calculatedIndex = (index % this.columns()) + (Math.floor(index / (this.rows() * this.columns())) * this.columns());\n } else if (order === NgxGalleryOrder.Row && this.remainingCount()) {\n calculatedIndex = index % this.columns();\n } else {\n calculatedIndex = index % Math.ceil(this.images().length / this.rows());\n }\n\n return this.getThumbnailPosition(calculatedIndex, this.columns());\n }\n\n getThumbnailTop(index: number): SafeStyle {\n let calculatedIndex;\n\n const order = this.order();\n if (order === NgxGalleryOrder.Column) {\n calculatedIndex = index % this.rows();\n } else if (order === NgxGalleryOrder.Page) {\n calculatedIndex = Math.floor(index / this.columns()) - (Math.floor(index / (this.rows() * this.columns())) * this.rows());\n } else if (order === NgxGalleryOrder.Row && this.remainingCount()) {\n calculatedIndex = Math.floor(index / this.columns());\n } else {\n calculatedIndex = Math.floor(index / Math.ceil(this.images().length / this.rows()));\n }\n\n return this.getThumbnailPosition(calculatedIndex, this.rows());\n }\n\n getThumbnailWidth(): SafeStyle {\n return this.getThumbnailDimension(this.columns());\n }\n\n getThumbnailHeight(): SafeStyle {\n return this.getThumbnailDimension(this.rows());\n }\n\n setThumbnailsPosition(): void {\n this.thumbnailsLeft = -((100 / this.columns()) * this.index) + '%';\n\n this.thumbnailsMarginLeft = -((this.margin() - (((this.columns() - 1)\n * this.margin()) / this.columns())) * this.index) + 'px';\n }\n\n setDefaultPosition(): void {\n this.thumbnailsLeft = '0px';\n this.thumbnailsMarginLeft = '0px';\n }\n\n canShowArrows(): boolean {\n if (this.remainingCount()) {\n return false;\n } else {\n const images = this.images();\n return this.arrows() && images && images.length > this.getVisibleCount()\n && (!this.arrowsAutoHide() || this.mouseenter);\n }\n }\n\n validateIndex(): void {\n const images = this.images();\n if (images) {\n let newIndex;\n\n if (this.order() === NgxGalleryOrder.Column) {\n newIndex = Math.floor(this.selectedIndex / this.rows());\n } else {\n newIndex = this.selectedIndex % Math.ceil(images.length / this.rows());\n }\n\n if (this.remainingCount()) {\n newIndex = 0;\n }\n\n if (newIndex < this.index || newIndex >= this.index + this.columns()) {\n const maxIndex = this.getMaxIndex() - this.columns();\n this.index = newIndex > maxIndex ? maxIndex : newIndex;\n\n this.setThumbnailsPosition();\n }\n }\n }\n\n getSafeUrl(image: string | SafeResourceUrl): SafeStyle {\n return this.sanitization.bypassSecurityTrustStyle(this.helperService.getBackgroundUrl(image.toString()));\n }\n\n getFileType(fileSource: string | SafeResourceUrl): string {\n return this.helperService.getFileType(fileSource.toString());\n }\n\n private getThumbnailPosition(index: number, count: number): SafeStyle {\n return this.getSafeStyle('calc(' + ((100 / count) * index) + '% + '\n + ((this.margin() - (((count - 1) * this.margin()) / count)) * index) + 'px)');\n }\n\n private getThumbnailDimension(count: number): SafeStyle {\n const margin = this.margin();\n if (margin !== 0) {\n return this.getSafeStyle('calc(' + (100 / count) + '% - '\n + (((count - 1) * margin) / count) + 'px)');\n } else {\n return this.getSafeStyle('calc(' + (100 / count) + '% + 1px)');\n }\n }\n\n private getMaxIndex(): number {\n if (this.order() === NgxGalleryOrder.Page) {\n let maxIndex = (Math.floor(this.images().length / this.getVisibleCount()) * this.columns());\n\n if (this.images().length % this.getVisibleCount() > this.columns()) {\n maxIndex += this.columns();\n } else {\n maxIndex += this.images().length % this.getVisibleCount();\n }\n\n return maxIndex;\n } else {\n return Math.ceil(this.images().length / this.rows());\n }\n }\n\n private getVisibleCount(): number {\n return this.columns() * this.rows();\n }\n\n private getSafeStyle(value: string): SafeStyle {\n return this.sanitization.bypassSecurityTrustStyle(value);\n }\n}\n","<div class=\"ngx-gallery-thumbnails-wrapper ngx-gallery-thumbnail-size-{{size()}}\">\n <div class=\"ngx-gallery-thumbnails\" [style.transform]=\"'tran