UNPKG

igniteui-webcomponents

Version:

Ignite UI for Web Components is a complete library of UI components, giving you the ability to build modern web applications using encapsulation and the concept of reusable components in a dependency-free approach.

556 lines 20.6 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var IgcCarouselComponent_1; import { ContextProvider } from '@lit/context'; import { LitElement, html, nothing } from 'lit'; import { property, queryAll, queryAssignedElements, state, } from 'lit/decorators.js'; import { createRef, ref } from 'lit/directives/ref.js'; import { styleMap } from 'lit/directives/style-map.js'; import { themes } from '../../theming/theming-decorator.js'; import IgcButtonComponent from '../button/button.js'; import { carouselContext } from '../common/context.js'; import { addKeyboardFocusRing } from '../common/controllers/focus-ring.js'; import { addGesturesController, } from '../common/controllers/gestures.js'; import { addKeybindings, arrowLeft, arrowRight, endKey, homeKey, } from '../common/controllers/key-bindings.js'; import { createMutationController, } from '../common/controllers/mutation-observer.js'; import { watch } from '../common/decorators/watch.js'; import { registerComponent } from '../common/definitions/register.js'; import { EventEmitterMixin } from '../common/mixins/event-emitter.js'; import { asNumber, createCounter, findElementFromEventPath, first, formatString, isLTR, last, partNameMap, wrap, } from '../common/util.js'; import IgcIconComponent from '../icon/icon.js'; import IgcCarouselIndicatorContainerComponent from './carousel-indicator-container.js'; import IgcCarouselIndicatorComponent from './carousel-indicator.js'; import IgcCarouselSlideComponent from './carousel-slide.js'; import { styles } from './themes/carousel.base.css.js'; import { all } from './themes/container.js'; import { styles as shared } from './themes/shared/carousel.common.css.js'; let IgcCarouselComponent = IgcCarouselComponent_1 = class IgcCarouselComponent extends EventEmitterMixin(LitElement) { static register() { registerComponent(IgcCarouselComponent_1, IgcCarouselIndicatorComponent, IgcCarouselIndicatorContainerComponent, IgcCarouselSlideComponent, IgcIconComponent, IgcButtonComponent); } get hasProjectedIndicators() { return this._projectedIndicators.length > 0; } get showIndicatorsLabel() { return this.total > this.maximumIndicatorsCount; } get nextIndex() { return wrap(0, this.total - 1, this.current + 1); } get prevIndex() { return wrap(0, this.total - 1, this.current - 1); } _observerCallback({ changes: { added, attributes }, }) { const activeSlides = this.slides.filter((slide) => slide.active); if (activeSlides.length <= 1) { return; } const idx = this.slides.indexOf(added.length ? last(added).node : last(attributes)); for (const [i, slide] of this.slides.entries()) { if (slide.active && i !== idx) { slide.active = false; } } this.activateSlide(this.slides[idx]); } get total() { return this.slides.length; } get current() { return Math.max(0, this.slides.indexOf(this._activeSlide)); } get isPlaying() { return this._playing; } get isPaused() { return this._paused; } contextChanged() { this._context.setValue(this, true); } intervalChange() { if (!this.isPlaying) { this._playing = true; } this.restartInterval(); } constructor() { super(); this._carouselId = `igc-carousel-${IgcCarouselComponent_1.increment()}`; this._carouselKeyboardInteractionFocus = addKeyboardFocusRing(this); this._hasKeyboardInteractionOnIndicators = false; this._hasMouseStop = false; this._context = new ContextProvider(this, { context: carouselContext, initialValue: this, }); this._carouselSlidesContainerRef = createRef(); this._indicatorsContainerRef = createRef(); this._prevButtonRef = createRef(); this._nextButtonRef = createRef(); this._playing = false; this._paused = false; this.disableLoop = false; this.disablePauseOnInteraction = false; this.hideNavigation = false; this.hideIndicators = false; this.vertical = false; this.indicatorsOrientation = 'end'; this.indicatorsLabelFormat = 'Slide {0}'; this.slidesLabelFormat = '{0} of {1}'; this.maximumIndicatorsCount = 10; this.animationType = 'slide'; this._internals = this.attachInternals(); this._internals.role = 'region'; this._internals.ariaRoleDescription = 'carousel'; this.addEventListener('pointerdown', this.handlePointerDown); this.addEventListener('pointerenter', this.handlePointerEnter); this.addEventListener('pointerleave', this.handlePointerLeave); addGesturesController(this, { ref: this._carouselSlidesContainerRef, touchOnly: true, }) .set('swipe-left', this.handleHorizontalSwipe) .set('swipe-right', this.handleHorizontalSwipe) .set('swipe-up', this.handleVerticalSwipe) .set('swipe-down', this.handleVerticalSwipe); addKeybindings(this, { ref: this._indicatorsContainerRef, bindingDefaults: { preventDefault: true }, }) .set(arrowLeft, this.handleArrowLeft) .set(arrowRight, this.handleArrowRight) .set(homeKey, this.handleHomeKey) .set(endKey, this.handleEndKey); addKeybindings(this, { ref: this._prevButtonRef, bindingDefaults: { preventDefault: true }, }).setActivateHandler(this.handleNavigationInteractionPrevious); addKeybindings(this, { ref: this._nextButtonRef, bindingDefaults: { preventDefault: true }, }).setActivateHandler(this.handleNavigationInteractionNext); createMutationController(this, { callback: this._observerCallback, filter: [IgcCarouselSlideComponent.tagName], config: { attributeFilter: ['active'], childList: true, subtree: true, }, }); } handleSlotChange() { if (this.total) { this.activateSlide(this.slides.findLast((slide) => slide.active) ?? first(this.slides)); } } handleIndicatorSlotChange() { this.requestUpdate(); } handlePointerDown() { if (this._carouselKeyboardInteractionFocus.focused) { this._carouselKeyboardInteractionFocus.reset(); } } handlePointerEnter() { this._hasMouseStop = true; if (this._carouselKeyboardInteractionFocus.focused) { return; } this.handlePauseOnInteraction(); } handlePointerLeave() { this._hasMouseStop = false; if (this._carouselKeyboardInteractionFocus.focused) { return; } this.handlePauseOnInteraction(); } handleFocusIn() { if (this._carouselKeyboardInteractionFocus.focused || this._hasMouseStop) { return; } this.handlePauseOnInteraction(); } handleFocusOut(event) { const node = event.relatedTarget; if (this.contains(node) || this.renderRoot.contains(node)) { return; } if (this._carouselKeyboardInteractionFocus.focused) { this._carouselKeyboardInteractionFocus.reset(); if (!this._hasMouseStop) { this.handlePauseOnInteraction(); } } } handlePauseOnInteraction() { if (!this.interval || this.disablePauseOnInteraction) return; if (this.isPlaying) { this.pause(); this.emitEvent('igcPaused'); } else { this.play(); this.emitEvent('igcPlaying'); } } async handleArrowLeft() { this._hasKeyboardInteractionOnIndicators = true; this.handleInteraction(isLTR(this) ? this.prev : this.next); } async handleArrowRight() { this._hasKeyboardInteractionOnIndicators = true; this.handleInteraction(isLTR(this) ? this.next : this.prev); } async handleHomeKey() { this._hasKeyboardInteractionOnIndicators = true; this.handleInteraction(() => this.select(isLTR(this) ? first(this.slides) : last(this.slides))); } async handleEndKey() { this._hasKeyboardInteractionOnIndicators = true; this.handleInteraction(() => this.select(isLTR(this) ? last(this.slides) : first(this.slides))); } handleVerticalSwipe({ data: { direction } }) { if (this.vertical) { this.handleInteraction(direction === 'up' ? this.next : this.prev); } } handleHorizontalSwipe({ data: { direction } }) { if (!this.vertical) { this.handleInteraction(async () => { if (isLTR(this)) { direction === 'left' ? await this.next() : await this.prev(); } else { direction === 'left' ? await this.prev() : await this.next(); } }); } } async handleIndicatorClick(event) { const indicator = findElementFromEventPath(IgcCarouselIndicatorComponent.tagName, event); const index = this.hasProjectedIndicators ? this._projectedIndicators.indexOf(indicator) : Array.from(this._defaultIndicators).indexOf(indicator); this.handleInteraction(() => this.select(this.slides[index], index > this.current ? 'next' : 'prev')); } handleNavigationInteractionNext() { this.handleInteraction(this.next); } handleNavigationInteractionPrevious() { this.handleInteraction(this.prev); } async handleInteraction(callback) { if (this.interval) { this.resetInterval(); } await callback.call(this); this.emitEvent('igcSlideChanged', { detail: this.current }); if (this.interval) { this.restartInterval(); } } activateSlide(slide) { if (this._activeSlide) { this._activeSlide.active = false; } this._activeSlide = slide; this._activeSlide.active = true; if (this._hasKeyboardInteractionOnIndicators) { this.hasProjectedIndicators ? this._projectedIndicators[this.current].focus() : this._defaultIndicators[this.current].focus(); this._hasKeyboardInteractionOnIndicators = false; } } updateProjectedIndicators() { for (const [idx, slide] of this.slides.entries()) { const indicator = this._projectedIndicators[idx]; indicator.active = slide.active; indicator.index = idx; this.setAttribute('aria-controls', slide.id); } } resetInterval() { if (this._lastInterval) { clearInterval(this._lastInterval); this._lastInterval = null; } } restartInterval() { this.resetInterval(); if (asNumber(this.interval) > 0) { this._lastInterval = setInterval(() => { if (this.isPlaying && this.total) { this.next(); this.emitEvent('igcSlideChanged', { detail: this.current }); } else { this.pause(); } }, this.interval); } } async animateSlides(nextSlide, currentSlide, dir) { if (dir === 'next') { currentSlide.previous = true; currentSlide.toggleAnimation('out'); this.activateSlide(nextSlide); await nextSlide.toggleAnimation('in'); currentSlide.previous = false; } else { currentSlide.previous = true; currentSlide.toggleAnimation('in', 'reverse'); this.activateSlide(nextSlide); await nextSlide.toggleAnimation('out', 'reverse'); currentSlide.previous = false; } } play() { if (!this.isPlaying) { this._paused = false; this._playing = true; this.restartInterval(); } } pause() { if (this.isPlaying) { this._playing = false; this._paused = true; this.resetInterval(); } } async next() { if (this.disableLoop && this.nextIndex === 0) { this.pause(); return false; } return await this.select(this.slides[this.nextIndex], 'next'); } async prev() { if (this.disableLoop && this.prevIndex === this.total - 1) { this.pause(); return false; } return await this.select(this.slides[this.prevIndex], 'prev'); } async select(slideOrIndex, animationDirection) { let index; let slide; if (typeof slideOrIndex === 'number') { index = slideOrIndex; slide = this.slides.at(index); } else { slide = slideOrIndex; index = this.slides.indexOf(slide); } if (index === this.current || index === -1 || !slide) { return false; } const dir = animationDirection ?? (index > this.current ? 'next' : 'prev'); await this.animateSlides(slide, this._activeSlide, dir); return true; } navigationTemplate() { return html ` <igc-button ${ref(this._prevButtonRef)} type="button" part="navigation previous" aria-label="Previous slide" aria-controls=${this._carouselId} ?disabled=${this.disableLoop && this.current === 0} @click=${this.handleNavigationInteractionPrevious} > <slot name="previous-button"> <igc-icon name="carousel_prev" collection="default" aria-hidden="true" ></igc-icon> </slot> </igc-button> <igc-button ${ref(this._nextButtonRef)} type="button" part="navigation next" aria-label="Next slide" aria-controls=${this._carouselId} ?disabled=${this.disableLoop && this.current === this.total - 1} @click=${this.handleNavigationInteractionNext} > <slot name="next-button"> <igc-icon name="carousel_next" collection="default" aria-hidden="true" ></igc-icon> </slot> </igc-button> `; } *renderIndicators() { for (const [i, slide] of this.slides.entries()) { const forward = slide.active ? 'visible' : 'hidden'; const backward = slide.active ? 'hidden' : 'visible'; yield html ` <igc-carousel-indicator exportparts="indicator, active, inactive" .active=${slide.active} .index=${i} > <div part="dot" style=${styleMap({ visibility: backward, zIndex: 1 })} ></div> <div part="dot active" slot="active" style=${styleMap({ visibility: forward })} ></div> </igc-carousel-indicator> `; } } indicatorTemplate() { const parts = partNameMap({ indicators: true, start: this.indicatorsOrientation === 'start', }); return html ` <igc-carousel-indicator-container> <div ${ref(this._indicatorsContainerRef)} role="tablist" part=${parts}> <slot name="indicator" @slotchange=${this.handleIndicatorSlotChange} @click=${this.handleIndicatorClick} > ${this.hasProjectedIndicators ? this.updateProjectedIndicators() : this.renderIndicators()} </slot> </div> </igc-carousel-indicator-container> `; } labelTemplate() { const parts = partNameMap({ label: true, indicators: true, start: this.indicatorsOrientation === 'start', }); const value = formatString(this.slidesLabelFormat, this.current + 1, this.total); return html ` <div part=${parts}> <span>${value}</span> </div> `; } render() { return html ` <section @focusin=${this.handleFocusIn} @focusout=${this.handleFocusOut}> ${this.hideNavigation ? nothing : this.navigationTemplate()} ${this.hideIndicators || this.showIndicatorsLabel ? nothing : this.indicatorTemplate()} ${!this.hideIndicators && this.showIndicatorsLabel ? this.labelTemplate() : nothing} <div ${ref(this._carouselSlidesContainerRef)} id=${this._carouselId} aria-live=${this.interval && this.isPlaying ? 'off' : 'polite'} > <slot @slotchange=${this.handleSlotChange}></slot> </div> </section> `; } }; IgcCarouselComponent.styles = [styles, shared]; IgcCarouselComponent.tagName = 'igc-carousel'; IgcCarouselComponent.increment = createCounter(); __decorate([ queryAll(IgcCarouselIndicatorComponent.tagName) ], IgcCarouselComponent.prototype, "_defaultIndicators", void 0); __decorate([ queryAssignedElements({ selector: IgcCarouselIndicatorComponent.tagName, slot: 'indicator', }) ], IgcCarouselComponent.prototype, "_projectedIndicators", void 0); __decorate([ state() ], IgcCarouselComponent.prototype, "_activeSlide", void 0); __decorate([ state() ], IgcCarouselComponent.prototype, "_playing", void 0); __decorate([ state() ], IgcCarouselComponent.prototype, "_paused", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: 'disable-loop' }) ], IgcCarouselComponent.prototype, "disableLoop", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: 'disable-pause-on-interaction', }) ], IgcCarouselComponent.prototype, "disablePauseOnInteraction", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: 'hide-navigation' }) ], IgcCarouselComponent.prototype, "hideNavigation", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: 'hide-indicators' }) ], IgcCarouselComponent.prototype, "hideIndicators", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], IgcCarouselComponent.prototype, "vertical", void 0); __decorate([ property({ reflect: false, attribute: 'indicators-orientation' }) ], IgcCarouselComponent.prototype, "indicatorsOrientation", void 0); __decorate([ property({ attribute: 'indicators-label-format' }) ], IgcCarouselComponent.prototype, "indicatorsLabelFormat", void 0); __decorate([ property({ attribute: 'slides-label-format' }) ], IgcCarouselComponent.prototype, "slidesLabelFormat", void 0); __decorate([ property({ type: Number, reflect: false }) ], IgcCarouselComponent.prototype, "interval", void 0); __decorate([ property({ type: Number, reflect: false, attribute: 'maximum-indicators-count', }) ], IgcCarouselComponent.prototype, "maximumIndicatorsCount", void 0); __decorate([ property({ attribute: 'animation-type' }) ], IgcCarouselComponent.prototype, "animationType", void 0); __decorate([ queryAssignedElements({ selector: IgcCarouselSlideComponent.tagName }) ], IgcCarouselComponent.prototype, "slides", void 0); __decorate([ watch('animationType'), watch('slidesLabelFormat'), watch('indicatorsLabelFormat') ], IgcCarouselComponent.prototype, "contextChanged", null); __decorate([ watch('interval') ], IgcCarouselComponent.prototype, "intervalChange", null); IgcCarouselComponent = IgcCarouselComponent_1 = __decorate([ themes(all) ], IgcCarouselComponent); export default IgcCarouselComponent; //# sourceMappingURL=carousel.js.map