import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, Output, ViewChild, signal } from '@angular/core'; import { SectionConfig } from '../../shared/models/config'; import { CatalogProductGridComponent } from '../../features/website/catalog/components/product-grid/product-grid.component'; import { ProductCollectionWidgetData } from '../contracts/widget-data.contract'; import { TranslatePipe } from '../../i18n/translate.pipe'; @Component({ selector: 'app-product-carousel-widget', standalone: true, imports: [CommonModule, CatalogProductGridComponent, TranslatePipe], template: ` `, styles: [ ` .product-carousel-widget { display: grid; gap: var(--space-lg, 24px); } .product-carousel-widget__title { margin: 0; font-size: 1.75rem; font-weight: 700; color: var(--text-primary, #1e3c38); } .product-carousel-widget__empty { margin: 0; color: var(--text-secondary, #667a77); } .product-carousel-widget__track { display: flex; align-items: center; gap: 8px; min-width: 0; } .product-carousel-widget__scroller { flex: 1; min-width: 0; overflow-x: auto; scroll-behavior: smooth; scrollbar-width: none; &::-webkit-scrollbar { display: none; } ::ng-deep .catalog-product-grid { display: flex; flex-wrap: nowrap; gap: var(--space-md, 16px); } ::ng-deep .catalog-product-shell { flex: 0 0 auto; width: calc((100% - (var(--items-per-page, 4) - 1) * var(--space-md, 16px)) / var(--items-per-page, 4)); min-width: 160px; } } .product-carousel-widget__arrow { flex-shrink: 0; width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--border-color, #d3dad9); background: #fff; color: var(--text-primary, #1e3c38); cursor: pointer; font-size: 1rem; &:hover:not(:disabled) { background: var(--primary-color, #497671); color: #fff; } &:disabled { opacity: 0.35; cursor: default; } &:focus-visible { outline: 2px solid var(--primary-color, #497671); outline-offset: 2px; } } ` ], changeDetection: ChangeDetectionStrategy.OnPush }) export class ProductCarouselWidgetComponent { @Input() section: SectionConfig | null = null; @Input() data: ProductCollectionWidgetData | null = null; @Output() productSelected = new EventEmitter(); @ViewChild('scroller') private scroller?: ElementRef; readonly atStart = signal(true); readonly atEnd = signal(false); get isCarousel(): boolean { return this.section?.layout?.strategy === 'carousel'; } /** Items visible per page - reuses `layout.columns` (default 4, min 1) rather than a dedicated field. */ itemsPerPage(): number { const columns = this.section?.layout?.columns; return columns && columns >= 1 ? Math.floor(columns) : 4; } scrollBy(direction: -1 | 1): void { const el = this.scroller?.nativeElement; if (!el) { return; } el.scrollBy({ left: direction * el.clientWidth * 0.8, behavior: 'smooth' }); } onScroll(): void { const el = this.scroller?.nativeElement; if (!el) { return; } this.atStart.set(el.scrollLeft <= 4); this.atEnd.set(el.scrollLeft + el.clientWidth >= el.scrollWidth - 4); } }