import { when } from 'lit/directives/when.js' import type { PropertyValues } from 'lit' import { css, html } from 'lit' import { query } from 'lit/decorators/query.js' import { queryAll } from 'lit/decorators/query-all.js' import { state } from 'lit/decorators/state.js' import { queryAssignedElements } from 'lit/decorators/query-assigned-elements.js' import { property } from 'lit/decorators.js' import { classMap } from 'lit/directives/class-map.js' import { UsBaseElement } from '../base/UsBaseElement' import { customElementIfNotExist } from '../../utils/customElementIfNotExist' import UsCarousel_css from './carousel.pcss' import vars_css from './vars.pcss' import './UsСarouselItem' import type { ICarouselItem } from '../../screens/intro-screen/model/IIntroCarouselData' declare global { interface HTMLElementTagNameMap { 'us-carousel': UsCarousel } } @customElementIfNotExist('us-carousel') export class UsCarousel extends UsBaseElement { static styles = [ UsBaseElement.styles, css([vars_css] as TemplateStringsArray | any), css([UsCarousel_css] as TemplateStringsArray | any), ] @property() widgetView = 'vertical' @property({ type: Boolean }) autoSlide = false @property({ type: Array }) items: ICarouselItem[] = [] @property({ type: String, attribute: 'data-testid', reflect: true }) dataTestId = 'carousel' @property({ type: Number, attribute: 'item-offset' }) itemOffset = 520 @property({ type: Number, attribute: 'active-slide' }) activeSlide = 0 @property({ type: Number, attribute: 'delay' }) delay = 3000 @state() carouselDelayIndex = -1 @query('.carousel-wrap') _carouselWrapNode!: HTMLElement @queryAll('.carousel-slider') _carouselSlideNode!: HTMLDivElement[] @queryAssignedElements() _slottedElements!: Array firstUpdated() { this.setWidthCarouselWrap() this.setWidthCarouselSlides() this.carouselDelayMove(this.delay) } async willUpdate(changedProperties: PropertyValues) { if (changedProperties.has('itemOffset')) { this.setWidthCarouselWrap() this.setWidthCarouselSlides() } } disconnectedCallback() { clearInterval(this.carouselDelayIndex) } render() { const shouldShowDots = this._slottedElements.length > 1 const carouselLoopDotTemplates = this._slottedElements.map((_, carouselIndex) => html` `) return html`
` } isActiveDot(dotNumber: number) { return Math.abs(this.activeSlide) === dotNumber } setWidthCarouselWrap() { if (this._carouselWrapNode === null) return this._carouselWrapNode.style.width = `${this.itemOffset}px` this._carouselWrapNode.style.maxWidth = `${this.itemOffset}px` } setWidthCarouselSlides() { if (this._slottedElements === null) return this._slottedElements.forEach((slideNodeElement: HTMLElement | null) => { if (slideNodeElement === null) return slideNodeElement.style.width = `${this.itemOffset}px` }) } moveLeftLoop() { this.moveToLoop(this.activeSlide - 1) } moveRightLoop() { this.moveToLoop(this.activeSlide + 1) } moveViaArrows(e: KeyboardEvent) { const children = (e.srcElement as HTMLElement).children const selectedEl = (e.srcElement as HTMLElement).querySelector('.carousel-dot--active') if (e.key === 'ArrowRight') { let index = 0 Array.from(children).forEach((el, idx) => { (el as HTMLElement).classList.remove('carousel-dot--active') if (el === selectedEl) index = idx }) if (index < children.length - 1) index = index + 1 else index = 0 children[index].classList.add('carousel-dot--active') this.moveToLoop(index) } if (e.key === 'ArrowLeft') { let index = 0 Array.from(children).forEach((el, idx) => { (el as HTMLElement).classList.remove('carousel-dot--active') if (el === selectedEl) index = idx }) if (index < children.length && index > 0) index = index - 1 else index = children.length - 1 children[index].classList.add('carousel-dot--active') this.moveToLoop(index) } } moveToLoop(slideNumber: number) { if (this._slottedElements === null) return const lengthCarousel = this._slottedElements.length let moveToSlideNumber = slideNumber let offsetItem = 0 if (Math.abs(slideNumber) === lengthCarousel) moveToSlideNumber = 0 offsetItem = -Math.abs(moveToSlideNumber * this.itemOffset) if (slideNumber < 0) { moveToSlideNumber = lengthCarousel - Math.abs(slideNumber) offsetItem = moveToSlideNumber * this.itemOffset * -1 } this._slottedElements.forEach((slideNodeElement: HTMLElement | null) => { if (slideNodeElement === null) return // @ts-ignore const item: HTMLElement = slideNodeElement.shadowRoot.querySelector('.carousel-slider') item.style.transform = `translateX(${offsetItem}px)` }) this.activeSlide = moveToSlideNumber this.reloadCarouselDelayMove(this.carouselDelayIndex) } carouselDelayMove(delay: number) { this.carouselDelayIndex = window.setInterval(() => { this.autoSlide && this.moveToLoop(this.activeSlide + 1) }, delay) } reloadCarouselDelayMove(carouselDelayIndex: number) { clearInterval(carouselDelayIndex) this.carouselDelayMove(this.delay) } }