UNPKG

embla-carousel-angular

Version:
146 lines (140 loc) 6.11 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, NgZone, ElementRef, DestroyRef, input, output, afterNextRender, effect, Directive } from '@angular/core'; import EmblaCarousel from 'embla-carousel'; import { canUseDOM, areOptionsEqual, arePluginsEqual } from 'embla-carousel-reactive-utils'; import { map, scan, filter, Subject } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; const EMBLA_OPTIONS_TOKEN = new InjectionToken('embla global options', { factory: () => undefined, }); function provideEmblaGlobalOptions(options) { return [ { provide: EMBLA_OPTIONS_TOKEN, useValue: options, }, ]; } // Solution from https://stackoverflow.com/a/53627167 function throttleDistinct(duration, equals = (a, b) => a === b) { return (source) => { return source .pipe(map(x => { return { value: x, time: Date.now(), keep: true, }; }), scan((acc, curr) => { const diff = curr.time - acc.time; const isSame = equals(curr.value, acc.value); return diff > duration || (diff < duration && !isSame) ? { ...curr, keep: true } : { ...acc, keep: false }; }), filter(x => x.keep), map(x => x.value)); }; } class EmblaCarouselDirective { constructor() { this._globalOptions = inject(EMBLA_OPTIONS_TOKEN); this._ngZone = inject(NgZone); this._elementRef = inject(ElementRef); this._destroyRef = inject(DestroyRef); this.options = input({}); this.plugins = input([]); this.subscribeToEvents = input([]); this.eventsThrottleTime = input(100); this.emblaChange = output(); this._destroy$ = new Subject(); this.storedOptions = this.options(); this.storedPlugins = this.plugins(); if (this._globalOptions) { EmblaCarousel.globalOptions = this._globalOptions; } // Init Embla Carousel afterNextRender({ write: () => { if (!canUseDOM()) return; this._ngZone.runOutsideAngular(() => { this.emblaApi = EmblaCarousel(this._elementRef.nativeElement, this.storedOptions, this.storedPlugins); this.listenEvents(); }); }, }); // Watch input changes effect(() => { const plugins = this.plugins(); const options = this.options(); let shouldReInit = false; if (options && !areOptionsEqual(this.storedOptions, options)) { this.storedOptions = options; shouldReInit = true; } if (plugins && !arePluginsEqual(this.storedPlugins, plugins)) { this.storedPlugins = plugins; shouldReInit = true; } if (shouldReInit) { this.reInit(); } }); // Cleanup Embla Carousel this._destroyRef.onDestroy(() => { this.emblaApi?.destroy(); }); } scrollTo(...args) { this._ngZone.runOutsideAngular(() => this.emblaApi?.scrollTo(...args)); } scrollPrev(...args) { this._ngZone.runOutsideAngular(() => this.emblaApi?.scrollPrev(...args)); } scrollNext(...args) { this._ngZone.runOutsideAngular(() => this.emblaApi?.scrollNext(...args)); } reInit() { if (!this.emblaApi) { return; } this._ngZone.runOutsideAngular(() => { this.emblaApi?.reInit(this.storedOptions, this.storedPlugins); }); } /** * `eventsThrottler$` Subject was made just because `scroll` event fires too often. */ listenEvents() { if (this.subscribeToEvents().length === 0) { return; } const eventsThrottler$ = new Subject(); eventsThrottler$ .pipe(throttleDistinct(this.eventsThrottleTime()), takeUntilDestroyed(this._destroyRef)) .subscribe(eventName => { this._ngZone.run(() => this.emblaChange.emit(eventName)); }); this._ngZone.runOutsideAngular(() => { this.subscribeToEvents().forEach(eventName => { this.emblaApi.on(eventName, () => eventsThrottler$.next(eventName)); }); }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: EmblaCarouselDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.1", type: EmblaCarouselDirective, isStandalone: true, selector: "[emblaCarousel]", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, subscribeToEvents: { classPropertyName: "subscribeToEvents", publicName: "subscribeToEvents", isSignal: true, isRequired: false, transformFunction: null }, eventsThrottleTime: { classPropertyName: "eventsThrottleTime", publicName: "eventsThrottleTime", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { emblaChange: "emblaChange" }, exportAs: ["emblaCarousel"], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: EmblaCarouselDirective, decorators: [{ type: Directive, args: [{ selector: '[emblaCarousel]', exportAs: 'emblaCarousel', }] }], ctorParameters: () => [] }); /* * Public API Surface of embla-carousel-angular */ /** * Generated bundle index. Do not edit. */ export { EMBLA_OPTIONS_TOKEN, EmblaCarouselDirective, provideEmblaGlobalOptions }; //# sourceMappingURL=embla-carousel-angular.mjs.map