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
JavaScript
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}
=${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}
=${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"
=${this.handleIndicatorSlotChange}
=${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 =${this.handleFocusIn} =${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 =${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