import { observable } from 'mobx'; import { FC, WebCell, WebCellProps, attribute, component, observer, on, reaction } from 'web-cell'; export interface CarouselItemProps extends WebCellProps { interval?: number; } export const CarouselItem: FC = ({ className = '', interval, children, ...props }) => (
{children}
); export const CarouselCaption: FC> = ({ className = '', children, ...props }) => (
{children}
); interface ItemMeta { caption?: string; } export interface CarouselProps { interval?: number; } export interface Carousel extends WebCell {} @component({ tagName: 'carousel-box', mode: 'open' }) @observer export class Carousel extends HTMLElement implements WebCell { @attribute @observable accessor interval: number | undefined; @observable accessor itemMeta: ItemMeta[] = []; @attribute @observable accessor currentIndex = 0; private timer: number; connectedCallback() { if (this.interval) this.timer ||= window.setInterval( () => this.turnByOffset(1), this.interval ); else this.handleActiveItem(this.currentIndex); } disconnectedCallback() { clearInterval(this.timer); } mountedCallback() { const items = [...this.querySelectorAll('.carousel-item')]; if (this.itemMeta.length !== items.length) this.itemMeta = items.map(item => ({ caption: ( item.querySelector('.carousel-caption')?.textContent || item.textContent ).trim() })); } turnByOffset(delta: number) { this.currentIndex = (this.currentIndex + delta) % this.itemMeta.length; } @on('click', '.carousel > button') handleButtonClick(_: MouseEvent, { dataset }: HTMLButtonElement) { this.turnByOffset(dataset.bsSlide === 'next' ? 1 : -1); } @on('keyup', '.carousel > button') handleButtonPress({ key }: KeyboardEvent, { dataset }: HTMLButtonElement) { if (key === 'Enter') this.turnByOffset(dataset.bsSlide === 'next' ? 1 : -1); } @on('click', '.carousel-indicators button') handleIndicatorsButtonClick(_: MouseEvent, { dataset }: HTMLButtonElement) { this.currentIndex = +dataset.bsSlideTo; } @on('keyup', '.carousel-indicators button') handleIndicatorsButtonPress( { key }: KeyboardEvent, { dataset }: HTMLButtonElement ) { if (key === 'Enter') this.currentIndex = +dataset.bsSlideTo; } @reaction(({ currentIndex }) => currentIndex) handleActiveItem(currentIndex: number) { this.querySelector('.carousel-item.active')?.classList.remove('active'); this.children[currentIndex].classList.add('active'); } renderContent() { const { itemMeta, currentIndex } = this; return (
{itemMeta.map(({ caption }, index) => (
); } render() { return ( <> {this.renderContent()} ); } }