Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 2x 3x 3x 3x 3x 3x 3x 3x 4x 1x 3x 4x 1x 1x 3x 9x 15x 3x | import { ChangeDetectionStrategy, Component, Inject, Input } from '@angular/core'; import { RxState } from '@rx-angular/state'; import { EMPTY, Observable, combineLatest, of } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; import SwiperCore, { Navigation, Pagination, SwiperOptions } from 'swiper'; import { LARGE_BREAKPOINT_WIDTH, MEDIUM_BREAKPOINT_WIDTH } from 'ish-core/configurations/injection-keys'; import { ShoppingFacade } from 'ish-core/facades/shopping.facade'; import { ProductLinks } from 'ish-core/models/product-links/product-links.model'; import { ProductCompletenessLevel } from 'ish-core/models/product/product.model'; import { InjectSingle } from 'ish-core/utils/injection'; import { mapToProperty } from 'ish-core/utils/operators'; SwiperCore.use([Navigation, Pagination]); /** * The Product Link Carousel Component * * Displays the products which are assigned to a specific product link type as an carousel. * It uses the {@link ProductItemComponent} for the rendering of products. * * @example * <ish-product-links-carousel [links]="links.crossselling" [productLinkTitle]="'product.product_links.crossselling.title' | translate"></ish-product-links-carousel> */ @Component({ selector: 'ish-product-links-carousel', templateUrl: './product-links-carousel.component.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [RxState], }) export class ProductLinksCarouselComponent { /** * list of products which are assigned to the specific product link type */ @Input({ required: true }) set links(links: ProductLinks) { this.state.set('products', () => links.products); } /** * title that should displayed for the specific product link type */ @Input({ required: true }) productLinkTitle: string; /** * display only available products if set to 'true' */ @Input() set displayOnlyAvailableProducts(value: boolean) { this.state.set('displayOnlyAvailableProducts', () => value); } productSKUs$ = this.state.select('products$'); /** * track already fetched SKUs */ private fetchedSKUs = new Set<Observable<string>>(); /** * configuration of swiper carousel * https://swiperjs.com/swiper-api */ swiperConfig: SwiperOptions; constructor( @Inject(LARGE_BREAKPOINT_WIDTH) largeBreakpointWidth: InjectSingle<typeof LARGE_BREAKPOINT_WIDTH>, @Inject(MEDIUM_BREAKPOINT_WIDTH) mediumBreakpointWidth: InjectSingle<typeof MEDIUM_BREAKPOINT_WIDTH>, private shoppingFacade: ShoppingFacade, private state: RxState<{ products: string[]; displayOnlyAvailableProducts: boolean; hiddenSlides: number[]; products$: Observable<string>[]; }> ) { this.state.set(() => ({ hiddenSlides: [], displayOnlyAvailableProducts: false, })); this.swiperConfig = { watchSlidesProgress: true, direction: 'horizontal', navigation: true, pagination: { clickable: true, }, breakpoints: { 0: { slidesPerView: 2, slidesPerGroup: 2, }, [mediumBreakpointWidth]: { slidesPerView: 3, slidesPerGroup: 3, }, [largeBreakpointWidth]: { slidesPerView: 4, slidesPerGroup: 4, }, }, }; const filteredProducts$ = combineLatest([ combineLatest([this.state.select('products'), this.state.select('displayOnlyAvailableProducts')]).pipe( map(([products, displayOnlyAvailableProducts]) => { // prepare lazy observables for all products if (displayOnlyAvailableProducts) { return products.map((sku, index) => this.shoppingFacade.product$(sku, ProductCompletenessLevel.List).pipe( tap(product => { // add slide to the hidden list if product is not available if (!product.available || product.failed) { this.state.set('hiddenSlides', () => [...this.state.get('hiddenSlides'), index].filter((v, i, a) => a.indexOf(v) === i) ); } }), filter(product => product.available && !product.failed), mapToProperty('sku') ) ); } else { return products.map(sku => of(sku)); } }) ), this.state.select('hiddenSlides'), ]).pipe(map(([products, hiddenSlides]) => products.filter((_, index) => !hiddenSlides.includes(index)))); this.state.connect('products$', filteredProducts$); } lazyFetch(fetch: boolean, sku$: Observable<string>): Observable<string> { Iif (fetch) { this.fetchedSKUs.add(sku$); } Iif (this.fetchedSKUs.has(sku$)) { return sku$; } return EMPTY; } } |