UNPKG

primeng

Version:

[![npm version](https://badge.fury.io/js/primeng.svg)](https://badge.fury.io/js/primeng) [![npm downloads](https://img.shields.io/npm/dm/primeng.svg)](https://www.npmjs.com/package/primeng) [![Actions CI](https://github.com/primefaces/primeng/workflows/No

1 lines 100 kB
{"version":3,"file":"primeng-galleria.mjs","sources":["../../src/app/components/galleria/galleria.ts","../../src/app/components/galleria/primeng-galleria.ts"],"sourcesContent":["import { AnimationEvent, animate, style, transition, trigger } from '@angular/animations';\nimport { CommonModule, DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport {\n AfterContentChecked,\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ContentChildren,\n DoCheck,\n ElementRef,\n EventEmitter,\n HostListener,\n Inject,\n Input,\n KeyValueDiffers,\n NgModule,\n OnChanges,\n OnDestroy,\n OnInit,\n Output,\n PLATFORM_ID,\n QueryList,\n Renderer2,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n booleanAttribute,\n numberAttribute\n} from '@angular/core';\nimport { PrimeNGConfig, PrimeTemplate, SharedModule } from 'primeng/api';\nimport { DomHandler } from 'primeng/dom';\nimport { ChevronLeftIcon } from 'primeng/icons/chevronleft';\nimport { ChevronRightIcon } from 'primeng/icons/chevronright';\nimport { TimesIcon } from 'primeng/icons/times';\nimport { WindowMaximizeIcon } from 'primeng/icons/windowmaximize';\nimport { WindowMinimizeIcon } from 'primeng/icons/windowminimize';\nimport { RippleModule } from 'primeng/ripple';\nimport { VoidListener } from 'primeng/ts-helpers';\nimport { UniqueComponentId, ZIndexUtils } from 'primeng/utils';\nimport { GalleriaResponsiveOptions } from './galleria.interface';\nimport { FocusTrapModule } from 'primeng/focustrap';\n/**\n * Galleria is an advanced content gallery component.\n * @group Components\n */\n@Component({\n selector: 'p-galleria',\n template: `\n <div *ngIf=\"fullScreen; else windowed\" #container>\n <div\n *ngIf=\"maskVisible\"\n #mask\n [ngClass]=\"{ 'p-galleria-mask p-component-overlay p-component-overlay-enter': true, 'p-galleria-visible': this.visible }\"\n [class]=\"maskClass\"\n [attr.role]=\"fullScreen ? 'dialog' : 'region'\"\n [attr.aria-modal]=\"fullScreen ? 'true' : undefined\"\n >\n <p-galleriaContent\n *ngIf=\"visible\"\n [@animation]=\"{ value: 'visible', params: { showTransitionParams: showTransitionOptions, hideTransitionParams: hideTransitionOptions } }\"\n (@animation.start)=\"onAnimationStart($event)\"\n (@animation.done)=\"onAnimationEnd($event)\"\n [value]=\"value\"\n [activeIndex]=\"activeIndex\"\n [numVisible]=\"numVisibleLimit || numVisible\"\n (maskHide)=\"onMaskHide()\"\n (activeItemChange)=\"onActiveItemChange($event)\"\n [ngStyle]=\"containerStyle\"\n [fullScreen]=\"fullScreen\"\n ></p-galleriaContent>\n </div>\n </div>\n\n <ng-template #windowed>\n <p-galleriaContent [value]=\"value\" [activeIndex]=\"activeIndex\" [numVisible]=\"numVisibleLimit || numVisible\" (activeItemChange)=\"onActiveItemChange($event)\"></p-galleriaContent>\n </ng-template>\n `,\n animations: [\n trigger('animation', [\n transition('void => visible', [style({ transform: 'scale(0.7)', opacity: 0 }), animate('{{showTransitionParams}}')]),\n transition('visible => void', [animate('{{hideTransitionParams}}', style({ transform: 'scale(0.7)', opacity: 0 }))])\n ])\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['./galleria.css'],\n host: {\n class: 'p-element'\n }\n})\nexport class Galleria implements OnChanges, OnDestroy {\n /**\n * Index of the first item.\n * @group Props\n */\n @Input() get activeIndex(): number {\n return this._activeIndex;\n }\n set activeIndex(activeIndex) {\n this._activeIndex = activeIndex;\n }\n /**\n * Whether to display the component on fullscreen.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) fullScreen: boolean = false;\n /**\n * Unique identifier of the element.\n * @group Props\n */\n @Input() id: string | undefined;\n /**\n * An array of objects to display.\n * @group Props\n */\n @Input() value: any[] | undefined;\n /**\n * Number of items per page.\n * @group Props\n */\n @Input({ transform: numberAttribute }) numVisible: number = 3;\n /**\n * An array of options for responsive design.\n * @see {GalleriaResponsiveOptions}\n * @group Props\n */\n @Input() responsiveOptions: GalleriaResponsiveOptions[] | undefined;\n /**\n * Whether to display navigation buttons in item section.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showItemNavigators: boolean = false;\n /**\n * Whether to display navigation buttons in thumbnail container.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showThumbnailNavigators: boolean = true;\n /**\n * Whether to display navigation buttons on item hover.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showItemNavigatorsOnHover: boolean = false;\n /**\n * When enabled, item is changed on indicator hover.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) changeItemOnIndicatorHover: boolean = false;\n /**\n * Defines if scrolling would be infinite.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) circular: boolean = false;\n /**\n * Items are displayed with a slideshow in autoPlay mode.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autoPlay: boolean = false;\n /**\n * When enabled, autorun should stop by click.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) shouldStopAutoplayByClick: boolean = true;\n /**\n * Time in milliseconds to scroll items.\n * @group Props\n */\n @Input({ transform: numberAttribute }) transitionInterval: number = 4000;\n /**\n * Whether to display thumbnail container.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showThumbnails: boolean = true;\n /**\n * Position of thumbnails.\n * @group Props\n */\n @Input() thumbnailsPosition: 'bottom' | 'top' | 'left' | 'right' | undefined = 'bottom';\n /**\n * Height of the viewport in vertical thumbnail.\n * @group Props\n */\n @Input() verticalThumbnailViewPortHeight: string = '300px';\n /**\n * Whether to display indicator container.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showIndicators: boolean = false;\n /**\n * When enabled, indicator container is displayed on item container.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showIndicatorsOnItem: boolean = false;\n /**\n * Position of indicators.\n * @group Props\n */\n @Input() indicatorsPosition: 'bottom' | 'top' | 'left' | 'right' | undefined = 'bottom';\n /**\n * Base zIndex value to use in layering.\n * @group Props\n */\n @Input({ transform: numberAttribute }) baseZIndex: number = 0;\n /**\n * Style class of the mask on fullscreen mode.\n * @group Props\n */\n @Input() maskClass: string | undefined;\n /**\n * Style class of the component on fullscreen mode. Otherwise, the 'class' property can be used.\n * @group Props\n */\n @Input() containerClass: string | undefined;\n /**\n * Inline style of the component on fullscreen mode. Otherwise, the 'style' property can be used.\n * @group Props\n */\n @Input() containerStyle: { [klass: string]: any } | null | undefined;\n /**\n * Transition options of the show animation.\n * @group Props\n */\n @Input() showTransitionOptions: string = '150ms cubic-bezier(0, 0, 0.2, 1)';\n /**\n * Transition options of the hide animation.\n * @group Props\n */\n @Input() hideTransitionOptions: string = '150ms cubic-bezier(0, 0, 0.2, 1)';\n /**\n * Specifies the visibility of the mask on fullscreen mode.\n * @group Props\n */\n @Input() get visible(): boolean {\n return this._visible;\n }\n set visible(visible: boolean) {\n this._visible = visible;\n\n if (this._visible && !this.maskVisible) {\n this.maskVisible = true;\n }\n }\n /**\n * Callback to invoke on active index change.\n * @param {number} number - Active index.\n * @group Emits\n */\n @Output() activeIndexChange: EventEmitter<number> = new EventEmitter<number>();\n /**\n * Callback to invoke on visiblity change.\n * @param {boolean} boolean - Visible value.\n * @group Emits\n */\n @Output() visibleChange: EventEmitter<boolean> = new EventEmitter<boolean>();\n\n @ViewChild('mask') mask: ElementRef | undefined;\n\n @ViewChild('container') container: ElementRef | undefined;\n\n @ContentChildren(PrimeTemplate) templates: QueryList<PrimeTemplate> | undefined;\n\n _visible: boolean = false;\n\n _activeIndex: number = 0;\n\n headerFacet: any;\n\n footerFacet: any;\n\n indicatorFacet: any;\n\n captionFacet: any;\n\n closeIconTemplate: TemplateRef<any> | undefined;\n\n previousThumbnailIconTemplate: TemplateRef<any> | undefined;\n\n nextThumbnailIconTemplate: TemplateRef<any> | undefined;\n\n itemPreviousIconTemplate: TemplateRef<any> | undefined;\n\n itemNextIconTemplate: TemplateRef<any> | undefined;\n\n maskVisible: boolean = false;\n\n numVisibleLimit = 0;\n\n constructor(\n @Inject(DOCUMENT) private document: Document,\n @Inject(PLATFORM_ID) public platformId: any,\n public element: ElementRef,\n public cd: ChangeDetectorRef,\n public config: PrimeNGConfig\n ) {}\n\n ngAfterContentInit() {\n this.templates?.forEach((item) => {\n switch (item.getType()) {\n case 'header':\n this.headerFacet = item.template;\n break;\n\n case 'footer':\n this.footerFacet = item.template;\n break;\n\n case 'indicator':\n this.indicatorFacet = item.template;\n break;\n\n case 'closeicon':\n this.closeIconTemplate = item.template;\n break;\n\n case 'itemnexticon':\n this.itemNextIconTemplate = item.template;\n break;\n\n case 'itempreviousicon':\n this.itemPreviousIconTemplate = item.template;\n break;\n\n case 'previousthumbnailicon':\n this.previousThumbnailIconTemplate = item.template;\n break;\n\n case 'nextthumbnailicon':\n this.nextThumbnailIconTemplate = item.template;\n break;\n\n case 'caption':\n this.captionFacet = item.template;\n break;\n }\n });\n }\n\n ngOnChanges(simpleChanges: SimpleChanges) {\n if (simpleChanges.value && simpleChanges.value.currentValue?.length < this.numVisible) {\n this.numVisibleLimit = simpleChanges.value.currentValue.length;\n } else {\n this.numVisibleLimit = 0;\n }\n }\n\n onMaskHide() {\n this.visible = false;\n this.visibleChange.emit(false);\n }\n\n onActiveItemChange(index: number) {\n if (this.activeIndex !== index) {\n this.activeIndex = index;\n this.activeIndexChange.emit(index);\n }\n }\n\n onAnimationStart(event: AnimationEvent) {\n switch (event.toState) {\n case 'visible':\n this.enableModality();\n setTimeout(() => {\n DomHandler.focus(DomHandler.findSingle(this.container.nativeElement, '[data-pc-section=\"closebutton\"]'));\n }, 25);\n break;\n\n case 'void':\n DomHandler.addClass(this.mask?.nativeElement, 'p-component-overlay-leave');\n break;\n }\n }\n\n onAnimationEnd(event: AnimationEvent) {\n switch (event.toState) {\n case 'void':\n this.disableModality();\n break;\n }\n }\n\n enableModality() {\n DomHandler.blockBodyScroll();\n this.cd.markForCheck();\n\n if (this.mask) {\n ZIndexUtils.set('modal', this.mask.nativeElement, this.baseZIndex || this.config.zIndex.modal);\n }\n }\n\n disableModality() {\n DomHandler.unblockBodyScroll();\n this.maskVisible = false;\n this.cd.markForCheck();\n\n if (this.mask) {\n ZIndexUtils.clear(this.mask.nativeElement);\n }\n }\n\n ngOnDestroy() {\n if (this.fullScreen) {\n DomHandler.removeClass(this.document.body, 'p-overflow-hidden');\n }\n\n if (this.mask) {\n this.disableModality();\n }\n }\n}\n\n@Component({\n selector: 'p-galleriaContent',\n template: `\n <div\n [attr.id]=\"id\"\n [attr.role]=\"'region'\"\n *ngIf=\"value && value.length > 0\"\n [ngClass]=\"{\n 'p-galleria p-component': true,\n 'p-galleria-fullscreen': this.galleria.fullScreen,\n 'p-galleria-indicator-onitem': this.galleria.showIndicatorsOnItem,\n 'p-galleria-item-nav-onhover': this.galleria.showItemNavigatorsOnHover && !this.galleria.fullScreen\n }\"\n [ngStyle]=\"!galleria.fullScreen ? galleria.containerStyle : {}\"\n [class]=\"galleriaClass()\"\n pFocusTrap\n [pFocusTrapDisabled]=\"!fullScreen\"\n >\n <button *ngIf=\"galleria.fullScreen\" type=\"button\" class=\"p-galleria-close p-link\" (click)=\"maskHide.emit()\" pRipple [attr.aria-label]=\"closeAriaLabel()\" [attr.data-pc-section]=\"'closebutton'\">\n <TimesIcon *ngIf=\"!galleria.closeIconTemplate\" [styleClass]=\"'p-galleria-close-icon'\" />\n <ng-template *ngTemplateOutlet=\"galleria.closeIconTemplate\"></ng-template>\n </button>\n <div *ngIf=\"galleria.templates && galleria.headerFacet\" class=\"p-galleria-header\">\n <p-galleriaItemSlot type=\"header\" [templates]=\"galleria.templates\"></p-galleriaItemSlot>\n </div>\n <div class=\"p-galleria-content\" [attr.aria-live]=\"galleria.autoPlay ? 'polite' : 'off'\">\n <p-galleriaItem\n [id]=\"id\"\n [value]=\"value\"\n [activeIndex]=\"activeIndex\"\n [circular]=\"galleria.circular\"\n [templates]=\"galleria.templates\"\n (onActiveIndexChange)=\"onActiveIndexChange($event)\"\n [showIndicators]=\"galleria.showIndicators\"\n [changeItemOnIndicatorHover]=\"galleria.changeItemOnIndicatorHover\"\n [indicatorFacet]=\"galleria.indicatorFacet\"\n [captionFacet]=\"galleria.captionFacet\"\n [showItemNavigators]=\"galleria.showItemNavigators\"\n [autoPlay]=\"galleria.autoPlay\"\n [slideShowActive]=\"slideShowActive\"\n (startSlideShow)=\"startSlideShow()\"\n (stopSlideShow)=\"stopSlideShow()\"\n ></p-galleriaItem>\n\n <p-galleriaThumbnails\n *ngIf=\"galleria.showThumbnails\"\n [containerId]=\"id\"\n [value]=\"value\"\n (onActiveIndexChange)=\"onActiveIndexChange($event)\"\n [activeIndex]=\"activeIndex\"\n [templates]=\"galleria.templates\"\n [numVisible]=\"numVisible\"\n [responsiveOptions]=\"galleria.responsiveOptions\"\n [circular]=\"galleria.circular\"\n [isVertical]=\"isVertical()\"\n [contentHeight]=\"galleria.verticalThumbnailViewPortHeight\"\n [showThumbnailNavigators]=\"galleria.showThumbnailNavigators\"\n [slideShowActive]=\"slideShowActive\"\n (stopSlideShow)=\"stopSlideShow()\"\n ></p-galleriaThumbnails>\n </div>\n <div *ngIf=\"galleria.templates && galleria.footerFacet\" class=\"p-galleria-footer\">\n <p-galleriaItemSlot type=\"footer\" [templates]=\"galleria.templates\"></p-galleriaItemSlot>\n </div>\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class GalleriaContent implements DoCheck {\n @Input() get activeIndex(): number {\n return this._activeIndex;\n }\n set activeIndex(activeIndex: number) {\n this._activeIndex = activeIndex;\n }\n\n @Input() value: any[] = [];\n\n @Input({ transform: numberAttribute }) numVisible: number | undefined;\n\n @Input({ transform: booleanAttribute }) fullScreen: boolean;\n\n @Output() maskHide: EventEmitter<boolean> = new EventEmitter();\n\n @Output() activeItemChange: EventEmitter<number> = new EventEmitter();\n\n @ViewChild('closeButton') closeButton: ElementRef | undefined;\n\n id: string;\n\n _activeIndex: number = 0;\n\n slideShowActive: boolean = true;\n\n interval: any;\n\n styleClass: string | undefined;\n\n private differ: any;\n\n constructor(\n public galleria: Galleria,\n public cd: ChangeDetectorRef,\n private differs: KeyValueDiffers,\n public config: PrimeNGConfig,\n private elementRef: ElementRef\n ) {\n this.id = this.galleria.id || UniqueComponentId();\n this.differ = this.differs.find(this.galleria).create();\n }\n\n // For custom fullscreen\n @HostListener('document:fullscreenchange', ['$event'])\n handleFullscreenChange(event: Event) {\n if (document?.fullscreenElement === this.elementRef.nativeElement?.children[0]) {\n this.fullScreen = true;\n } else {\n this.fullScreen = false;\n }\n }\n\n ngDoCheck(): void {\n if (isPlatformBrowser(this.galleria.platformId)) {\n const changes = this.differ.diff(this.galleria as unknown as Record<string, unknown>);\n if (changes && changes.forEachItem.length > 0) {\n // Because we change the properties of the parent component,\n // and the children take our entity from the injector.\n // We can tell the children to redraw themselves when we change the properties of the parent component.\n // Since we have an onPush strategy\n this.cd.markForCheck();\n }\n }\n }\n\n galleriaClass() {\n const thumbnailsPosClass = this.galleria.showThumbnails && this.getPositionClass('p-galleria-thumbnails', this.galleria.thumbnailsPosition as string);\n const indicatorPosClass = this.galleria.showIndicators && this.getPositionClass('p-galleria-indicators', this.galleria.indicatorsPosition as string);\n\n return (this.galleria.containerClass ? this.galleria.containerClass + ' ' : '') + (thumbnailsPosClass ? thumbnailsPosClass + ' ' : '') + (indicatorPosClass ? indicatorPosClass + ' ' : '');\n }\n\n startSlideShow() {\n if (isPlatformBrowser(this.galleria.platformId)) {\n this.interval = setInterval(() => {\n let activeIndex = this.galleria.circular && this.value.length - 1 === this.activeIndex ? 0 : this.activeIndex + 1;\n this.onActiveIndexChange(activeIndex);\n this.activeIndex = activeIndex;\n }, this.galleria.transitionInterval);\n\n this.slideShowActive = true;\n }\n }\n\n stopSlideShow() {\n if (this.galleria.autoPlay && !this.galleria.shouldStopAutoplayByClick) {\n return;\n }\n\n if (this.interval) {\n clearInterval(this.interval);\n }\n\n this.slideShowActive = false;\n }\n\n getPositionClass(preClassName: string, position: string) {\n const positions = ['top', 'left', 'bottom', 'right'];\n const pos = positions.find((item) => item === position);\n\n return pos ? `${preClassName}-${pos}` : '';\n }\n\n isVertical() {\n return this.galleria.thumbnailsPosition === 'left' || this.galleria.thumbnailsPosition === 'right';\n }\n\n onActiveIndexChange(index: number) {\n if (this.activeIndex !== index) {\n this.activeIndex = index;\n this.activeItemChange.emit(this.activeIndex);\n }\n }\n\n closeAriaLabel() {\n return this.config.translation.aria ? this.config.translation.aria.close : undefined;\n }\n}\n\n@Component({\n selector: 'p-galleriaItemSlot',\n template: `\n <ng-container *ngIf=\"contentTemplate\">\n <ng-container *ngTemplateOutlet=\"contentTemplate; context: context\"></ng-container>\n </ng-container>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class GalleriaItemSlot {\n @Input() templates: QueryList<PrimeTemplate> | undefined;\n\n @Input({ transform: numberAttribute }) index: number | undefined;\n\n @Input() get item(): any {\n return this._item;\n }\n\n set item(item: any) {\n this._item = item;\n if (this.templates) {\n this.templates.forEach((item) => {\n if (item.getType() === this.type) {\n switch (this.type) {\n case 'item':\n case 'caption':\n case 'thumbnail':\n this.context = { $implicit: this.item };\n this.contentTemplate = item.template;\n break;\n }\n }\n });\n }\n }\n\n @Input() type: string | undefined;\n\n contentTemplate: TemplateRef<any> | undefined;\n\n context: any;\n\n _item: any;\n\n ngAfterContentInit() {\n this.templates?.forEach((item) => {\n if (item.getType() === this.type) {\n switch (this.type) {\n case 'item':\n case 'caption':\n case 'thumbnail':\n this.context = { $implicit: this.item };\n this.contentTemplate = item.template;\n break;\n\n case 'indicator':\n this.context = { $implicit: this.index };\n this.contentTemplate = item.template;\n break;\n\n default:\n this.context = {};\n this.contentTemplate = item.template;\n break;\n }\n }\n });\n }\n}\n\n@Component({\n selector: 'p-galleriaItem',\n template: `\n <div class=\"p-galleria-item-wrapper\">\n <div class=\"p-galleria-item-container\">\n <button\n *ngIf=\"showItemNavigators\"\n type=\"button\"\n role=\"navigation\"\n [ngClass]=\"{ 'p-galleria-item-prev p-galleria-item-nav p-link': true, 'p-galleria-item-nav-focused': leftButtonFocused, 'p-disabled': this.isNavBackwardDisabled() }\"\n (click)=\"navBackward($event)\"\n [disabled]=\"isNavBackwardDisabled()\"\n pRipple\n (focus)=\"onButtonFocus('left')\"\n (blur)=\"onButtonBlur('left')\"\n >\n <ChevronLeftIcon *ngIf=\"!galleria.itemPreviousIconTemplate\" [styleClass]=\"'p-galleria-item-prev-icon'\" />\n <ng-template *ngTemplateOutlet=\"galleria.itemPreviousIconTemplate\"></ng-template>\n </button>\n <div [id]=\"id + '_item_' + activeIndex\" role=\"group\" [attr.aria-label]=\"ariaSlideNumber(activeIndex + 1)\" [attr.aria-roledescription]=\"ariaSlideLabel()\" [style.width]=\"'100%'\">\n <p-galleriaItemSlot type=\"item\" [item]=\"activeItem\" [templates]=\"templates\" class=\"p-galleria-item\"></p-galleriaItemSlot>\n </div>\n <button\n *ngIf=\"showItemNavigators\"\n type=\"button\"\n [ngClass]=\"{ 'p-galleria-item-next p-galleria-item-nav p-link': true, 'p-galleria-item-nav-focused': rightButtonFocused, 'p-disabled': this.isNavForwardDisabled() }\"\n (click)=\"navForward($event)\"\n [disabled]=\"isNavForwardDisabled()\"\n pRipple\n role=\"navigation\"\n (focus)=\"onButtonFocus('right')\"\n (blur)=\"onButtonBlur('right')\"\n >\n <ChevronRightIcon *ngIf=\"!galleria.itemNextIconTemplate\" [styleClass]=\"'p-galleria-item-next-icon'\" />\n <ng-template *ngTemplateOutlet=\"galleria.itemNextIconTemplate\"></ng-template>\n </button>\n <div class=\"p-galleria-caption\" *ngIf=\"captionFacet\">\n <p-galleriaItemSlot type=\"caption\" [item]=\"activeItem\" [templates]=\"templates\"></p-galleriaItemSlot>\n </div>\n </div>\n <ul *ngIf=\"showIndicators\" class=\"p-galleria-indicators p-reset\">\n <li\n *ngFor=\"let item of value; let index = index\"\n tabindex=\"0\"\n (click)=\"onIndicatorClick(index)\"\n (mouseenter)=\"onIndicatorMouseEnter(index)\"\n (keydown)=\"onIndicatorKeyDown($event, index)\"\n [ngClass]=\"{ 'p-galleria-indicator': true, 'p-highlight': isIndicatorItemActive(index) }\"\n [attr.aria-label]=\"ariaPageLabel(index + 1)\"\n [attr.aria-selected]=\"activeIndex === index\"\n [attr.aria-controls]=\"id + '_item_' + index\"\n >\n <button type=\"button\" tabIndex=\"-1\" class=\"p-link\" *ngIf=\"!indicatorFacet\"></button>\n <p-galleriaItemSlot type=\"indicator\" [index]=\"index\" [templates]=\"templates\"></p-galleriaItemSlot>\n </li>\n </ul>\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class GalleriaItem implements OnChanges {\n @Input() id: string | undefined;\n\n @Input({ transform: booleanAttribute }) circular: boolean = false;\n\n @Input() value: any[] | undefined;\n\n @Input({ transform: booleanAttribute }) showItemNavigators: boolean = false;\n\n @Input({ transform: booleanAttribute }) showIndicators: boolean = true;\n\n @Input({ transform: booleanAttribute }) slideShowActive: boolean = true;\n\n @Input({ transform: booleanAttribute }) changeItemOnIndicatorHover: boolean = true;\n\n @Input({ transform: booleanAttribute }) autoPlay: boolean = false;\n\n @Input() templates: QueryList<PrimeTemplate> | undefined;\n\n @Input() indicatorFacet: any;\n\n @Input() captionFacet: any;\n\n @Output() startSlideShow: EventEmitter<Event> = new EventEmitter();\n\n @Output() stopSlideShow: EventEmitter<Event> = new EventEmitter();\n\n @Output() onActiveIndexChange: EventEmitter<number> = new EventEmitter();\n\n @Input() get activeIndex(): number {\n return this._activeIndex;\n }\n\n set activeIndex(activeIndex) {\n this._activeIndex = activeIndex;\n }\n\n get activeItem() {\n return this.value && this.value[this._activeIndex];\n }\n\n _activeIndex: number = 0;\n\n leftButtonFocused: boolean = false;\n\n rightButtonFocused: boolean = false;\n\n constructor(public galleria: Galleria) {}\n\n ngOnChanges({ autoPlay }: SimpleChanges): void {\n if (autoPlay?.currentValue) {\n this.startSlideShow.emit();\n }\n\n if (autoPlay && autoPlay.currentValue === false) {\n this.stopTheSlideShow();\n }\n }\n\n next() {\n let nextItemIndex = this.activeIndex + 1;\n let activeIndex = this.circular && (<any[]>this.value).length - 1 === this.activeIndex ? 0 : nextItemIndex;\n this.onActiveIndexChange.emit(activeIndex);\n }\n\n prev() {\n let prevItemIndex = this.activeIndex !== 0 ? this.activeIndex - 1 : 0;\n let activeIndex = this.circular && this.activeIndex === 0 ? (<any[]>this.value).length - 1 : prevItemIndex;\n this.onActiveIndexChange.emit(activeIndex);\n }\n\n onButtonFocus(pos: 'left' | 'right') {\n if (pos === 'left') {\n this.leftButtonFocused = true;\n } else this.rightButtonFocused = true;\n }\n\n onButtonBlur(pos: 'left' | 'right') {\n if (pos === 'left') {\n this.leftButtonFocused = false;\n } else this.rightButtonFocused = false;\n }\n\n stopTheSlideShow() {\n if (this.slideShowActive && this.stopSlideShow) {\n this.stopSlideShow.emit();\n }\n }\n\n navForward(e: MouseEvent) {\n this.stopTheSlideShow();\n this.next();\n\n if (e && e.cancelable) {\n e.preventDefault();\n }\n }\n\n navBackward(e: MouseEvent) {\n this.stopTheSlideShow();\n this.prev();\n\n if (e && e.cancelable) {\n e.preventDefault();\n }\n }\n\n onIndicatorClick(index: number) {\n this.stopTheSlideShow();\n this.onActiveIndexChange.emit(index);\n }\n\n onIndicatorMouseEnter(index: number) {\n if (this.changeItemOnIndicatorHover) {\n this.stopTheSlideShow();\n this.onActiveIndexChange.emit(index);\n }\n }\n\n onIndicatorKeyDown(event, index: number) {\n switch (event.code) {\n case 'Enter':\n case 'Space':\n this.stopTheSlideShow();\n this.onActiveIndexChange.emit(index);\n event.preventDefault();\n break;\n\n case 'ArrowDown':\n case 'ArrowUp':\n event.preventDefault();\n break;\n\n default:\n break;\n }\n }\n\n isNavForwardDisabled() {\n return !this.circular && this.activeIndex === (<any[]>this.value).length - 1;\n }\n\n isNavBackwardDisabled() {\n return !this.circular && this.activeIndex === 0;\n }\n\n isIndicatorItemActive(index: number) {\n return this.activeIndex === index;\n }\n\n ariaSlideLabel() {\n return this.galleria.config.translation.aria ? this.galleria.config.translation.aria.slide : undefined;\n }\n\n ariaSlideNumber(value) {\n return this.galleria.config.translation.aria ? this.galleria.config.translation.aria.slideNumber.replace(/{slideNumber}/g, value) : undefined;\n }\n\n ariaPageLabel(value) {\n return this.galleria.config.translation.aria ? this.galleria.config.translation.aria.pageLabel.replace(/{page}/g, value) : undefined;\n }\n}\n\n@Component({\n selector: 'p-galleriaThumbnails',\n template: `\n <div class=\"p-galleria-thumbnail-wrapper\">\n <div class=\"p-galleria-thumbnail-container\">\n <button\n *ngIf=\"showThumbnailNavigators\"\n type=\"button\"\n [ngClass]=\"{ 'p-galleria-thumbnail-prev p-link': true, 'p-disabled': this.isNavBackwardDisabled() }\"\n (click)=\"navBackward($event)\"\n [disabled]=\"isNavBackwardDisabled()\"\n pRipple\n [attr.aria-label]=\"ariaPrevButtonLabel()\"\n >\n <ng-container *ngIf=\"!galleria.previousThumbnailIconTemplate\">\n <ChevronLeftIcon *ngIf=\"!isVertical\" [styleClass]=\"'p-galleria-thumbnail-prev-icon'\" />\n <ChevronUpIcon *ngIf=\"isVertical\" [styleClass]=\"'p-galleria-thumbnail-prev-icon'\" />\n </ng-container>\n <ng-template *ngTemplateOutlet=\"galleria.previousThumbnailIconTemplate\"></ng-template>\n </button>\n <div class=\"p-galleria-thumbnail-items-container\" [ngStyle]=\"{ height: isVertical ? contentHeight : '' }\">\n <div #itemsContainer class=\"p-galleria-thumbnail-items\" (transitionend)=\"onTransitionEnd()\" (touchstart)=\"onTouchStart($event)\" (touchmove)=\"onTouchMove($event)\" role=\"tablist\">\n <div\n *ngFor=\"let item of value; let index = index\"\n [ngClass]=\"{\n 'p-galleria-thumbnail-item': true,\n 'p-galleria-thumbnail-item-current': activeIndex === index,\n 'p-galleria-thumbnail-item-active': isItemActive(index),\n 'p-galleria-thumbnail-item-start': firstItemAciveIndex() === index,\n 'p-galleria-thumbnail-item-end': lastItemActiveIndex() === index\n }\"\n [attr.aria-selected]=\"activeIndex === index\"\n [attr.aria-controls]=\"containerId + '_item_' + index\"\n [attr.data-pc-section]=\"'thumbnailitem'\"\n [attr.data-p-active]=\"activeIndex === index\"\n (keydown)=\"onThumbnailKeydown($event, index)\"\n >\n <div\n class=\"p-galleria-thumbnail-item-content\"\n [attr.tabindex]=\"activeIndex === index ? 0 : -1\"\n [attr.aria-current]=\"activeIndex === index ? 'page' : undefined\"\n [attr.aria-label]=\"ariaPageLabel(index + 1)\"\n (click)=\"onItemClick(index)\"\n (touchend)=\"onItemClick(index)\"\n (keydown.enter)=\"onItemClick(index)\"\n >\n <p-galleriaItemSlot type=\"thumbnail\" [item]=\"item\" [templates]=\"templates\"></p-galleriaItemSlot>\n </div>\n </div>\n </div>\n </div>\n <button\n *ngIf=\"showThumbnailNavigators\"\n type=\"button\"\n [ngClass]=\"{ 'p-galleria-thumbnail-next p-link': true, 'p-disabled': this.isNavForwardDisabled() }\"\n (click)=\"navForward($event)\"\n [disabled]=\"isNavForwardDisabled()\"\n pRipple\n [attr.aria-label]=\"ariaNextButtonLabel()\"\n >\n <ng-container *ngIf=\"!galleria.nextThumbnailIconTemplate\">\n <ChevronRightIcon *ngIf=\"!isVertical\" [ngClass]=\"'p-galleria-thumbnail-next-icon'\" />\n <ChevronDownIcon *ngIf=\"isVertical\" [ngClass]=\"'p-galleria-thumbnail-next-icon'\" />\n </ng-container>\n <ng-template *ngTemplateOutlet=\"galleria.nextThumbnailIconTemplate\"></ng-template>\n </button>\n </div>\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class GalleriaThumbnails implements OnInit, AfterContentChecked, AfterViewInit, OnDestroy {\n @Input() containerId: string | undefined;\n\n @Input() value: any[] | undefined;\n\n @Input({ transform: booleanAttribute }) isVertical: boolean = false;\n\n @Input({ transform: booleanAttribute }) slideShowActive: boolean = false;\n\n @Input({ transform: booleanAttribute }) circular: boolean = false;\n\n @Input() responsiveOptions: GalleriaResponsiveOptions[] | undefined;\n\n @Input() contentHeight: string = '300px';\n\n @Input() showThumbnailNavigators = true;\n\n @Input() templates: QueryList<PrimeTemplate> | undefined;\n\n @Output() onActiveIndexChange: EventEmitter<number> = new EventEmitter();\n\n @Output() stopSlideShow: EventEmitter<Event> = new EventEmitter();\n\n @ViewChild('itemsContainer') itemsContainer: ElementRef | undefined;\n\n @Input() get numVisible(): number {\n return this._numVisible;\n }\n\n set numVisible(numVisible) {\n this._numVisible = numVisible;\n this._oldNumVisible = this.d_numVisible;\n this.d_numVisible = numVisible;\n }\n\n @Input() get activeIndex(): number {\n return this._activeIndex;\n }\n\n set activeIndex(activeIndex) {\n this._oldactiveIndex = this._activeIndex;\n this._activeIndex = activeIndex;\n }\n\n index: number | undefined;\n\n startPos: { x: number; y: number } | null = null;\n\n thumbnailsStyle: HTMLStyleElement | null = null;\n\n sortedResponsiveOptions: GalleriaResponsiveOptions[] | null = null;\n\n totalShiftedItems: number = 0;\n\n page: number = 0;\n\n documentResizeListener: VoidListener;\n\n _numVisible: number = 0;\n\n d_numVisible: number = 0;\n\n _oldNumVisible: number = 0;\n\n _activeIndex: number = 0;\n\n _oldactiveIndex: number = 0;\n\n constructor(\n public galleria: Galleria,\n @Inject(DOCUMENT) private document: Document,\n @Inject(PLATFORM_ID) private platformId: any,\n private renderer: Renderer2,\n private cd: ChangeDetectorRef\n ) {}\n\n ngOnInit() {\n if (isPlatformBrowser(this.platformId)) {\n this.createStyle();\n\n if (this.responsiveOptions) {\n this.bindDocumentListeners();\n }\n }\n }\n\n ngAfterContentChecked() {\n let totalShiftedItems = this.totalShiftedItems;\n\n if ((this._oldNumVisible !== this.d_numVisible || this._oldactiveIndex !== this._activeIndex) && this.itemsContainer) {\n if (this._activeIndex <= this.getMedianItemIndex()) {\n totalShiftedItems = 0;\n } else if ((<any[]>this.value).length - this.d_numVisible + this.getMedianItemIndex() < this._activeIndex) {\n totalShiftedItems = this.d_numVisible - (<any[]>this.value).length;\n } else if ((<any[]>this.value).length - this.d_numVisible < this._activeIndex && this.d_numVisible % 2 === 0) {\n totalShiftedItems = this._activeIndex * -1 + this.getMedianItemIndex() + 1;\n } else {\n totalShiftedItems = this._activeIndex * -1 + this.getMedianItemIndex();\n }\n\n if (totalShiftedItems !== this.totalShiftedItems) {\n this.totalShiftedItems = totalShiftedItems;\n }\n\n if (this.itemsContainer && this.itemsContainer.nativeElement) {\n this.itemsContainer.nativeElement.style.transform = this.isVertical ? `translate3d(0, ${totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`;\n }\n\n if (this._oldactiveIndex !== this._activeIndex) {\n DomHandler.removeClass(this.itemsContainer.nativeElement, 'p-items-hidden');\n this.itemsContainer.nativeElement.style.transition = 'transform 500ms ease 0s';\n }\n\n this._oldactiveIndex = this._activeIndex;\n this._oldNumVisible = this.d_numVisible;\n }\n }\n\n ngAfterViewInit() {\n if (isPlatformBrowser(this.platformId)) {\n this.calculatePosition();\n }\n }\n\n createStyle() {\n if (!this.thumbnailsStyle) {\n this.thumbnailsStyle = this.document.createElement('style');\n this.document.body.appendChild(this.thumbnailsStyle);\n }\n\n let innerHTML = `\n #${this.containerId} .p-galleria-thumbnail-item {\n flex: 1 0 ${100 / this.d_numVisible}%\n }\n `;\n\n if (this.responsiveOptions) {\n this.sortedResponsiveOptions = [...this.responsiveOptions];\n this.sortedResponsiveOptions.sort((data1, data2) => {\n const value1 = data1.breakpoint;\n const value2 = data2.breakpoint;\n let result = null;\n\n if (value1 == null && value2 != null) result = -1;\n else if (value1 != null && value2 == null) result = 1;\n else if (value1 == null && value2 == null) result = 0;\n else if (typeof value1 === 'string' && typeof value2 === 'string') result = value1.localeCompare(value2, undefined, { numeric: true });\n else result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0;\n\n return -1 * result;\n });\n\n for (let i = 0; i < this.sortedResponsiveOptions.length; i++) {\n let res = this.sortedResponsiveOptions[i];\n\n innerHTML += `\n @media screen and (max-width: ${res.breakpoint}) {\n #${this.containerId} .p-galleria-thumbnail-item {\n flex: 1 0 ${100 / res.numVisible}%\n }\n }\n `;\n }\n }\n\n this.thumbnailsStyle.innerHTML = innerHTML;\n DomHandler.setAttribute(this.thumbnailsStyle, 'nonce', this.galleria.config?.csp()?.nonce);\n }\n\n calculatePosition() {\n if (isPlatformBrowser(this.platformId)) {\n if (this.itemsContainer && this.sortedResponsiveOptions) {\n let windowWidth = window.innerWidth;\n let matchedResponsiveData = {\n numVisible: this._numVisible\n };\n\n for (let i = 0; i < this.sortedResponsiveOptions.length; i++) {\n let res = this.sortedResponsiveOptions[i];\n\n if (parseInt(res.breakpoint, 10) >= windowWidth) {\n matchedResponsiveData = res;\n }\n }\n\n if (this.d_numVisible !== matchedResponsiveData.numVisible) {\n this.d_numVisible = matchedResponsiveData.numVisible;\n this.cd.markForCheck();\n }\n }\n }\n }\n\n getTabIndex(index: number) {\n return this.isItemActive(index) ? 0 : null;\n }\n\n navForward(e: TouchEvent | MouseEvent) {\n this.stopTheSlideShow();\n\n let nextItemIndex = this._activeIndex + 1;\n if (nextItemIndex + this.totalShiftedItems > this.getMedianItemIndex() && (-1 * this.totalShiftedItems < this.getTotalPageNumber() - 1 || this.circular)) {\n this.step(-1);\n }\n\n let activeIndex = this.circular && (<any[]>this.value).length - 1 === this._activeIndex ? 0 : nextItemIndex;\n this.onActiveIndexChange.emit(activeIndex);\n\n if (e.cancelable) {\n e.preventDefault();\n }\n }\n\n navBackward(e: TouchEvent | MouseEvent) {\n this.stopTheSlideShow();\n\n let prevItemIndex = this._activeIndex !== 0 ? this._activeIndex - 1 : 0;\n let diff = prevItemIndex + this.totalShiftedItems;\n if (this.d_numVisible - diff - 1 > this.getMedianItemIndex() && (-1 * this.totalShiftedItems !== 0 || this.circular)) {\n this.step(1);\n }\n\n let activeIndex = this.circular && this._activeIndex === 0 ? (<any[]>this.value).length - 1 : prevItemIndex;\n this.onActiveIndexChange.emit(activeIndex);\n\n if (e.cancelable) {\n e.preventDefault();\n }\n }\n\n onItemClick(index: number) {\n this.stopTheSlideShow();\n\n let selectedItemIndex = index;\n if (selectedItemIndex !== this._activeIndex) {\n const diff = selectedItemIndex + this.totalShiftedItems;\n let dir = 0;\n if (selectedItemIndex < this._activeIndex) {\n dir = this.d_numVisible - diff - 1 - this.getMedianItemIndex();\n if (dir > 0 && -1 * this.totalShiftedItems !== 0) {\n this.step(dir);\n }\n } else {\n dir = this.getMedianItemIndex() - diff;\n if (dir < 0 && -1 * this.totalShiftedItems < this.getTotalPageNumber() - 1) {\n this.step(dir);\n }\n }\n\n this.activeIndex = selectedItemIndex;\n this.onActiveIndexChange.emit(this.activeIndex);\n }\n }\n\n onThumbnailKeydown(event: KeyboardEvent, index: number) {\n if (event.code === 'Enter' || event.code === 'Space') {\n this.onItemClick(index);\n event.preventDefault();\n }\n\n switch (event.code) {\n case 'ArrowRight':\n this.onRightKey();\n break;\n\n case 'ArrowLeft':\n this.onLeftKey();\n break;\n\n case 'Home':\n this.onHomeKey();\n event.preventDefault();\n break;\n\n case 'End':\n this.onEndKey();\n event.preventDefault();\n break;\n\n case 'ArrowUp':\n case 'ArrowDown':\n event.preventDefault();\n break;\n\n case 'Tab':\n this.onTabKey();\n break;\n\n default:\n break;\n }\n }\n\n onRightKey() {\n const indicators = DomHandler.find(this.itemsContainer.nativeElement, '[data-pc-section=\"thumbnailitem\"]');\n const activeIndex = this.findFocusedIndicatorIndex();\n\n this.changedFocusedIndicator(activeIndex, activeIndex + 1 === indicators.length ? indicators.length - 1 : activeIndex + 1);\n }\n\n onLeftKey() {\n const activeIndex = this.findFocusedIndicatorIndex();\n\n this.changedFocusedIndicator(activeIndex, activeIndex - 1 <= 0 ? 0 : activeIndex - 1);\n }\n\n onHomeKey() {\n const activeIndex = this.findFocusedIndicatorIndex();\n\n this.changedFocusedIndicator(activeIndex, 0);\n }\n\n onEndKey() {\n const indicators = DomHandler.find(this.itemsContainer.nativeElement, '[data-pc-section=\"thumbnailitem\"]');\n const activeIndex = this.findFocusedIndicatorIndex();\n\n this.changedFocusedIndicator(activeIndex, indicators.length - 1);\n }\n\n onTabKey() {\n const indicators = [...DomHandler.find(this.itemsContainer.nativeElement, '[data-pc-section=\"thumbnailitem\"]')];\n const highlightedIndex = indicators.findIndex((ind) => DomHandler.getAttribute(ind, 'data-p-active') === true);\n\n const activeIndicator = DomHandler.findSingle(this.itemsContainer.nativeElement, '[tabindex=\"0\"]');\n\n const activeIndex = indicators.findIndex((ind) => ind === activeIndicator.parentElement);\n\n indicators[activeIndex].children[0].tabIndex = '-1';\n indicators[highlightedIndex].children[0].tabIndex = '0';\n }\n\n findFocusedIndicatorIndex() {\n const indicators = [...DomHandler.find(this.itemsContainer.nativeElement, '[data-pc-section=\"thumbnailitem\"]')];\n const activeIndicator = DomHandler.findSingle(this.itemsContainer.nativeElement, '[data-pc-section=\"thumbnailitem\"] > [tabindex=\"0\"]');\n\n return indicators.findIndex((ind) => ind === activeIndicator.parentElement);\n }\n\n changedFocusedIndicator(prevInd, nextInd) {\n const indicators = DomHandler.find(this.itemsContainer.nativeElement, '[data-pc-section=\"thumbnailitem\"]');\n\n indicators[prevInd].children[0].tabIndex = '-1';\n indicators[nextInd].children[0].tabIndex = '0';\n indicators[nextInd].children[0].focus();\n }\n\n step(dir: number) {\n let totalShiftedItems = this.totalShiftedItems + dir;\n\n if (dir < 0 && -1 * totalShiftedItems + this.d_numVisible > (<any[]>this.value).length - 1) {\n totalShiftedItems = this.d_numVisible - (<any[]>this.value).length;\n } else if (dir > 0 && totalShiftedItems > 0) {\n totalShiftedItems = 0;\n }\n\n if (this.circular) {\n if (dir < 0 && (<any[]>this.value).length - 1 === this._activeIndex) {\n totalShiftedItems = 0;\n } else if (dir > 0 && this._activeIndex === 0) {\n totalShiftedItems = this.d_numVisible - (<any[]>this.value).length;\n }\n }\n\n if (this.itemsContainer) {\n DomHandler.removeClass(this.itemsContainer.nativeElement, 'p-items-hidden');\n this.itemsContainer.nativeElement.style.transform = this.isVertical ? `translate3d(0, ${totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`;\n