cfc-ds
Version:
Design System do Conselho Federal de Contabilidade baseado no govbr-ds
467 lines • 95.9 kB
JavaScript
import { Component, ContentChild, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core';
import { fromEvent, interval } from 'rxjs';
import { tap } from 'rxjs/operators';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "@angular/flex-layout/extended";
export class CarouselComponent {
items = [];
config = {
loop: false,
autoPlay: false,
autoPlayInterval: 5000,
showNavigationButtons: true,
showPlayButtons: false,
showIndicator: true,
navigationButtonsPosition: 'outside', // 'inside' ou 'outside'
indicatorPosition: 'inside', // 'inside' ou 'outside'
indicatorType: 'simple',
itemsPerPage: 1,
navigationType: 'page',
transitionDuration: 300,
transitionType: 'slide',
enableTouch: true
};
pageChange = new EventEmitter();
itemClick = new EventEmitter();
carouselStage;
carouselContent;
contentTemplate;
currentPageIndex = 0;
totalPages = 0;
isPlaying = false;
isPaused = false;
isHovering = false;
isDragging = false;
dragStartX = 0;
dragCurrentX = 0;
initialTransform = 0;
currentTransform = 0;
loadedImages = new Set();
adjacentImagesLoaded = false;
autoPlaySubscription;
touchStartX = 0;
touchEndX = 0;
touchStartY = 0;
touchStartTime = 0;
swipeSubscriptions = [];
mousedownSubscription;
mousemoveSubscription;
mouseupSubscription;
mouseleaveSubscription;
get currentPage() {
return this.currentPageIndex + 1;
}
get pages() {
return Array.from({ length: this.totalPages }, (_, i) => i);
}
get blocks() {
const blocksCount = Math.ceil(this.items.length / (this.config.itemsPerPage ?? 1));
return Array.from({ length: blocksCount }, (_, i) => i);
}
ngOnInit() {
// Verificar e ajustar config para indicador textual
if (this.config.indicatorType === 'text') {
this.config.indicatorPosition = 'outside';
}
this.calculateTotalPages();
this.initializeCarousel();
// Pré-carregar a primeira imagem e as adjacentes
if (this.items.length > 0) {
this.preloadAdjacentImages(0);
}
if (this.config.autoPlay) {
this.play();
}
}
ngAfterViewInit() {
if (this.config.enableTouch) {
this.setupTouchEvents();
this.setupMouseEvents();
}
}
ngOnDestroy() {
this.stopAutoPlay();
this.cleanupTouchEvents();
this.cleanupMouseEvents();
}
onMouseEnter() {
this.isHovering = true;
if (this.config.autoPlay && this.isPlaying) {
this.pauseAutoPlay();
}
}
onMouseLeave() {
this.isHovering = false;
if (this.config.autoPlay && this.isPaused) {
this.resumeAutoPlay();
}
}
calculateTotalPages() {
if (this.config.navigationType === 'block') {
this.totalPages = Math.ceil(this.items.length / (this.config.itemsPerPage ?? 1));
}
else {
this.totalPages = this.items.length;
}
}
initializeCarousel() {
if (this.items.length > 0) {
this.items.forEach((item, index) => {
item.active = index === 0;
});
}
}
goToPage(pageIndex) {
if (pageIndex < 0 || pageIndex >= this.totalPages) {
if (this.config.loop) {
pageIndex = pageIndex < 0 ? this.totalPages - 1 : 0;
}
else {
return;
}
}
// Pré-carregar imagens adjacentes antes da transição
this.preloadAdjacentImages(pageIndex);
this.currentPageIndex = pageIndex;
// Atualizar estado de ativo para os itens
if (this.config.navigationType === 'block') {
const startIndex = this.currentPageIndex * (this.config.itemsPerPage ?? 1);
this.items.forEach((item, index) => {
item.active = index >= startIndex && index < startIndex + (this.config.itemsPerPage ?? 1);
});
}
else {
this.items.forEach((item, index) => {
item.active = index === this.currentPageIndex;
});
}
this.updateContentTransform(this.currentPageIndex);
this.pageChange.emit(this.currentPageIndex);
}
// Método para pré-carregar imagens adjacentes
preloadAdjacentImages(currentIndex) {
// Determinar quais imagens devem ser pré-carregadas (atual, anterior e próxima)
const indicesToPreload = [
currentIndex,
Math.max(0, currentIndex - 1),
Math.min(this.totalPages - 1, currentIndex + 1)
];
// Filtrar índices únicos
const uniqueIndices = [...new Set(indicesToPreload)];
// Pré-carregar cada imagem
uniqueIndices.forEach(index => {
if (index >= 0 && index < this.items.length) {
const item = this.items[index];
if (item.imageUrl && !this.loadedImages.has(item.imageUrl)) {
this.preloadImage(item.imageUrl);
}
}
});
}
// Método para pré-carregar uma única imagem
preloadImage(imageUrl) {
if (!imageUrl || this.loadedImages.has(imageUrl))
return;
const img = new Image();
img.onload = () => {
this.loadedImages.add(imageUrl);
};
img.src = imageUrl;
}
// Método para aplicar a transformação ao conteúdo
updateContentTransform(pageIndex, animationOverride = '') {
if (!this.carouselContent || !this.carouselContent.nativeElement)
return;
const element = this.carouselContent.nativeElement;
const translateX = -pageIndex * 100;
// Aplicar a transição antes de mudar a transformação
element.style.transition = animationOverride || `transform ${this.config.transitionDuration}ms ease-in-out`;
// Pequeno delay antes de aplicar a transformação para garantir que a transição seja registrada
setTimeout(() => {
element.style.transform = `translateX(${translateX}%)`;
this.currentTransform = translateX;
}, 10);
}
next() {
this.goToPage(this.currentPageIndex + 1);
}
prev() {
this.goToPage(this.currentPageIndex - 1);
}
play() {
if (this.isPlaying) {
return;
}
this.isPlaying = true;
this.isPaused = false;
this.autoPlaySubscription = interval(this.config.autoPlayInterval)
.pipe(tap(() => {
if (!this.isHovering && !this.isDragging) {
if (this.currentPageIndex === this.totalPages - 1 && !this.config.loop) {
this.stopAutoPlay();
}
else {
this.next();
}
}
}))
.subscribe();
}
pauseAutoPlay() {
this.isPaused = true;
this.stopAutoPlay();
}
resumeAutoPlay() {
if (this.isPaused) {
this.play();
}
}
pause() {
this.pauseAutoPlay();
}
togglePlay() {
if (this.isPlaying) {
this.pause();
}
else {
this.play();
}
}
stopAutoPlay() {
if (this.autoPlaySubscription) {
this.autoPlaySubscription.unsubscribe();
this.autoPlaySubscription = undefined;
this.isPlaying = false;
}
}
setupTouchEvents() {
if (!this.carouselStage || !this.carouselStage.nativeElement) {
return;
}
const element = this.carouselStage.nativeElement;
// Usando eventos nativos para melhor desempenho
element.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: true });
element.addEventListener('touchmove', this.handleTouchMove.bind(this), { passive: false });
element.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: true });
}
handleTouchStart(event) {
if (this.isPlaying) {
this.pauseAutoPlay();
}
this.touchStartX = event.touches[0].clientX;
this.touchStartY = event.touches[0].clientY;
this.touchStartTime = Date.now();
this.isDragging = true;
if (this.carouselContent && this.carouselContent.nativeElement) {
const style = window.getComputedStyle(this.carouselContent.nativeElement);
const matrix = new WebKitCSSMatrix(style.transform);
this.initialTransform = matrix.m41 / this.carouselContent.nativeElement.offsetWidth * 100;
this.currentTransform = this.initialTransform;
// Remover transição para movimento suave durante o arrasto
this.carouselContent.nativeElement.style.transition = 'none';
}
}
handleTouchMove(event) {
if (!this.isDragging)
return;
const touch = event.touches[0];
const deltaX = touch.clientX - this.touchStartX;
const deltaY = touch.clientY - this.touchStartY;
// Verificar se o deslizamento é horizontal antes de prevenir comportamento padrão
if (Math.abs(deltaX) > Math.abs(deltaY)) {
event.preventDefault();
}
if (this.carouselContent && this.carouselContent.nativeElement) {
const containerWidth = this.carouselContent.nativeElement.offsetWidth;
const movePercentage = (deltaX / containerWidth) * 100;
// Adicionar resistência nas bordas quando não estiver em loop
let newTransform = this.initialTransform + movePercentage;
if (!this.config.loop) {
if (this.currentPageIndex === 0 && movePercentage > 0) {
newTransform = this.initialTransform + (movePercentage / 3); // Resistência no início
}
else if (this.currentPageIndex === this.totalPages - 1 && movePercentage < 0) {
newTransform = this.initialTransform + (movePercentage / 3); // Resistência no final
}
}
this.carouselContent.nativeElement.style.transform = `translateX(${newTransform}%)`;
this.currentTransform = newTransform;
}
}
handleTouchEnd(event) {
if (!this.isDragging)
return;
const touchEndTime = Date.now();
const touchDuration = touchEndTime - this.touchStartTime;
this.touchEndX = event.changedTouches[0].clientX;
const deltaX = this.touchEndX - this.touchStartX;
// Calcular velocidade do movimento para determinar swipe
const velocity = Math.abs(deltaX) / touchDuration;
// Determinar o comportamento com base no movimento e velocidade
let newPage = this.currentPageIndex;
if (Math.abs(deltaX) > 100 || velocity > 0.5) {
// Movimento significativo ou swipe rápido
newPage = deltaX > 0 ? this.currentPageIndex - 1 : this.currentPageIndex + 1;
}
else {
// Baseado na porcentagem de movimento
const movedPercentage = Math.abs(deltaX) / (this.carouselStage?.nativeElement.offsetWidth || 1);
if (movedPercentage > 0.2) {
newPage = deltaX > 0 ? this.currentPageIndex - 1 : this.currentPageIndex + 1;
}
}
this.isDragging = false;
// Voltar para o slide atual ou ir para o próximo/anterior
if (this.carouselContent && this.carouselContent.nativeElement) {
this.carouselContent.nativeElement.style.transition = `transform ${this.config.transitionDuration}ms var(--animation-ease-in-out)`;
this.goToPage(newPage);
}
// Restaurar autoplay se necessário
if (this.isPaused) {
this.resumeAutoPlay();
}
}
// Adicionar suporte a eventos de mouse para desktop
setupMouseEvents() {
if (!this.carouselStage || !this.carouselStage.nativeElement)
return;
const element = this.carouselStage.nativeElement;
this.mousedownSubscription = fromEvent(element, 'mousedown')
.subscribe(this.handleMouseDown.bind(this));
this.mousemoveSubscription = fromEvent(document, 'mousemove')
.subscribe(this.handleMouseMove.bind(this));
this.mouseupSubscription = fromEvent(document, 'mouseup')
.subscribe(this.handleMouseUp.bind(this));
this.mouseleaveSubscription = fromEvent(document, 'mouseleave')
.subscribe(this.handleMouseUp.bind(this));
}
handleMouseDown(event) {
// Só capturar cliques com botão esquerdo
if (event.button !== 0)
return;
// Prevenir comportamento padrão (seleção de texto)
event.preventDefault();
if (this.isPlaying) {
this.pauseAutoPlay();
}
this.dragStartX = event.clientX;
this.isDragging = true;
if (this.carouselContent && this.carouselContent.nativeElement) {
const style = window.getComputedStyle(this.carouselContent.nativeElement);
const matrix = new WebKitCSSMatrix(style.transform);
this.initialTransform = matrix.m41 / this.carouselContent.nativeElement.offsetWidth * 100;
this.currentTransform = this.initialTransform;
this.carouselContent.nativeElement.style.transition = 'none';
// Cursor de arrasto
document.body.style.cursor = 'grabbing';
}
}
handleMouseMove(event) {
if (!this.isDragging)
return;
this.dragCurrentX = event.clientX;
const deltaX = this.dragCurrentX - this.dragStartX;
if (this.carouselContent && this.carouselContent.nativeElement) {
const containerWidth = this.carouselContent.nativeElement.offsetWidth;
const movePercentage = (deltaX / containerWidth) * 100;
// Adicionar resistência nas bordas quando não estiver em loop
let newTransform = this.initialTransform + movePercentage;
if (!this.config.loop) {
if (this.currentPageIndex === 0 && movePercentage > 0) {
newTransform = this.initialTransform + (movePercentage / 3);
}
else if (this.currentPageIndex === this.totalPages - 1 && movePercentage < 0) {
newTransform = this.initialTransform + (movePercentage / 3);
}
}
this.carouselContent.nativeElement.style.transform = `translateX(${newTransform}%)`;
this.currentTransform = newTransform;
}
}
handleMouseUp(event) {
if (!this.isDragging)
return;
document.body.style.cursor = '';
this.isDragging = false;
if (this.dragStartX !== 0 && this.dragCurrentX !== 0) {
const deltaX = this.dragCurrentX - this.dragStartX;
let newPage = this.currentPageIndex;
// Determinar se deve mudar de slide com base na distância do movimento
if (Math.abs(deltaX) > 100) {
newPage = deltaX > 0 ? this.currentPageIndex - 1 : this.currentPageIndex + 1;
}
else {
const movedPercentage = Math.abs(deltaX) / (this.carouselStage?.nativeElement.offsetWidth || 1);
if (movedPercentage > 0.2) {
newPage = deltaX > 0 ? this.currentPageIndex - 1 : this.currentPageIndex + 1;
}
}
if (this.carouselContent && this.carouselContent.nativeElement) {
this.carouselContent.nativeElement.style.transition = `transform ${this.config.transitionDuration}ms var(--animation-ease-in-out)`;
this.goToPage(newPage);
}
}
this.dragStartX = 0;
this.dragCurrentX = 0;
// Restaurar autoplay se necessário
if (this.isPaused) {
this.resumeAutoPlay();
}
}
cleanupTouchEvents() {
if (this.carouselStage && this.carouselStage.nativeElement) {
const element = this.carouselStage.nativeElement;
element.removeEventListener('touchstart', this.handleTouchStart.bind(this));
element.removeEventListener('touchmove', this.handleTouchMove.bind(this));
element.removeEventListener('touchend', this.handleTouchEnd.bind(this));
}
this.swipeSubscriptions.forEach(sub => sub.unsubscribe());
this.swipeSubscriptions = [];
}
cleanupMouseEvents() {
this.mousedownSubscription?.unsubscribe();
this.mousemoveSubscription?.unsubscribe();
this.mouseupSubscription?.unsubscribe();
this.mouseleaveSubscription?.unsubscribe();
}
isFirstPage() {
return this.currentPageIndex === 0;
}
isLastPage() {
return this.currentPageIndex === this.totalPages - 1;
}
shouldDisableNavButton(direction) {
if (this.config.loop) {
return false;
}
return direction === 'prev' ? this.isFirstPage() : this.isLastPage();
}
trackByFn(index, item) {
return item.id || index;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: CarouselComponent, selector: "cfc-carousel", inputs: { items: "items", config: "config" }, outputs: { pageChange: "pageChange", itemClick: "itemClick" }, queries: [{ propertyName: "contentTemplate", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "carouselStage", first: true, predicate: ["carouselStage"], descendants: true }, { propertyName: "carouselContent", first: true, predicate: ["carouselContent"], descendants: true }], ngImport: i0, template: "<div class=\"carousel-wrapper\">\r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (anterior) outside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'outside'\"\r\n class=\"carousel-nav-button carousel-prev-button outside\"\r\n [disabled]=\"shouldDisableNavButton('prev')\"\r\n (click)=\"prev()\">\r\n <i class=\"fas fa-angle-left\"></i>\r\n </button>\r\n <div class=\"carousel-container\"\r\n (mouseenter)=\"onMouseEnter()\"\r\n (mouseleave)=\"onMouseLeave()\">\r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (anterior) inside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'inside'\"\r\n class=\"carousel-nav-button carousel-prev-button inside\"\r\n [disabled]=\"shouldDisableNavButton('prev')\"\r\n (click)=\"prev()\">\r\n <i class=\"fas fa-angle-left\"></i>\r\n </button>\r\n <!-- Palco (\u00E1rea de conte\u00FAdo) -->\r\n <div class=\"carousel-stage\" #carouselStage\r\n [class.is-dragging]=\"isDragging\">\r\n <!-- Modifica\u00E7\u00E3o no carousel-content -->\r\n <div class=\"carousel-content\" #carouselContent>\r\n <ng-container *ngFor=\"let item of items; let i = index; trackBy: trackByFn\">\r\n <div class=\"carousel-item\"\r\n [class.active]=\"item.active\"\r\n [class.dragging]=\"isDragging\"\r\n (click)=\"!isDragging && itemClick.emit(item)\">\r\n <!-- Renderiza\u00E7\u00E3o de imagem com tratamento para pr\u00E9-carregamento -->\r\n <div *ngIf=\"item.imageUrl\" class=\"carousel-image-container\">\r\n <!-- Modificado para carregar imagem diretamente, mas usar opacity para o efeito de fade -->\r\n <img \r\n [src]=\"item.imageUrl\" \r\n [alt]=\"item.imageAlt || item.title || ''\" \r\n class=\"carousel-image\"\r\n [ngClass]=\"{'lazyloaded': loadedImages.has(item.imageUrl)}\"\r\n (load)=\"loadedImages.add(item.imageUrl)\">\r\n \r\n <!-- Imagem de placeholder enquanto carrega -->\r\n <div *ngIf=\"!loadedImages.has(item.imageUrl)\" class=\"carousel-image-placeholder\"></div>\r\n </div>\r\n\r\n <!-- Resto do conte\u00FAdo permanece igual -->\r\n <ng-container *ngIf=\"!item.imageUrl || contentTemplate\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: {$implicit: item}\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n \r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (pr\u00F3ximo) inside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'inside'\"\r\n class=\"carousel-nav-button carousel-next-button inside\"\r\n [disabled]=\"shouldDisableNavButton('next')\"\r\n (click)=\"next()\">\r\n <i class=\"fas fa-angle-right\"></i>\r\n </button>\r\n\r\n <!-- Bot\u00E3o de reprodu\u00E7\u00E3o (interno ao palco) -->\r\n <div *ngIf=\"config.showPlayButtons && config.navigationButtonsPosition === 'inside'\"\r\n class=\"carousel-play-button-container\">\r\n <button class=\"carousel-play-button\" (click)=\"togglePlay()\">\r\n <i [class]=\"isPlaying ? 'fas fa-pause' : 'fas fa-play'\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Indicador de p\u00E1ginas (interno ao palco) -->\r\n <div *ngIf=\"config.showIndicator && config.indicatorPosition === 'inside'\"\r\n class=\"carousel-indicator-container inside\">\r\n <div *ngIf=\"config.indicatorType === 'simple'\" class=\"carousel-indicator\">\r\n <span *ngFor=\"let page of pages\"\r\n class=\"indicator-dot\"\r\n [class.active]=\"page === currentPageIndex\"\r\n (click)=\"goToPage(page)\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (pr\u00F3ximo) outside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'outside'\"\r\n class=\"carousel-nav-button carousel-next-button outside\"\r\n [disabled]=\"shouldDisableNavButton('next')\"\r\n (click)=\"next()\">\r\n <i class=\"fas fa-angle-right\"></i>\r\n </button>\r\n</div>\r\n\r\n<!-- Bot\u00E3o de reprodu\u00E7\u00E3o (externo ao palco) -->\r\n<div *ngIf=\"config.showPlayButtons && config.navigationButtonsPosition === 'outside'\"\r\n class=\"carousel-play-button-container outside\">\r\n <button class=\"carousel-play-button\" (click)=\"togglePlay()\">\r\n <i [class]=\"isPlaying ? 'fas fa-pause' : 'fas fa-play'\"></i>\r\n </button>\r\n</div>\r\n\r\n<!-- Indicador de p\u00E1ginas (externo ao palco) -->\r\n<div *ngIf=\"config.showIndicator && config.indicatorPosition === 'outside'\"\r\n class=\"carousel-indicator-container outside\">\r\n <div *ngIf=\"config.indicatorType === 'simple'\" class=\"carousel-indicator\">\r\n <span *ngFor=\"let page of pages\"\r\n class=\"indicator-dot\"\r\n [class.active]=\"page === currentPageIndex\"\r\n (click)=\"goToPage(page)\"></span>\r\n </div>\r\n <div *ngIf=\"config.indicatorType === 'text'\" class=\"carousel-indicator-text\">\r\n {{ currentPage }}/{{ totalPages }}\r\n </div>\r\n</div>\r\n\r\n<!-- Template para o conte\u00FAdo -->\r\n<ng-template #itemTemplate let-item>\r\n <ng-content *ngIf=\"!contentTemplate\"></ng-content>\r\n <ng-container *ngIf=\"contentTemplate\">\r\n <ng-container *ngTemplateOutlet=\"contentTemplate; context: {$implicit: item}\"></ng-container>\r\n </ng-container>\r\n</ng-template>\r\n<ng-content select=\"[carouselContent]\"></ng-content>", styles: [":host{display:block}.carousel-wrapper{display:flex;align-items:center;position:relative;width:100%}.carousel-container{position:relative;width:100%;overflow:hidden}.carousel-container:not(:hover) .carousel-play-button-container:not(.outside){opacity:0;transition:opacity .3s ease}.carousel-container:hover .carousel-play-button-container:not(.outside){opacity:1;transition:opacity .3s ease}.carousel-stage{position:relative;overflow:hidden;width:100%;touch-action:pan-y;-webkit-user-select:none;user-select:none;cursor:grab}.carousel-stage.is-dragging{cursor:grabbing}.carousel-content{display:flex;position:relative;height:100%;width:100%;will-change:transform;touch-action:none;transition:transform .3s ease-in-out}.carousel-block{display:flex;width:100%;position:relative;transition:transform var(--transition-duration, .3s) var(--animation-ease-in-out)}.carousel-block .carousel-item{flex-shrink:0;width:100%;height:100%;position:relative;flex:0 0 100%}.carousel-block .carousel-block .carousel-item{width:calc((100% - (var(--items-per-page, 3) - 1) * var(--item-spacing, 20px)) / var(--items-per-page, 3))}.carousel-block .carousel-item.dragging{pointer-events:none}.carousel-item{flex-shrink:0;width:100%;height:100%;position:relative}.carousel-block .carousel-item{width:calc((100% - (var(--items-per-page, 3) - 1) * var(--item-spacing, 20px)) / var(--items-per-page, 3))}.carousel-item.dragging{pointer-events:none}.carousel-image-container{position:relative;width:100%;height:100%;overflow:hidden}.carousel-image{width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .3s ease-in-out}.carousel-image.lazyloaded{opacity:1}.carousel-overlay{position:absolute;bottom:0;left:0;right:0;background:#000000b3;color:#fff;padding:15px}.carousel-overlay h3{margin:0 0 8px;font-size:1.2em}.carousel-overlay p{margin:0;font-size:.9em}.carousel-nav-button{background:#ffffff80;border:none;border-radius:50%;width:40px;height:40px;display:flex;justify-content:center;align-items:center;cursor:pointer;z-index:2;transition:background-color .3s}.carousel-nav-button:hover{background:#fffc}.carousel-nav-button:disabled{opacity:.3;cursor:not-allowed}.carousel-nav-button.inside{position:absolute;top:50%;transform:translateY(-50%)}.carousel-nav-button.outside{margin:0 10px}.carousel-nav-button.carousel-prev-button.inside{left:10px}.carousel-nav-button.carousel-next-button.inside{right:10px}.carousel-play-button-container{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:3}.carousel-play-button-container.outside{position:relative;top:auto;left:auto;transform:none;display:flex;justify-content:center;margin:10px 0;width:100%}.carousel-play-button-container .carousel-play-button{background:#00000080;color:#fff;border:none;border-radius:50%;width:40px;height:40px;display:flex;justify-content:center;align-items:center;cursor:pointer;transition:background-color .3s,opacity .3s}.carousel-play-button-container .carousel-play-button:hover{background:#000c}.carousel-indicator-container{display:flex;justify-content:center;align-items:center}.carousel-indicator-container.inside{position:absolute;bottom:10px;left:0;right:0;z-index:3}.carousel-indicator-container.outside{margin-top:10px;width:100%}.carousel-indicator-container .carousel-indicator{display:flex;gap:8px}.carousel-indicator-container .carousel-indicator .indicator-dot{width:10px;height:10px;border-radius:50%;background-color:#0000004d;cursor:pointer;transition:background-color .3s ease}.carousel-indicator-container .carousel-indicator .indicator-dot.active{background-color:#0057b7}.carousel-indicator-container .carousel-indicator .indicator-dot:hover{background-color:#00000080}.carousel-indicator-container .carousel-indicator-text{padding:5px 15px;border-radius:4px;font-size:14px;font-weight:500;color:#333;display:inline-block;text-align:center;min-width:60px}@keyframes bounce-left{0%,to{transform:translate(0)}50%{transform:translate(10px)}}@keyframes bounce-right{0%,to{transform:translate(0)}50%{transform:translate(-10px)}}@media (max-width: 768px){.carousel-overlay h3{font-size:1em}.carousel-overlay p{font-size:.8em}.carousel-nav-button,.carousel-play-button{width:30px;height:30px;font-size:12px}}@media (max-width: 576px){.carousel-nav-button.inside{display:none}.carousel-play-button-container .carousel-play-button{width:25px;height:25px;font-size:10px}}.carousel-image-placeholder{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#f0f0f0;display:flex;align-items:center;justify-content:center}.carousel-image-placeholder:after{content:\"\";width:30px;height:30px;border-radius:50%;border:2px solid rgba(0,0,0,.2);border-top-color:#333;animation:spin 1s infinite linear}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.DefaultClassDirective, selector: " [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]", inputs: ["ngClass", "ngClass.xs", "ngClass.sm", "ngClass.md", "ngClass.lg", "ngClass.xl", "ngClass.lt-sm", "ngClass.lt-md", "ngClass.lt-lg", "ngClass.lt-xl", "ngClass.gt-xs", "ngClass.gt-sm", "ngClass.gt-md", "ngClass.gt-lg"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CarouselComponent, decorators: [{
type: Component,
args: [{ selector: 'cfc-carousel', template: "<div class=\"carousel-wrapper\">\r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (anterior) outside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'outside'\"\r\n class=\"carousel-nav-button carousel-prev-button outside\"\r\n [disabled]=\"shouldDisableNavButton('prev')\"\r\n (click)=\"prev()\">\r\n <i class=\"fas fa-angle-left\"></i>\r\n </button>\r\n <div class=\"carousel-container\"\r\n (mouseenter)=\"onMouseEnter()\"\r\n (mouseleave)=\"onMouseLeave()\">\r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (anterior) inside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'inside'\"\r\n class=\"carousel-nav-button carousel-prev-button inside\"\r\n [disabled]=\"shouldDisableNavButton('prev')\"\r\n (click)=\"prev()\">\r\n <i class=\"fas fa-angle-left\"></i>\r\n </button>\r\n <!-- Palco (\u00E1rea de conte\u00FAdo) -->\r\n <div class=\"carousel-stage\" #carouselStage\r\n [class.is-dragging]=\"isDragging\">\r\n <!-- Modifica\u00E7\u00E3o no carousel-content -->\r\n <div class=\"carousel-content\" #carouselContent>\r\n <ng-container *ngFor=\"let item of items; let i = index; trackBy: trackByFn\">\r\n <div class=\"carousel-item\"\r\n [class.active]=\"item.active\"\r\n [class.dragging]=\"isDragging\"\r\n (click)=\"!isDragging && itemClick.emit(item)\">\r\n <!-- Renderiza\u00E7\u00E3o de imagem com tratamento para pr\u00E9-carregamento -->\r\n <div *ngIf=\"item.imageUrl\" class=\"carousel-image-container\">\r\n <!-- Modificado para carregar imagem diretamente, mas usar opacity para o efeito de fade -->\r\n <img \r\n [src]=\"item.imageUrl\" \r\n [alt]=\"item.imageAlt || item.title || ''\" \r\n class=\"carousel-image\"\r\n [ngClass]=\"{'lazyloaded': loadedImages.has(item.imageUrl)}\"\r\n (load)=\"loadedImages.add(item.imageUrl)\">\r\n \r\n <!-- Imagem de placeholder enquanto carrega -->\r\n <div *ngIf=\"!loadedImages.has(item.imageUrl)\" class=\"carousel-image-placeholder\"></div>\r\n </div>\r\n\r\n <!-- Resto do conte\u00FAdo permanece igual -->\r\n <ng-container *ngIf=\"!item.imageUrl || contentTemplate\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: {$implicit: item}\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n \r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (pr\u00F3ximo) inside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'inside'\"\r\n class=\"carousel-nav-button carousel-next-button inside\"\r\n [disabled]=\"shouldDisableNavButton('next')\"\r\n (click)=\"next()\">\r\n <i class=\"fas fa-angle-right\"></i>\r\n </button>\r\n\r\n <!-- Bot\u00E3o de reprodu\u00E7\u00E3o (interno ao palco) -->\r\n <div *ngIf=\"config.showPlayButtons && config.navigationButtonsPosition === 'inside'\"\r\n class=\"carousel-play-button-container\">\r\n <button class=\"carousel-play-button\" (click)=\"togglePlay()\">\r\n <i [class]=\"isPlaying ? 'fas fa-pause' : 'fas fa-play'\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Indicador de p\u00E1ginas (interno ao palco) -->\r\n <div *ngIf=\"config.showIndicator && config.indicatorPosition === 'inside'\"\r\n class=\"carousel-indicator-container inside\">\r\n <div *ngIf=\"config.indicatorType === 'simple'\" class=\"carousel-indicator\">\r\n <span *ngFor=\"let page of pages\"\r\n class=\"indicator-dot\"\r\n [class.active]=\"page === currentPageIndex\"\r\n (click)=\"goToPage(page)\"></span>\r\n </div>\r\n </div>\r\n </div>\r\n \r\n <!-- Bot\u00E3o de navega\u00E7\u00E3o (pr\u00F3ximo) outside -->\r\n <button *ngIf=\"config.showNavigationButtons && config.navigationButtonsPosition === 'outside'\"\r\n class=\"carousel-nav-button carousel-next-button outside\"\r\n [disabled]=\"shouldDisableNavButton('next')\"\r\n (click)=\"next()\">\r\n <i class=\"fas fa-angle-right\"></i>\r\n </button>\r\n</div>\r\n\r\n<!-- Bot\u00E3o de reprodu\u00E7\u00E3o (externo ao palco) -->\r\n<div *ngIf=\"config.showPlayButtons && config.navigationButtonsPosition === 'outside'\"\r\n class=\"carousel-play-button-container outside\">\r\n <button class=\"carousel-play-button\" (click)=\"togglePlay()\">\r\n <i [class]=\"isPlaying ? 'fas fa-pause' : 'fas fa-play'\"></i>\r\n </button>\r\n</div>\r\n\r\n<!-- Indicador de p\u00E1ginas (externo ao palco) -->\r\n<div *ngIf=\"config.showIndicator && config.indicatorPosition === 'outside'\"\r\n class=\"carousel-indicator-container outside\">\r\n <div *ngIf=\"config.indicatorType === 'simple'\" class=\"carousel-indicator\">\r\n <span *ngFor=\"let page of pages\"\r\n class=\"indicator-dot\"\r\n [class.active]=\"page === currentPageIndex\"\r\n (click)=\"goToPage(page)\"></span>\r\n </div>\r\n <div *ngIf=\"config.indicatorType === 'text'\" class=\"carousel-indicator-text\">\r\n {{ currentPage }}/{{ totalPages }}\r\n </div>\r\n</div>\r\n\r\n<!-- Template para o conte\u00FAdo -->\r\n<ng-template #itemTemplate let-item>\r\n <ng-content *ngIf=\"!contentTemplate\"></ng-content>\r\n <ng-container *ngIf=\"contentTemplate\">\r\n <ng-container *ngTemplateOutlet=\"contentTemplate; context: {$implicit: item}\"></ng-container>\r\n </ng-container>\r\n</ng-template>\r\n<ng-content select=\"[carouselContent]\"></ng-content>", styles: [":host{display:block}.carousel-wrapper{display:flex;align-items:center;position:relative;width:100%}.carousel-container{position:relative;width:100%;overflow:hidden}.carousel-container:not(:hover) .carousel-play-button-container:not(.outside){opacity:0;transition:opacity .3s ease}.carousel-container:hover .carousel-play-button-container:not(.outside){opacity:1;transition:opacity .3s ease}.carousel-stage{position:relative;overflow:hidden;width:100%;touch-action:pan-y;-webkit-user-select:none;user-select:none;cursor:grab}.carousel-stage.is-dragging{cursor:grabbing}.carousel-content{display:flex;position:relative;height:100%;width:100%;will-change:transform;touch-action:none;transition:transform .3s ease-in-out}.carousel-block{display:flex;width:100%;position:relative;transition:transform var(--transition-duration, .3s) var(--animation-ease-in-out)}.carousel-block .carousel-item{flex-shrink:0;width:100%;height:100%;position:relative;flex:0 0 100%}.carousel-block .carousel-block .carousel-item{width:calc((100% - (var(--items-per-page, 3) - 1) * var(--item-spacing, 20px)) / var(--items-per-page, 3))}.carousel-block .carousel-item.dragging{pointer-events:none}.carousel-item{flex-shrink:0;width:100%;height:100%;position:relative}.carousel-block .carousel-item{width:calc((100% - (var(--items-per-page, 3) - 1) * var(--item-spacing, 20px)) / var(--items-per-page, 3))}.carousel-item.dragging{pointer-events:none}.carousel-image-container{position:relative;width:100%;height:100%;overflow:hidden}.carousel-image{width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .3s ease-in-out}.carousel-image.lazyloaded{opacity:1}.carousel-overlay{position:absolute;bottom:0;left:0;right:0;background:#000000b3;color:#fff;padding:15px}.carousel-overlay h3{margin:0 0 8px;font-size:1.2em}.carousel-overlay p{margin:0;font-size:.9em}.carousel-nav-button{background:#ffffff80;border:none;border-radius:50%;width:40px;height:40px;display:flex;justify-content:center;align-items:center;cursor:pointer;z-index:2;transition:background-color .3s}.carousel-nav-button:hover{background:#fffc}.carousel-nav-button:disabled{opacity:.3;cursor:not-allowed}.carousel-nav-button.inside{position:absolute;top:50%;transform:translateY(-50%)}.carousel-nav-button.outside{margin:0 10px}.carousel-nav-button.carousel-prev-button.inside{left:10px}.carousel-nav-button.carousel-next-button.inside{right:10px}.carousel-play-button-container{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:3}.carousel-play-button-container.outside{position:relative;top:auto;left:auto;transform:none;display:flex;justify-content:center;margin:10px 0;width:100%}.carousel-play-button-container .carousel-play-button{background:#00000080;color:#fff;border:none;border-radius:50%;width:40px;height:40px;display:flex;justify-content:center;align-items:center;cursor:pointer;transition:background-color .3s,opacity .3s}.carousel-play-button-container .carousel-play-button:hover{background:#000c}.carousel-indicator-container{display:flex;justify-content:center;align-items:center}.carousel-indicator-container.inside{position:absolute;bottom:10px;left:0;right:0;z-index:3}.carousel-indicator-container.outside{margin-top:10px;width:100%}.carousel-indicator-container .carousel-indicator{display:flex;gap:8px}.carousel-indicator-container .carousel-indicator .indicator-dot{width:10px;height:10px;border-radius:50%;background-color:#0000004d;cursor:pointer;transition:background-color .3s ease}.carousel-indicator-container .carousel-indicator .indicator-dot.active{background-color:#0057b7}.carousel-indicator-container .carousel-indicator .indicator-dot:hover{background-color:#00000080}.carousel-indicator-container .carousel-indicator-text{padding:5px 15px;border-radius:4px;font-size:14px;font-weight:500;color:#333;display:inline-block;text-align:center;min-width:60px}@keyframes bounce-left{0%,to{transform:translate(0)}50%{transform:translate(10px)}}@keyframes bounce-right{0%,to{transform:translate(0)}50%{transform:translate(-10px)}}@media (max-width: 768px){.carousel-overlay h3{font-size:1em}.carousel-overlay p{font-size:.8em}.carousel-nav-button,.carousel-play-button{width:30px;height:30px;font-size:12px}}@media (max-width: 576px){.carousel-nav-button.inside{display:none}.carousel-play-button-container .carousel-play-button{width:25px;height:25px;font-size:10px}}.carousel-image-placeholder{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#f0f0f0;display:flex;align-items:center;justify-content:center}.carousel-image-placeholder:after{content:\"\";width:30px;height:30px;border-radius:50%;border:2px solid rgba(0,0,0,.2);border-top-color:#333;animation:spin 1s infinite linear}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
}], propDecorators: { items: [{
type: Input
}], config: [{
type: Input
}], pageChange: [{
type: Output
}], itemClick: [{
type: Output
}], carouselStage: [{
type: ViewChild,
args: ['carouselStage']
}], carouselContent: [{
type: ViewChild,
args: ['carouselContent']
}], contentTemplate: [{
type: ContentChild,
args: [TemplateRef]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2Fyb3VzZWwuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvY2ZjLWRzL3NyYy9saWIvY29tcG9uZW50cy9jYXJvdXNlbC9jYXJvdXNlbC5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9jZmMtZHMvc3JjL2xpYi9jb21wb25lbnRzL2Nhcm91c2VsL2Nhcm91c2VsLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCxTQUFTLEVBQ1QsWUFBWSxFQUVaLFlBQVksRUFDWixLQUFLLEVBR0wsTUFBTSxFQUNOLFdBQVcsRUFDWCxTQUFTLEVBRVYsTUFBTSxlQUFlLENBQUM7QUFFdkIsT0FBTyxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQ3pELE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7OztBQVFyQyxNQUFNLE9BQU8saUJBQWlCO0lBQ25CLEtBQUssR0FBbUIsRUFBRSxDQUFDO0lBQzNCLE1BQU0sR0FBbUI7UUFDaEMsSUFBSSxFQUFFLEtBQUs7UUFDWCxRQUFRLEVBQUUsS0FBSztRQUNmLGdCQUFnQixFQUFFLElBQUk7UUFDdEIscUJBQXFCLEVBQUUsSUFBSTtRQUMzQixlQUFlLEVBQUUsS0FBSztRQUN0QixhQUFhLEVBQUUsSUFBSTtRQUNuQix5QkFBeUIsRUFBRSxTQUFTLEVBQUUsd0JBQXdCO1FBQzlELGlCQUFpQixFQUFFLFFBQVEsRUFBVSx3QkFBd0I7UUFDN0QsYUFBYSxFQUFFLFFBQVE7UUFDdkIsWUFBWSxFQUFFLENBQUM7UUFDZixjQUFjLEVBQUUsTUFBTTtRQUN0QixrQkFBa0IsRUFBRSxHQUFHO1FBQ3ZCLGNBQWMsRUFBRSxPQUFPO1FBQ3ZCLFdBQVcsRUFBRSxJQUFJO0tBQ2xCLENBQUM7SUFFUSxVQUFVLEdBQUcsSUFBSSxZQUFZLEVBQVUs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