@ngfly/carousel
Version:
A smooth, customizable carousel component for Angular 17+ applications
1 lines • 39.7 kB
JavaScript
import*as t from"@angular/core";import{Injectable as e,inject as i,ChangeDetectorRef as n,NgZone as s,Component as r,Input as o,ContentChild as a,ViewChild as l,Directive as c,NgModule as h}from"@angular/core";import*as d from"@angular/common";import{CommonModule as u}from"@angular/common";import{BehaviorSubject as m,Subject as g,fromEvent as p}from"rxjs";import{debounceTime as f,takeUntil as v}from"rxjs/operators";const y={NAVIGATION_SIZE:"32px",CONTENT_PADDING:"10px",ANIMATION_DURATION:"300ms",ANIMATION_TIMING:"ease",EMPTY_STATE_HEIGHT:"200px",NAVIGATION_PREV_ICON:"❮",NAVIGATION_NEXT_ICON:"❯",INDICATOR_SIZE:"10px",INDICATOR_SPACING:"5px",INDICATOR_ACTIVE_COLOR:"#333",INDICATOR_INACTIVE_COLOR:"#ccc",INDICATOR_ACTIVE_OPACITY:"1",INDICATOR_INACTIVE_OPACITY:"0.5",INDICATOR_ANIMATION_DURATION:"250ms",INDICATOR_ANIMATION_TIMING:"ease",INDICATOR_TRANSITION:"all 0.3s ease-in-out"};class x{constructor(){this.isVisibleSubject=new m(!0),this.isVisible$=this.isVisibleSubject.asObservable()}setVisibility(t){this.isVisibleSubject.next(t)}getButtonShapeStyles(t){const e={};switch(t){case"circle":e.borderRadius="50%";break;case"rounded":e.borderRadius="8px";break;default:e.borderRadius="0"}return e}getIndicatorStyles(t,e=!1){const i={width:y.INDICATOR_SIZE,height:y.INDICATOR_SIZE,display:"inline-block",transition:t?.transition||(t?.animation?.timing?`all ${t.animation.duration||y.INDICATOR_ANIMATION_DURATION} ${t.animation.timing}`:y.INDICATOR_TRANSITION),cursor:"pointer",margin:`0 ${t?.spacing||y.INDICATOR_SPACING}`,borderRadius:"50%"};if(e){i.backgroundColor=y.INDICATOR_ACTIVE_COLOR,i.opacity=y.INDICATOR_ACTIVE_OPACITY,i.transform="scale(1.2)";const e=!1!==t?.animation?.enabled,n=t?.animation?.type||"pulse";e&&"none"!==n&&("custom"===n&&t?.animation?.custom?i.animation=t.animation.custom:"pulse"===n&&(i.animation="indicator-pulse 1s infinite alternate")),t?.active&&Object.assign(i,t.active)}else i.backgroundColor=y.INDICATOR_INACTIVE_COLOR,i.opacity=y.INDICATOR_INACTIVE_OPACITY,i.transform="scale(1)",t?.inactive&&Object.assign(i,t.inactive);return i}getIndicatorContainerStyles(t){const e={position:"absolute",display:"flex",justifyContent:"center",alignItems:"center",zIndex:"100",padding:"10px",pointerEvents:"auto"};return e.bottom=t?.position?.bottom||"4px",e.left=t?.position?.left||"50%",e.transform="translateX(-50%)",t?.position&&(Object.keys(t.position).forEach((i=>{const n=t.position;n[i]&&(e[i]=n[i])})),"50%"!==t.position.left||e.transform?"50%"!==t.position.top||e.transform||(e.transform="translateY(-50%)"):e.transform="translateX(-50%)"),t?.container&&Object.assign(e,t.container),e}getNavigationIcons(t,e){const i={horizontal:{prev:y.NAVIGATION_PREV_ICON,next:y.NAVIGATION_NEXT_ICON},vertical:{prev:y.NAVIGATION_PREV_ICON,next:y.NAVIGATION_NEXT_ICON}},n=e||{},s=n.vertical||{};return{prev:t?s.prev||i.vertical.prev:n.prev||i.horizontal.prev,next:t?s.next||i.vertical.next:n.next||i.horizontal.next}}parseTimeToMs(t){return t?t.endsWith("ms")?parseInt(t.slice(0,-2),10):t.endsWith("s")?1e3*parseFloat(t.slice(0,-1)):parseInt(t,10):300}}x.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:x,deps:[],target:t.ɵɵFactoryTarget.Injectable}),x.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:x,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:x,decorators:[{type:e,args:[{providedIn:"root"}]}]});const I={linear:t=>t,ease:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>t*(2-t),easeInOutQuad:t=>t<.5?2*t*t:(4-2*t)*t-1,easeInCubic:t=>t*t*t,easeOutCubic:t=>--t*t*t+1,easeInOutCubic:t=>t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1};function b(t){switch(t){case"linear":return I.linear;case"ease-in":return I.easeInQuad;case"ease-out":return I.easeOutQuad;case"ease-in-out":default:return I.easeInOutQuad;case"ease":return I.ease}}function _(t,e){return e?`translateY(-${t}px)`:`translateX(-${t}px)`}function w(t,e=300){return t?t.endsWith("ms")?parseInt(t,10):t.endsWith("s")?1e3*parseFloat(t):parseInt(t,10)||e:e}function S(t,e,i,n=.8){if(!t)return e*n;if(t.endsWith("%")){return e*(parseFloat(t)/100)}return i[t]?i[t]:t.endsWith("px")?parseFloat(t):e*n}function T(t,e,i=300,n=I.easeInOutQuad){return new Promise((s=>{const r=t.scrollLeft,o=e-r,a=performance.now();requestAnimationFrame((function l(c){const h=c-a;if(h>=i)return t.scrollLeft=e,void s();const d=n(h/i);t.scrollLeft=r+o*d,requestAnimationFrame(l)}))}))}function O(t,e,i=300,n=I.easeInOutQuad){return new Promise((s=>{const r=t.scrollTop,o=e-r,a=performance.now();requestAnimationFrame((function l(c){const h=c-a;if(h>=i)return t.scrollTop=e,void s();const d=n(h/i);t.scrollTop=r+o*d,requestAnimationFrame(l)}))}))}class C{constructor(){this.cdr=i(n),this.ngZone=i(s),this.carouselService=i(x),this.slides=[],this.configs={},this.currentTranslate=0,this.currentIndex=0,this.destroy$=new g,this.itemWidths=[],this.itemHeights=[],this.containerWidth=0,this.containerHeight=0,this.intersectionObserver=null,this.resizeObserver=null,this.visibilityChangeTimeout=null,this.initialized=!1,this.scrollSizeMap={xs:50,sm:100,md:150,lg:200,xl:250,"2xl":300,"3xl":350,"4xl":400,"5xl":450,"6xl":500,"7xl":550,"8xl":600,"9xl":650,"10xl":700,full:1},this.showPrevButton=!1,this.showNextButton=!1,this.filteredItems=[],this.emptyStateText="No items found",this.emptyStateIcon="📭",this.emptyStateTextColor="#666",this.showEmptyStateIcon=!0}ngOnInit(){this.filteredItems=this.slides||[];this.filteredItems.length>1&&(this.showPrevButton=!0,this.showNextButton=!0),this.configs.emptyState&&(this.emptyStateText=this.configs.emptyState.text||this.emptyStateText,this.emptyStateIcon=this.configs.emptyState.icon||this.emptyStateIcon,this.emptyStateTextColor=this.configs.emptyState.textColor||this.emptyStateTextColor,this.showEmptyStateIcon=!this.configs.emptyState.hideIcon)}ngOnChanges(t){t.slides&&(this.filteredItems=this.slides||[],this.initialized&&setTimeout((()=>{this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow(),this.cdr.detectChanges()}))),t.configs&&this.initialized&&setTimeout((()=>{this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow(),this.setupAutoplay(),this.cdr.detectChanges()}))}setupAutoplay(){if(this.clearAutoplayInterval(),!this.configs.autoplay)return;const t=this.carouselService.parseTimeToMs(this.configs.autoplayDelay||"3000ms");this.autoplayInterval=setInterval((()=>{const t=this.trackElement?.nativeElement,e=this.wrapperElement?.nativeElement;if(!t||!e)return;const i=this.isVertical?t.offsetHeight-e.offsetHeight:t.offsetWidth-e.offsetWidth;if(this.currentTranslate>=i){if(!this.configs.loop)return void this.clearAutoplayInterval();this.currentTranslate=0,this.currentIndex=0}else this.next();this.cdr.detectChanges()}),t)}clearAutoplayInterval(){this.autoplayInterval&&(clearInterval(this.autoplayInterval),this.autoplayInterval=void 0)}ngAfterViewInit(){this.ngZone.runOutsideAngular((()=>{setTimeout((()=>{this.ngZone.run((()=>{this.initializeCarousel(),this.setupResizeListener(),this.setupIntersectionObserver(),this.setupResizeObserver(),this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow(),this.initialized=!0,this.cdr.detectChanges(),setTimeout((()=>{this.setupAutoplay()})),setTimeout((()=>{this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow(),this.cdr.detectChanges()}),100)}))}))}))}ngOnDestroy(){this.autoplayInterval&&(clearInterval(this.autoplayInterval),this.autoplayInterval=void 0),this.intersectionObserver&&(this.intersectionObserver.disconnect(),this.intersectionObserver=null),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.visibilityChangeTimeout&&(clearTimeout(this.visibilityChangeTimeout),this.visibilityChangeTimeout=null),this.destroy$.next(),this.destroy$.complete(),this.itemWidths=[],this.itemHeights=[],this.filteredItems=[]}get isVertical(){return"vertical"===this.configs.orientation}get containerStyles(){const t=this.configs.containerStyle||{};return"width"in t||(t.width=this.configs.containerWidth||"100%"),"height"in t||(t.height=this.configs.containerHeight||"auto"),t}get trackStyles(){const t={transform:_(this.currentTranslate,this.isVertical)};return this.isVertical?{...t,flexDirection:"column"}:t}initializeCarousel(){this.trackElement&&this.wrapperElement&&(this.currentTranslate=0,this.currentIndex=0,this.checkOverflow())}setupResizeListener(){p(window,"resize").pipe(f(200),v(this.destroy$)).subscribe((()=>{this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow()}))}setupIntersectionObserver(){"IntersectionObserver"in window&&this.ngZone.runOutsideAngular((()=>{this.intersectionObserver=new IntersectionObserver((t=>{const e=t[0]?.isIntersecting;e&&this.initialized&&this.ngZone.run((()=>{this.visibilityChangeTimeout=setTimeout((()=>{this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow()}),50)}))}),{threshold:.1}),this.wrapperElement?.nativeElement&&this.intersectionObserver.observe(this.wrapperElement.nativeElement)}))}setupResizeObserver(){"ResizeObserver"in window&&this.ngZone.runOutsideAngular((()=>{this.resizeObserver=new ResizeObserver((()=>{this.initialized&&this.ngZone.run((()=>{this.updateContainerDimensions(),this.calculateItemDimensions(),this.checkOverflow()}))})),this.wrapperElement?.nativeElement&&this.resizeObserver.observe(this.wrapperElement.nativeElement)}))}updateContainerDimensions(){this.wrapperElement&&(this.containerWidth=this.wrapperElement.nativeElement.offsetWidth,this.containerHeight=this.wrapperElement.nativeElement.offsetHeight)}checkOverflow(){if(!this.initialized&&!this.trackElement?.nativeElement)return;if(this.filteredItems.length>1&&this.configs.singleItemMode)return this.configs.loop?(this.showPrevButton=!0,void(this.showNextButton=!0)):(this.showPrevButton=this.currentIndex>0,void(this.showNextButton=this.currentIndex<this.filteredItems.length-1));if(!this.showNavigation||this.filteredItems.length<=1)return this.showPrevButton=!1,void(this.showNextButton=!1);const t=this.trackElement?.nativeElement,e=this.wrapperElement?.nativeElement;if(t&&e&&0!==e.offsetWidth&&0!==e.offsetHeight){if(this.configs.loop&&this.filteredItems.length>1)this.showPrevButton=!0,this.showNextButton=!0;else if(this.showPrevButton=this.currentTranslate>0,this.isVertical){const i=t.offsetHeight,n=e.offsetHeight;this.showNextButton=i-this.currentTranslate>n+1}else{const i=t.offsetWidth,n=e.offsetWidth;this.showNextButton=i-this.currentTranslate>n+1}this.cdr.detectChanges()}}calculateItemDimensions(){if(!this.trackElement||!this.wrapperElement)return;const t=this.wrapperElement.nativeElement,e=t.offsetHeight,i=t.offsetWidth;this.containerWidth=i,this.containerHeight=e;const n=Array.from(this.trackElement.nativeElement.children);0!==n.length&&setTimeout((()=>{if(this.configs.singleItemMode)return this.isVertical?(this.itemHeights=n.map((()=>e)),this.itemWidths=n.map((t=>{const e=window.getComputedStyle(t);return t.offsetWidth+parseInt(e.marginLeft||"0",10)+parseInt(e.marginRight||"0",10)}))):(this.itemWidths=n.map((()=>i)),this.itemHeights=n.map((t=>{const e=window.getComputedStyle(t);return t.offsetHeight+parseInt(e.marginTop||"0",10)+parseInt(e.marginBottom||"0",10)}))),this.updateTranslatePosition(),void this.cdr.detectChanges();this.itemWidths=n.map((t=>{const e=window.getComputedStyle(t);return t.offsetWidth+parseInt(e.marginLeft||"0",10)+parseInt(e.marginRight||"0",10)})),this.itemHeights=n.map((t=>{const e=window.getComputedStyle(t);return t.offsetHeight+parseInt(e.marginTop||"0",10)+parseInt(e.marginBottom||"0",10)})),this.cdr.detectChanges()}),50)}getScrollAmount(){const t=this.wrapperElement?.nativeElement;if(!t)return 0;if(this.configs.singleItemMode){if(this.isVertical&&this.itemHeights.length>0)return this.currentIndex<this.itemHeights.length?this.itemHeights[this.currentIndex]:this.itemHeights[0]||t.offsetHeight;if(this.itemWidths.length>0)return this.currentIndex<this.itemWidths.length?this.itemWidths[this.currentIndex]:this.itemWidths[0]||t.offsetWidth}const e=this.configs.scrollSize||"sm";if("full"===e)return this.isVertical?t.offsetHeight:t.offsetWidth;if("string"==typeof e&&e.endsWith("%")){const i=parseInt(e,10)/100;return this.isVertical?t.offsetHeight*i:t.offsetWidth*i}return this.scrollSizeMap[e]||this.scrollSizeMap.sm}previous(){if(this.showPrevButton){if(this.configs.singleItemMode)this.currentIndex>0?(this.currentIndex--,this.updateTranslatePosition()):this.configs.loop&&this.filteredItems.length>0&&(this.currentIndex=this.filteredItems.length-1,this.updateTranslatePosition());else{const t=this.getScrollAmount();this.currentTranslate=Math.max(0,this.currentTranslate-t)}this.checkOverflow()}}next(){if(!this.showNextButton)return;const t=this.trackElement?.nativeElement,e=this.wrapperElement?.nativeElement;if(t&&e){if(this.configs.singleItemMode)this.currentIndex<this.filteredItems.length-1?(this.currentIndex++,this.updateTranslatePosition()):this.configs.loop&&this.filteredItems.length>0&&(this.currentIndex=0,this.currentTranslate=0);else{const i=this.getScrollAmount(),n=this.isVertical?t.offsetHeight-e.offsetHeight:t.offsetWidth-e.offsetWidth;this.currentTranslate=Math.min(n,this.currentTranslate+i)}this.checkOverflow()}}updateTranslatePosition(){if(0===this.currentIndex)return void(this.currentTranslate=0);const t=this.configs.itemGap?parseInt(this.configs.itemGap.replace("px",""),10):0;let e=0;for(let i=0;i<this.currentIndex;i++)this.isVertical?e+=(this.itemHeights[i]||0)+t:e+=(this.itemWidths[i]||0)+t;this.currentTranslate=e}getItemStyle(t){const e={flexShrink:"0",flexGrow:"0",boxSizing:"border-box",overflow:"hidden",borderRadius:"inherit"};if(this.configs.itemWidth&&("100%"===this.configs.itemWidth&&this.containerWidth>0?(e.width=this.containerWidth+"px",e.maxWidth="100%"):e.width=this.configs.itemWidth),this.configs.itemHeight?("100%"===this.configs.itemHeight||parseInt(this.configs.itemHeight,10)===this.containerHeight)&&this.containerHeight>0?(e.height=this.containerHeight+"px",e.minHeight=this.containerHeight+"px",e.maxHeight="100%",e.display="flex",e.flexDirection="column",e.alignItems="stretch",e.justifyContent="stretch"):e.height=this.configs.itemHeight:this.isVertical&&this.configs.containerHeight&&(e.height=this.containerHeight+"px",e.minHeight=this.containerHeight+"px"),!this.configs.itemGap)return e;return e[this.isVertical?"marginTop":"marginLeft"]=0===t?"0":this.configs.itemGap,e}get contentPadding(){return this.configs.contentPadding||y.CONTENT_PADDING}get animationDuration(){return this.configs.animationDuration||y.ANIMATION_DURATION}get animationTiming(){return this.configs.animationTiming||y.ANIMATION_TIMING}get showNavigation(){return this.configs.showNavigation??!0}getEmptyStateContainerStyle(){return{width:"100%",boxSizing:"border-box",borderRadius:"inherit",backgroundColor:this.configs.emptyState?.backgroundColor||"transparent"}}hasItems(){return this.filteredItems?.length>0}getNavControlsClass(){return"carousel__nav-controls"}getNavControlsStyle(){const t={position:"absolute",top:"0",left:"0",width:"100%",height:"100%",pointerEvents:"none"};return this.configs.navigationStyle?.zIndex&&(t["--carousel-z-index"]=this.configs.navigationStyle.zIndex),t}get prevIcon(){return this.carouselService.getNavigationIcons(this.isVertical,this.configs.navigationStyle?.icons).prev}get nextIcon(){return this.carouselService.getNavigationIcons(this.isVertical,this.configs.navigationStyle?.icons).next}getIconStyles(t){return{color:t?this.configs.navigationStyle?.nextButton?.color||"#666":this.configs.navigationStyle?.prevButton?.color||"#666"}}getButtonPositionStyle(t){const e=this.configs.navigationStyle,i="prev"===t?e?.prevButton||{}:e?.nextButton||{},n={position:"absolute",pointerEvents:"auto"};["top","bottom","left","right"].forEach((t=>{const e=i[t];void 0!==e&&(n[t]=0===e||"0"===e?"0px":e)}));const s={"left=50%":"translateX(-50%)","right=50%":"translateX(50%)","top=50%":"translateY(-50%)","bottom=50%":"translateY(50%)"};for(const[t,e]of Object.entries(s)){const[s,r]=t.split("=");if(i[s]===r){n.transform=i.transform?`${i.transform} ${e}`:e;break}}!n.transform&&i.transform&&(n.transform=i.transform);return["top","bottom","left","right"].some((t=>void 0!==i[t]))||(this.isVertical?(n.left="50%",n.transform="translateX(-50%)",n["prev"===t?"top":"bottom"]="0px"):(n.top="50%",n.transform="translateY(-50%)",n["prev"===t?"left":"right"]="0px")),n}getButtonFullStyles(t){const e={...this.carouselService.getButtonShapeStyles(this.configs.navigationStyle?.buttonShape)},i=this.configs.navigationStyle?.[`${t}Button`];if(i){const n=this.getButtonPositionStyle(t);Object.assign(e,n);const s=this.configs.navigationStyle?.buttonShape;if(s){const{borderRadius:t,...n}=i;Object.assign(e,n)}else Object.assign(e,i);i.zIndex?e["--carousel-z-index"]=i.zIndex:this.configs.navigationStyle?.zIndex&&(e["--carousel-z-index"]=this.configs.navigationStyle.zIndex)}return e}getPrevButtonFullStyles(){return this.getButtonFullStyles("prev")}getNextButtonFullStyles(){return this.getButtonFullStyles("next")}getPrevIndex(t){const e=this.filteredItems?.length||0;return 0===e?t:t>0?t-1:this.configs.loop?e-1:t}goToPrevSlide(){const t=this.getPrevIndex(this.currentIndex);t!==this.currentIndex&&(this.currentIndex=t,this.updateTranslatePosition(),this.checkOverflow())}getIndicatorContainerStyles(){return this.carouselService.getIndicatorContainerStyles(this.configs.indicatorStyle)}getIndicatorItemStyles(t){const e=t===this.currentIndex;return this.carouselService.getIndicatorStyles(this.configs.indicatorStyle,e)}goToSlide(t){if(t===this.currentIndex||t<0||t>=this.filteredItems.length)return;if(this.currentIndex=t,this.configs.singleItemMode)return this.updateTranslatePosition(),void this.checkOverflow();const e=this.trackElement?.nativeElement,i=this.wrapperElement?.nativeElement;if(!e||!i)return;const n=parseInt(this.configs.itemGap?.replace("px","")||"0",10),s=(this.isVertical?this.itemHeights:this.itemWidths).slice(0,t).reduce(((t,e)=>t+(e||0)+n),0),r=this.isVertical?e.offsetHeight-i.offsetHeight:e.offsetWidth-i.offsetWidth;this.currentTranslate=Math.min(r,Math.max(0,s)),this.checkOverflow()}get showIndicators(){return this.configs.showIndicators??!1}}C.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:C,deps:[],target:t.ɵɵFactoryTarget.Component}),C.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"14.3.0",type:C,isStandalone:!0,selector:"carousel",inputs:{slides:"slides",configs:"configs"},providers:[x],queries:[{propertyName:"itemTemplate",first:!0,predicate:["carouselItem"],descendants:!0},{propertyName:"emptyStateTemplate",first:!0,predicate:["emptyState"],descendants:!0}],viewQueries:[{propertyName:"trackElement",first:!0,predicate:["track"],descendants:!0},{propertyName:"wrapperElement",first:!0,predicate:["wrapper"],descendants:!0}],usesOnChanges:!0,ngImport:t,template:'<div class="carousel" \n [class.carousel--vertical]="isVertical" \n [ngClass]="configs.containerClass" \n [ngStyle]="containerStyles"\n [style.height]="configs.containerHeight || \'auto\'"\n [style.width]="configs.containerWidth || \'auto\'">\n <div\n #wrapper\n [style.--content-padding]="contentPadding"\n class="carousel__wrapper">\n <div\n #track\n [ngStyle]="trackStyles"\n [class.carousel__track--vertical]="isVertical"\n [style.--animation-duration]="animationDuration"\n [style.--animation-timing]="animationTiming"\n class="carousel__track">\n <ng-container *ngIf="hasItems(); else emptyState">\n <ng-container *ngFor="let slide of filteredItems; let i = index">\n <div class="carousel__item" \n [ngClass]="configs.itemClass" \n [ngStyle]="getItemStyle(i)" \n [style.--item-width]="configs.itemWidth || \'auto\'"\n [style.--item-height]="configs.itemHeight || \'auto\'">\n <ng-container *ngIf="itemTemplate; else defaultTemplate">\n <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: slide, index: i }"></ng-container>\n </ng-container>\n <ng-template #defaultTemplate>\n <div class="carousel__item-default">\n {{ slide }}\n </div>\n </ng-template>\n </div>\n </ng-container>\n </ng-container>\n \n <ng-template #emptyState>\n <div class="carousel__empty-container" [ngStyle]="getEmptyStateContainerStyle()">\n <ng-container *ngIf="emptyStateTemplate; else defaultEmptyState">\n <ng-container *ngTemplateOutlet="emptyStateTemplate; context: { $implicit: emptyStateText }"></ng-container>\n </ng-container>\n <ng-template #defaultEmptyState>\n <div class="carousel__empty-state" [style.color]="emptyStateTextColor">\n <div *ngIf="showEmptyStateIcon" class="carousel__empty-icon">{{emptyStateIcon}}</div>\n <div class="carousel__empty-text">{{emptyStateText}}</div>\n </div>\n </ng-template>\n </div>\n </ng-template>\n </div>\n </div>\n\n <div *ngIf="showNavigation && hasItems()" [ngClass]="getNavControlsClass()" [ngStyle]="getNavControlsStyle()"> \n <button \n [class.carousel__nav-button--disabled]="!showPrevButton" \n [disabled]="!showPrevButton" \n [ngStyle]="getPrevButtonFullStyles()" \n (click)="previous()" \n style="pointer-events:auto;"\n class="carousel__nav-button">\n <span class="carousel__nav-icon" [ngStyle]="getIconStyles(false)">{{ prevIcon }}</span>\n </button>\n \n <button\n [class.carousel__nav-button--disabled]="!showNextButton"\n [disabled]="!showNextButton"\n [ngStyle]="getNextButtonFullStyles()"\n (click)="next()"\n style="pointer-events:auto;"\n class="carousel__nav-button">\n <span class="carousel__nav-icon" [ngStyle]="getIconStyles(true)">{{ nextIcon }}</span>\n </button>\n </div>\n\n \x3c!-- Carousel indicators --\x3e\n <div\n *ngIf="showIndicators && hasItems() && filteredItems.length > 1" \n class="carousel__indicators" \n [ngStyle]="getIndicatorContainerStyles()">\n <div\n *ngFor="let slide of filteredItems; let i = index"\n class="carousel__indicator"\n [class.carousel__indicator--active]="i === currentIndex"\n [ngStyle]="getIndicatorItemStyles(i)"\n (click)="goToSlide(i)">\n </div>\n </div>\n</div>\n',styles:[".carousel{--carousel-item-bg: #fff;--carousel-item-border-color: #e0e0e0;--carousel-empty-bg: #fff;--carousel-empty-color: #666;--carousel-nav-bg: #fff;--carousel-nav-border-color: #e0e0e0;--carousel-nav-disabled-bg: #f5f5f5;--carousel-nav-disabled-border: #ddd;--carousel-nav-disabled-opacity: .4;--carousel-empty-icon-size: 32px;--carousel-empty-text-size: 16px;--carousel-nav-icon-size: 16px;--carousel-nav-item-gap: 24px;--carousel-indicator-active-color: #333;--carousel-indicator-inactive-color: #ccc;--carousel-z-index: 100;position:relative;overflow:hidden;display:flex;flex-direction:column;border-radius:inherit;height:100%;min-height:inherit;box-sizing:border-box;width:fit-content}.carousel--vertical{flex-direction:column}.carousel__wrapper{flex:1;overflow:hidden;position:relative;width:100%;border-radius:inherit;height:100%;min-height:inherit;display:flex;flex-direction:column;box-sizing:border-box}.carousel__track{display:flex;flex-wrap:nowrap;transition:transform var(--animation-duration, .3s) var(--animation-timing, ease);width:fit-content;min-width:100%;height:fit-content!important;min-height:fit-content!important;border-radius:inherit;align-items:stretch;flex:1;box-sizing:border-box}.carousel__track--vertical{flex-direction:column;width:100%;height:fit-content!important;min-height:100%;align-items:stretch}.carousel__item{flex:0 0 auto;box-sizing:border-box;border-radius:inherit;min-height:100px;height:100%;display:flex;flex-direction:column;position:relative;overflow:hidden}.carousel__item[style*=--item-width]{flex:0 0 var(--item-width)!important;width:var(--item-width)!important;max-width:var(--item-width)!important;min-width:var(--item-width)!important}.carousel__item[style*=--item-height]{height:var(--item-height)!important;min-height:var(--item-height)!important;max-height:var(--item-height)!important}.carousel__item>*{height:100%!important;min-height:inherit!important;flex:1 1 auto!important;display:flex!important;flex-direction:column!important;box-sizing:border-box!important;overflow:hidden}.carousel--vertical .carousel__item{width:100%}.carousel__item-default{background:var(--carousel-item-bg);height:100%;width:100%;display:flex;align-items:center;justify-content:center;border:1px solid var(--carousel-item-border-color);border-radius:inherit;padding:20px;box-sizing:border-box;flex:1}.carousel__empty-container{width:100%;box-sizing:border-box;border-radius:inherit;flex:1;display:flex;align-items:center;justify-content:center;min-height:inherit;height:100%}.carousel__empty-state{background:var(--carousel-empty-bg);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:30px;text-align:center;color:var(--carousel-empty-color);border-radius:inherit;min-height:200px;width:100%;height:100%;flex:1}.carousel__nav-controls{position:absolute;display:flex;gap:0;z-index:var(--carousel-z-index);pointer-events:none;padding:0;margin:0;width:100%;height:100%;top:0;left:0}.carousel__nav-button{background:var(--carousel-nav-bg);border:1px solid var(--carousel-nav-border-color);width:32px;height:32px;padding:0;margin:0;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:var(--carousel-z-index);position:absolute;outline:none;box-shadow:0 2px 4px #0000001a}.carousel__nav-button:hover:not(.carousel__nav-button--disabled){opacity:.9;box-shadow:0 3px 6px #00000026}.carousel__nav-button--disabled{opacity:var(--carousel-nav-disabled-opacity);cursor:not-allowed;background-color:var(--carousel-nav-disabled-bg);border-color:var(--carousel-nav-disabled-border);pointer-events:none}.carousel__nav-icon{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:var(--carousel-nav-icon-size);line-height:1}.carousel--vertical .carousel__nav-icon{transform:rotate(90deg)}.carousel__empty-icon{font-size:var(--carousel-empty-icon-size);margin-bottom:12px}.carousel__empty-text{font-size:var(--carousel-empty-text-size);margin-bottom:12px}.carousel__indicators{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:5px;position:absolute;bottom:4px;left:50%;transform:translate(-50%);z-index:100;pointer-events:auto}.carousel--vertical .carousel__indicators{flex-direction:row;bottom:10px;left:50%;transform:translate(-50%)}.carousel__indicator{transition:all .3s ease-in-out;background-color:var(--carousel-indicator-inactive-color, #ccc);opacity:.5;cursor:pointer;border-radius:50%;width:10px;height:10px;min-width:8px;min-height:8px}.carousel__indicator:hover{opacity:.8}.carousel__indicator--active{transform:scale(1.2);background-color:var(--carousel-indicator-active-color, #333);opacity:1;animation:carousel__indicator-pulse 1s infinite alternate}:root{--carousel-nav-bg: #fff;--carousel-nav-border-color: #e0e0e0;--carousel-nav-disabled-bg: #f5f5f5;--carousel-nav-disabled-border: #ddd;--carousel-nav-disabled-opacity: .4}@keyframes carousel__indicator-pulse{0%{transform:scale(1.2);box-shadow:0 0 #3333334d}to{transform:scale(1.3);box-shadow:0 0 0 5px #3330}}@keyframes carousel__indicator-expand{0%{width:8px}to{width:36px}}\n"],dependencies:[{kind:"ngmodule",type:u},{kind:"directive",type:d.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:d.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:d.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:d.NgTemplateOutlet,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]},{kind:"directive",type:d.NgStyle,selector:"[ngStyle]",inputs:["ngStyle"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:C,decorators:[{type:r,args:[{selector:"carousel",standalone:!0,imports:[u],providers:[x],template:'<div class="carousel" \n [class.carousel--vertical]="isVertical" \n [ngClass]="configs.containerClass" \n [ngStyle]="containerStyles"\n [style.height]="configs.containerHeight || \'auto\'"\n [style.width]="configs.containerWidth || \'auto\'">\n <div\n #wrapper\n [style.--content-padding]="contentPadding"\n class="carousel__wrapper">\n <div\n #track\n [ngStyle]="trackStyles"\n [class.carousel__track--vertical]="isVertical"\n [style.--animation-duration]="animationDuration"\n [style.--animation-timing]="animationTiming"\n class="carousel__track">\n <ng-container *ngIf="hasItems(); else emptyState">\n <ng-container *ngFor="let slide of filteredItems; let i = index">\n <div class="carousel__item" \n [ngClass]="configs.itemClass" \n [ngStyle]="getItemStyle(i)" \n [style.--item-width]="configs.itemWidth || \'auto\'"\n [style.--item-height]="configs.itemHeight || \'auto\'">\n <ng-container *ngIf="itemTemplate; else defaultTemplate">\n <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: slide, index: i }"></ng-container>\n </ng-container>\n <ng-template #defaultTemplate>\n <div class="carousel__item-default">\n {{ slide }}\n </div>\n </ng-template>\n </div>\n </ng-container>\n </ng-container>\n \n <ng-template #emptyState>\n <div class="carousel__empty-container" [ngStyle]="getEmptyStateContainerStyle()">\n <ng-container *ngIf="emptyStateTemplate; else defaultEmptyState">\n <ng-container *ngTemplateOutlet="emptyStateTemplate; context: { $implicit: emptyStateText }"></ng-container>\n </ng-container>\n <ng-template #defaultEmptyState>\n <div class="carousel__empty-state" [style.color]="emptyStateTextColor">\n <div *ngIf="showEmptyStateIcon" class="carousel__empty-icon">{{emptyStateIcon}}</div>\n <div class="carousel__empty-text">{{emptyStateText}}</div>\n </div>\n </ng-template>\n </div>\n </ng-template>\n </div>\n </div>\n\n <div *ngIf="showNavigation && hasItems()" [ngClass]="getNavControlsClass()" [ngStyle]="getNavControlsStyle()"> \n <button \n [class.carousel__nav-button--disabled]="!showPrevButton" \n [disabled]="!showPrevButton" \n [ngStyle]="getPrevButtonFullStyles()" \n (click)="previous()" \n style="pointer-events:auto;"\n class="carousel__nav-button">\n <span class="carousel__nav-icon" [ngStyle]="getIconStyles(false)">{{ prevIcon }}</span>\n </button>\n \n <button\n [class.carousel__nav-button--disabled]="!showNextButton"\n [disabled]="!showNextButton"\n [ngStyle]="getNextButtonFullStyles()"\n (click)="next()"\n style="pointer-events:auto;"\n class="carousel__nav-button">\n <span class="carousel__nav-icon" [ngStyle]="getIconStyles(true)">{{ nextIcon }}</span>\n </button>\n </div>\n\n \x3c!-- Carousel indicators --\x3e\n <div\n *ngIf="showIndicators && hasItems() && filteredItems.length > 1" \n class="carousel__indicators" \n [ngStyle]="getIndicatorContainerStyles()">\n <div\n *ngFor="let slide of filteredItems; let i = index"\n class="carousel__indicator"\n [class.carousel__indicator--active]="i === currentIndex"\n [ngStyle]="getIndicatorItemStyles(i)"\n (click)="goToSlide(i)">\n </div>\n </div>\n</div>\n',styles:[".carousel{--carousel-item-bg: #fff;--carousel-item-border-color: #e0e0e0;--carousel-empty-bg: #fff;--carousel-empty-color: #666;--carousel-nav-bg: #fff;--carousel-nav-border-color: #e0e0e0;--carousel-nav-disabled-bg: #f5f5f5;--carousel-nav-disabled-border: #ddd;--carousel-nav-disabled-opacity: .4;--carousel-empty-icon-size: 32px;--carousel-empty-text-size: 16px;--carousel-nav-icon-size: 16px;--carousel-nav-item-gap: 24px;--carousel-indicator-active-color: #333;--carousel-indicator-inactive-color: #ccc;--carousel-z-index: 100;position:relative;overflow:hidden;display:flex;flex-direction:column;border-radius:inherit;height:100%;min-height:inherit;box-sizing:border-box;width:fit-content}.carousel--vertical{flex-direction:column}.carousel__wrapper{flex:1;overflow:hidden;position:relative;width:100%;border-radius:inherit;height:100%;min-height:inherit;display:flex;flex-direction:column;box-sizing:border-box}.carousel__track{display:flex;flex-wrap:nowrap;transition:transform var(--animation-duration, .3s) var(--animation-timing, ease);width:fit-content;min-width:100%;height:fit-content!important;min-height:fit-content!important;border-radius:inherit;align-items:stretch;flex:1;box-sizing:border-box}.carousel__track--vertical{flex-direction:column;width:100%;height:fit-content!important;min-height:100%;align-items:stretch}.carousel__item{flex:0 0 auto;box-sizing:border-box;border-radius:inherit;min-height:100px;height:100%;display:flex;flex-direction:column;position:relative;overflow:hidden}.carousel__item[style*=--item-width]{flex:0 0 var(--item-width)!important;width:var(--item-width)!important;max-width:var(--item-width)!important;min-width:var(--item-width)!important}.carousel__item[style*=--item-height]{height:var(--item-height)!important;min-height:var(--item-height)!important;max-height:var(--item-height)!important}.carousel__item>*{height:100%!important;min-height:inherit!important;flex:1 1 auto!important;display:flex!important;flex-direction:column!important;box-sizing:border-box!important;overflow:hidden}.carousel--vertical .carousel__item{width:100%}.carousel__item-default{background:var(--carousel-item-bg);height:100%;width:100%;display:flex;align-items:center;justify-content:center;border:1px solid var(--carousel-item-border-color);border-radius:inherit;padding:20px;box-sizing:border-box;flex:1}.carousel__empty-container{width:100%;box-sizing:border-box;border-radius:inherit;flex:1;display:flex;align-items:center;justify-content:center;min-height:inherit;height:100%}.carousel__empty-state{background:var(--carousel-empty-bg);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:30px;text-align:center;color:var(--carousel-empty-color);border-radius:inherit;min-height:200px;width:100%;height:100%;flex:1}.carousel__nav-controls{position:absolute;display:flex;gap:0;z-index:var(--carousel-z-index);pointer-events:none;padding:0;margin:0;width:100%;height:100%;top:0;left:0}.carousel__nav-button{background:var(--carousel-nav-bg);border:1px solid var(--carousel-nav-border-color);width:32px;height:32px;padding:0;margin:0;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:var(--carousel-z-index);position:absolute;outline:none;box-shadow:0 2px 4px #0000001a}.carousel__nav-button:hover:not(.carousel__nav-button--disabled){opacity:.9;box-shadow:0 3px 6px #00000026}.carousel__nav-button--disabled{opacity:var(--carousel-nav-disabled-opacity);cursor:not-allowed;background-color:var(--carousel-nav-disabled-bg);border-color:var(--carousel-nav-disabled-border);pointer-events:none}.carousel__nav-icon{display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:var(--carousel-nav-icon-size);line-height:1}.carousel--vertical .carousel__nav-icon{transform:rotate(90deg)}.carousel__empty-icon{font-size:var(--carousel-empty-icon-size);margin-bottom:12px}.carousel__empty-text{font-size:var(--carousel-empty-text-size);margin-bottom:12px}.carousel__indicators{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:5px;position:absolute;bottom:4px;left:50%;transform:translate(-50%);z-index:100;pointer-events:auto}.carousel--vertical .carousel__indicators{flex-direction:row;bottom:10px;left:50%;transform:translate(-50%)}.carousel__indicator{transition:all .3s ease-in-out;background-color:var(--carousel-indicator-inactive-color, #ccc);opacity:.5;cursor:pointer;border-radius:50%;width:10px;height:10px;min-width:8px;min-height:8px}.carousel__indicator:hover{opacity:.8}.carousel__indicator--active{transform:scale(1.2);background-color:var(--carousel-indicator-active-color, #333);opacity:1;animation:carousel__indicator-pulse 1s infinite alternate}:root{--carousel-nav-bg: #fff;--carousel-nav-border-color: #e0e0e0;--carousel-nav-disabled-bg: #f5f5f5;--carousel-nav-disabled-border: #ddd;--carousel-nav-disabled-opacity: .4}@keyframes carousel__indicator-pulse{0%{transform:scale(1.2);box-shadow:0 0 #3333334d}to{transform:scale(1.3);box-shadow:0 0 0 5px #3330}}@keyframes carousel__indicator-expand{0%{width:8px}to{width:36px}}\n"]}]}],propDecorators:{slides:[{type:o}],configs:[{type:o}],itemTemplate:[{type:a,args:["carouselItem"]}],emptyStateTemplate:[{type:a,args:["emptyState"]}],trackElement:[{type:l,args:["track"]}],wrapperElement:[{type:l,args:["wrapper"]}]}});class N{constructor(t,e){this.el=t,this.renderer=e,this.carouselLazyLoad="",this.errorImage="assets/images/no-image.png",this.isLoaded=!1}ngOnInit(){!this.isLoaded&&this.carouselLazyLoad&&("IntersectionObserver"in window?(this.observer=new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&(this.loadImage(),this.observer?.unobserve(this.el.nativeElement))}))})),this.observer.observe(this.el.nativeElement)):this.loadImage())}ngOnDestroy(){this.observer&&(this.observer.disconnect(),this.observer=void 0)}loadImage(){this.isLoaded=!0;const t=this.el.nativeElement;if("IMG"===t.tagName){this.renderer.setAttribute(t,"src",this.carouselLazyLoad);const e=()=>{this.renderer.setAttribute(t,"src",this.errorImage);this.renderer.listen(t,"error",e)()};this.renderer.listen(t,"error",e)}else this.renderer.setStyle(t,"background-image",`url('${this.carouselLazyLoad}')`)}}N.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:N,deps:[{token:t.ElementRef},{token:t.Renderer2}],target:t.ɵɵFactoryTarget.Directive}),N.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"14.3.0",type:N,isStandalone:!0,selector:"[carouselLazyLoad]",inputs:{carouselLazyLoad:"carouselLazyLoad",errorImage:"errorImage"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:N,decorators:[{type:c,args:[{selector:"[carouselLazyLoad]",standalone:!0}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:t.Renderer2}]},propDecorators:{carouselLazyLoad:[{type:o}],errorImage:[{type:o}]}});class z{}z.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:z,deps:[],target:t.ɵɵFactoryTarget.NgModule}),z.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"14.3.0",ngImport:t,type:z,imports:[C,N],exports:[C,N]}),z.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:z,imports:[C]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"14.3.0",ngImport:t,type:z,decorators:[{type:h,args:[{imports:[C,N],exports:[C,N]}]}]});export{y as CAROUSEL_DEFAULTS,C as CarouselComponent,z as CarouselModule,x as CarouselService,N as LazyLoadDirective,S as calculateScrollAmount,_ as createTranslation,I as easings,b as getCssTimingFunction,w as parseTimeToMs,T as smoothScroll,O as smoothScrollVertical};