UNPKG

ngx-hm-carousel

Version:

A light carousel for Angular, support mobile touch by hammerJs with SSR

831 lines (825 loc) 42.2 kB
import * as i0 from '@angular/core'; import { Directive, inject, ViewContainerRef, TemplateRef, input, PLATFORM_ID, DestroyRef, Renderer2, NgZone, ChangeDetectorRef, viewChild, ElementRef, contentChildren, contentChild, computed, signal, effect, forwardRef, Component, ChangeDetectionStrategy } from '@angular/core'; import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet, AsyncPipe } from '@angular/common'; import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { merge, fromEvent, tap, BehaviorSubject, Subject, forkJoin, filter, switchMap, timer, takeUntil, of, interval, bufferCount } from 'rxjs'; import { resizeObservable } from '@nghedgehog/core'; class NgxHmCarouselItemDirective { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NgxHmCarouselItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.3", type: NgxHmCarouselItemDirective, isStandalone: true, selector: "[ngx-hm-carousel-item]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NgxHmCarouselItemDirective, decorators: [{ type: Directive, args: [{ selector: '[ngx-hm-carousel-item]', standalone: true, }] }] }); // TODO: ssr problem should not hide on ssr // TODO: show number change should recalculate is show and init show number class NgxHmCarouselDynamicDirective { _view = inject(ViewContainerRef); _template = inject((TemplateRef)); index = input(undefined, { alias: 'ngxHmCarouselDynamic', }); length = input(0, { alias: 'ngxHmCarouselDynamicLength' }); show = input(1, { alias: 'ngxHmCarouselDynamicShow' }); currentI = input(0, { alias: 'ngxHmCarouselDynamicIndex', transform: (value) => { if (!this.completed) { const nextI = value + this.show(); const prevI = value - this.show(); if (this.index() === 0 || this.index() === this.length() - 1 || this.index() === nextI || this.index() === prevI || this.index() === value) { this._view.createEmbeddedView(this._template); this.completed = true; } } return value; }, }); completed = false; constructor() { this._view.clear(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NgxHmCarouselDynamicDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.3", type: NgxHmCarouselDynamicDirective, isStandalone: true, selector: "[ngxHmCarouselDynamic]", inputs: { index: { classPropertyName: "index", publicName: "ngxHmCarouselDynamic", isSignal: true, isRequired: false, transformFunction: null }, length: { classPropertyName: "length", publicName: "ngxHmCarouselDynamicLength", isSignal: true, isRequired: false, transformFunction: null }, show: { classPropertyName: "show", publicName: "ngxHmCarouselDynamicShow", isSignal: true, isRequired: false, transformFunction: null }, currentI: { classPropertyName: "currentI", publicName: "ngxHmCarouselDynamicIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NgxHmCarouselDynamicDirective, decorators: [{ type: Directive, args: [{ selector: '[ngxHmCarouselDynamic]', standalone: true, }] }], ctorParameters: () => [] }); class NgxHmCarouselComponent { platformId = inject(PLATFORM_ID); _document = inject(DOCUMENT); _destroyRef = inject(DestroyRef); _renderer = inject(Renderer2); _zone = inject(NgZone); _cd = inject(ChangeDetectorRef); container = viewChild.required('containerElm', { read: ElementRef }); btnPrev = viewChild('prev'); btnNext = viewChild('next'); progressContainerElm = viewChild('progress', { read: ElementRef }); // get all item elms itemElms = contentChildren(NgxHmCarouselItemDirective, { read: ElementRef, descendants: true, }); contentPrev = contentChild('carouselPrev', { read: TemplateRef }); contentNext = contentChild('carouselNext', { read: TemplateRef }); dotElm = contentChild('carouselDot'); progressElm = contentChild('carouselProgress'); infiniteContainer = contentChild('infiniteContainer', { read: ViewContainerRef, }); contentContent = contentChild.required('carouselContent', { read: TemplateRef, }); /** the data you using with *ngFor, it need when infinite mode or autoplay mode */ data = input([]); /** when infinite is true, the animation time with item, default is 400. */ aniTime = input(400); /** this class will add in #containerElm when model change */ aniClass = input('transition'); /** * this class will add when carousel auto play, * this default autoplay animation is same as aniClass */ aniClassAuto = input(this.aniClass()); /** * user move picture with the container width rate, * when more than that rate, it will go to next or prev, * set false will never move with distance rate, * default is `0.15` */ panBoundary = input(0.15, { alias: 'pan-boundary' }); /** when `show-num` is bigger than 1, the first item align, default is `center` */ align = input('center'); /** * disable when drag occur the child element will follow touch point. * default is `false` */ notDrag = input(false, { alias: 'not-follow-pan' }); /** * the event binding state for stop auto play when mouse move over */ mouseEnable = input(false, { alias: 'mouse-enable' }); /** each auto play between time */ delay = input(8000, { alias: 'between-delay' }); /** auto play direction, default is `right`. */ direction = input('right', { alias: 'autoplay-direction' }); /** how many number with each scroll, default is `1`. */ scrollNum = input(1, { alias: 'scroll-num' }); /** Could user scroll many item once, simulate with scrollbar, default is `false` */ isDragMany = input(false, { alias: 'drag-many' }); /** Minimal velocity required before recognizing, unit is in px per ms, default `0.3` */ swipeVelocity = input(0.3, { alias: 'swipe-velocity' }); /** * switch show number with custom logic like css * @media (min-width: `number`px) */ breakpoint = input([]); /** disable drag event with touch and mouse pan moving, default is `false` */ disableDrag = input(undefined, { alias: 'disable-drag', transform: (value) => { if (this.rootElm) { if (value) { this.destroyHammer(); } else { this.hammer = this.bindHammer(); } } return value; }, }); /** is the carousel can move infinite */ infinite = input(false, { alias: 'infinite', }); /** auto play speed */ speed = input(undefined, { alias: 'autoplay-speed', transform: (value) => { this.speedChange.next(value); return value; }, }); /** PinchRecognizer that you want to add to hammer event */ recognizers = input([]); /** PinchRecognizer that you want to add to hammer event */ stopPanListener = input(false); _showNum = 1; /** * how many number items to show once, default is `1` * set `auto` to using `[breakpoint]` set value. */ showNum = input(this._showNum, { alias: 'show-num', transform: (value) => { if (value === 'auto') { this.isAutoNum = true; } else { this._showNum = +value; if (this.rootElm) { this.setViewWidth(); this.reSetAlignDistance(); } } return this._showNum; }, }); /** is that carousel auto play */ autoplay = input(undefined, { alias: 'autoplay', transform: (value) => { if (isPlatformBrowser(this.platformId)) { if (this.elms) { this.progressWidth = 0; if (value) { this.doNextSub$ = this.doNext$?.subscribe(); } else { if (this.doNextSub$) { this.doNextSub$.unsubscribe(); } } } } return value; }, }); get currentIndex() { return this._currentIndex; } set currentIndex(value) { // if now index is not equal to save index, do something if (this.currentIndex !== value) { // if the value is not contain with the boundary not handler if (!this.runLoop() && !(0 <= value && value <= this.itemElms().length - 1)) { return; } this._currentIndex = value; if (this.elms) { if (this.autoplay() && !this.isFromAuto) { this._zone.runOutsideAngular(() => { this.stopEvent.next(undefined); this.callRestart(); }); } this.drawView(this.currentIndex, this.hasInitWriteValue); if (this.isDragMany()) { this.hasInitWriteValue = true; } } if (0 <= this.currentIndex && this.currentIndex <= this.itemElms().length - 1) { this._zone.run(() => { this.onChange(this.currentIndex); this._cd.detectChanges(); }); } } this.isFromAuto = false; } get progressWidth() { return this._progressWidth; } set progressWidth(value) { const containerElm = this.progressContainerElm(); if (this.progressElm() && containerElm && this.autoplay()) { this._progressWidth = value; this._renderer.setStyle(containerElm.nativeElement.children[0], 'width', `${this.progressWidth}%`); } } get grabbing() { return this._grabbing; } set grabbing(value) { if (this._grabbing !== value) { // console.log(value); this._zone.run(() => { this._grabbing = value; if (value) { this._renderer.addClass(this.containerElm, 'grabbing'); } else { this.panCount = 0; this.callRestart(); this._renderer.removeClass(this.containerElm, 'grabbing'); } this._cd.detectChanges(); }); } } // using for check mouse or touchend leaveObs$ = merge(fromEvent(this._document, 'mouseup'), fromEvent(this._document, 'touchend')).pipe(tap((e) => { this.grabbing = false; e.stopPropagation(); e.preventDefault(); })); hammer; set left(value) { if (isPlatformBrowser(this.platformId)) { this._renderer.setStyle(this.containerElm, 'transform', `translateX(${value}px)`); } else { this._renderer.setStyle(this.containerElm, 'transform', `translateX(${value}%)`); } } get maxRightIndex() { let addIndex = 0; switch (this.align()) { case 'left': addIndex = 0; break; case 'center': addIndex = this.showNum() - 1; break; case 'right': addIndex = this.showNum() - 1; break; } return this.itemElms().length - 1 - this._showNum + 1 + addIndex; } runLoop = computed(() => this.autoplay() || this.infinite()); get lengthOne() { return this.itemElms().length === 1; } get rootElmWidth() { return isPlatformBrowser(this.platformId) ? this.rootElm.getBoundingClientRect().width : 100; } set containerElmWidth(value) { this.setStyle(this.containerElm, 'width', value); } isFromAuto = true; isAutoNum = false; mouseOnContainer = false; alignDistance = 0; elmWidth = 0; rootElm; containerElm; elms; infiniteElmRefs = signal([]); saveTimeOut$; doNextSub$; doNext$; restart = new BehaviorSubject(null); speedChange = new BehaviorSubject(5000); stopEvent = new Subject(); _progressWidth = 0; _currentIndex = 0; _grabbing = false; panCount = 0; // this variable use for check the init value is write with ngModel, // when init first, not set with animation hasInitWriteValue = false; itemElmsChanges = toObservable(this.itemElms); constructor() { effect(() => { this.infiniteElmRefs().forEach((ref) => { this.addStyle(ref.rootNodes[0], { visibility: this.runLoop() ? 'visible' : 'hidden', }); }); }); const effectRef = effect(() => { this.rootElm = this.container().nativeElement; this.containerElm = this.rootElm.children[0]; this.init(); forkJoin([ this.bindClick(), // when item changed, remove old hammer binding, and reset width this.itemElmsChanges.pipe( // detectChanges to change view dots tap(() => { if (this.currentIndex > this.itemElms().length - 1) { // pass the change detection check, requestAnimationFrame(() => { this.currentIndex = this.itemElms().length - 1; }); } this.destroy(); this.removeInfiniteElm(); this.init(); this.progressWidth = 0; }), tap(() => this._cd.detectChanges())), resizeObservable(this.rootElm, () => this.containerResize()), ]) .pipe(takeUntilDestroyed(this._destroyRef)) .subscribe(); // only exec once effectRef.destroy(); }, { allowSignalWrites: true, }); } ngOnDestroy() { this.destroy(); } writeValue(value) { if (value || value === 0) { this.currentIndex = value; this.hasInitWriteValue = true; } } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } onChange = (_) => { // }; onTouched = () => { // }; init() { this.initVariable(); this.setViewWidth(true); this.reSetAlignDistance(); if (!this.disableDrag()) { this.hammer = this.bindHammer(); } this.drawView(this.currentIndex, false); if (isPlatformBrowser(this.platformId) && this.runLoop()) { this.addInfiniteElm(); } } destroy() { this.destroyHammer(); if (this.autoplay()) { this.doNextSub$?.unsubscribe(); } } destroyHammer() { if (this.hammer) { this.hammer.destroy(); } } addInfiniteElm() { const infiniteContainer = this.infiniteContainer(); const showNum = this.showNum(); if (!infiniteContainer || typeof showNum !== 'number') return; for (let i = 1; i <= showNum; i++) { const elm = infiniteContainer.createEmbeddedView(this.contentContent(), { $implicit: this.data()[this.itemElms().length - i], index: this.itemElms().length - i, }); this.addStyle(elm.rootNodes[0], { position: 'absolute', // boxShadow: `0 0 0 5000px rgba(200, 75, 75, 0.5) inset`, transform: `translateX(-${100 * i}%)`, visibility: this.runLoop() ? 'visible' : 'hidden', }); this.setStyle(elm.rootNodes[0], 'width', this.elmWidth); const elm2 = infiniteContainer.createEmbeddedView(this.contentContent(), { $implicit: this.data()[i - 1], index: i - 1, }); this.addStyle(elm2.rootNodes[0], { // boxShadow: `0 0 0 5000px rgba(200, 75, 75, 0.5) inset`, position: 'absolute', right: 0, top: 0, transform: `translateX(${100 * i}%)`, visibility: this.runLoop() ? 'visible' : 'hidden', }); this.setStyle(elm2.rootNodes[0], 'width', this.elmWidth); elm.detectChanges(); elm2.detectChanges(); this.infiniteElmRefs.set([...this.infiniteElmRefs(), elm, elm2]); } } removeInfiniteElm() { this.infiniteElmRefs().forEach((a) => { a.detach(); a.destroy(); }); if (this.infiniteContainer()) { this.infiniteContainer().clear(); } this.infiniteElmRefs.set([]); } containerResize() { this.setViewWidth(); this.reSetAlignDistance(); const showNum = this.showNum(); const touchEnd = typeof showNum === 'number' && showNum >= this.elms.length; if (this.align() !== 'center' && touchEnd) { this.currentIndex = 0; } this.drawView(this.currentIndex, false); } initVariable() { this._zone.runOutsideAngular(() => { this.elms = this.itemElms().map((x) => x.nativeElement); let startEvent = this.restart.asObservable(); let stopEvent = this.stopEvent.asObservable(); if (this.mouseEnable()) { startEvent = merge(startEvent, fromEvent(this.containerElm, 'mouseleave').pipe( // when leave, we should reverse grabbing state to set the mouseOn state, // because when the grabbing, the mask will on, and it will occur leave again filter(() => !this.grabbing), tap(() => (this.mouseOnContainer = false)))); stopEvent = merge(stopEvent, fromEvent(this.containerElm, 'mouseover').pipe(tap(() => (this.mouseOnContainer = true)))); } this.doNext$ = startEvent.pipe( // not using debounceTime, it will stop mouseover event detect, will cause mouse-enable error // debounceTime(this.delay), switchMap(() => this.speedChange), switchMap(() => timer(this.delay()).pipe(switchMap(() => this.runProgress(20)), tap(() => { this.isFromAuto = true; // console.log('next'); if (this.direction() === 'left') { this.currentIndex -= this.scrollNum(); } else { this.currentIndex += this.scrollNum(); } }), takeUntil(stopEvent.pipe(tap(() => (this.progressWidth = 0))))))); if (this.autoplay()) { this.doNextSub$ = this.doNext$.subscribe(); } }); } reSetAlignDistance() { switch (this.align()) { case 'center': this.alignDistance = (this.rootElmWidth - this.elmWidth) / 2; break; case 'left': this.alignDistance = 0; break; case 'right': this.alignDistance = this.rootElmWidth - this.elmWidth; break; } } setViewWidth(isInit) { if (this.isAutoNum) { this._showNum = this.getAutoNum(); } this._renderer.addClass(this.containerElm, 'grab'); if (isInit) { // remain one elm height this._renderer.addClass(this.containerElm, 'ngx-hm-carousel-display-nowrap'); } this.elmWidth = this.rootElmWidth / this._showNum; this._renderer.removeClass(this.containerElm, 'ngx-hm-carousel-display-nowrap'); this.containerElmWidth = this.elmWidth * this.elms.length; this._renderer.setStyle(this.containerElm, 'position', 'relative'); this.infiniteElmRefs().forEach((ref) => { this.setStyle(ref.rootNodes[0], 'width', this.elmWidth); }); this.elms.forEach((elm) => { this.setStyle(elm, 'width', this.elmWidth); }); } bindHammer() { if (!isPlatformBrowser(this.platformId)) { return null; } return this._zone.runOutsideAngular(() => { const hm = new Hammer.Manager(this.containerElm); const pan = new Hammer.Pan({ direction: Hammer.DIRECTION_HORIZONTAL, threshold: 0, }); hm.add(pan); hm.on('panleft panright panend pancancel', (e) => { // console.log(e.type); if (this.stopPanListener()) { return; } if (this.lengthOne) { return; } this.removeContainerTransition(); if (this.autoplay()) { this._zone.runOutsideAngular(() => { this.stopEvent.next(undefined); }); } switch (e.type) { case 'panleft': case 'panright': { this.panCount++; // only when panmove more than two times, set move if (this.panCount < 2) { return; } this.grabbing = true; const showNum = this.showNum(); // When show-num is bigger than length, stop hammer if (this.align() !== 'center' && typeof showNum === 'number' && showNum >= this.elms.length) { this.hammer?.stop(true); return; } // Slow down at the first and last pane. if (!this.runLoop() && this.outOfBound(e.type)) { e.deltaX *= 0.2; } if (!this.notDrag()) { this.left = -this.currentIndex * this.elmWidth + this.alignDistance + e.deltaX; } // if not drag many, when bigger than half if (!this.isDragMany()) { if (Math.abs(e.deltaX) > this.elmWidth * 0.5) { if (e.deltaX > 0) { this.currentIndex -= this.scrollNum(); } else { this.currentIndex += this.scrollNum(); } this.hammer?.stop(true); return; } } } break; case 'pancancel': this.drawView(this.currentIndex); break; case 'panend': { const panBoundary = this.panBoundary(); // if boundary more than rate if (panBoundary !== false && Math.abs(e.deltaX) > this.elmWidth * panBoundary) { const moveNum = this.isDragMany() ? Math.ceil(Math.abs(e.deltaX) / this.elmWidth) : this.scrollNum(); const prevIndex = this.currentIndex - moveNum; const nextIndex = this.currentIndex + moveNum; // if right if (e.deltaX > 0) { this.goPrev(prevIndex); // left } else { this.goNext(nextIndex); } break; } else if (e.velocityX < -this.swipeVelocity() && e.distance > 10) { this.goNext(this.currentIndex + this.scrollNum()); } else if (e.velocityX > this.swipeVelocity() && e.distance > 10) { this.goPrev(this.currentIndex - this.scrollNum()); } else { this.drawView(this.currentIndex); } } break; } }); this.recognizers().forEach((recognizer) => { hm.add(recognizer); }); return hm; }); } goPrev(prevIndex) { if (!this.runLoop() && prevIndex < 0) { prevIndex = 0; this.drawView(0); } this.currentIndex = prevIndex; } goNext(nextIndex) { if (!this.runLoop() && nextIndex > this.maxRightIndex) { nextIndex = this.maxRightIndex; this.drawView(nextIndex); } this.currentIndex = nextIndex; } bindClick() { const nextBtn = this.btnNext(); const prevBtn = this.btnPrev(); if (nextBtn && prevBtn) { return forkJoin([ fromEvent(nextBtn.nativeElement, 'click').pipe(tap(() => this.currentIndex++)), fromEvent(prevBtn.nativeElement, 'click').pipe(tap(() => this.currentIndex--)), ]); } return of(null); } callRestart() { // if that is autoplay // if that mouse is not on container( only mouse-enable is true, the state maybe true) // if now is grabbing, skip this restart, using grabbing change restart if (this.autoplay() && !this.mouseOnContainer && !this.grabbing) { this._zone.runOutsideAngular(() => { this.restart.next(null); }); } } drawView(index, isAnimation = true, isFromAuto = this.isFromAuto) { // move element only on length is more than 1 if (this.elms.length > 1) { this.removeContainerTransition(); this.left = -(index * this.elmWidth - this.alignDistance); if (isAnimation) { if (isFromAuto) { this._renderer.addClass(this.containerElm, this.aniClassAuto()); } else { this._renderer.addClass(this.containerElm, this.aniClass()); } // if infinite move to next index with timeout this.infiniteHandler(index); } } else { this.left = this.alignDistance; } } removeContainerTransition() { this._renderer.removeClass(this.containerElm, this.aniClass()); this._renderer.removeClass(this.containerElm, this.aniClassAuto()); } infiniteHandler(index) { if (this.runLoop()) { let state = 0; state = index < 0 ? -1 : state; state = index > this.itemElms().length - 1 ? 1 : state; // index = index % this._showNum; if (state !== 0) { switch (state) { case -1: this._currentIndex = (this.itemElms().length + index) % this.itemElms().length; break; case 1: this._currentIndex = index % this.itemElms().length; break; } const isFromAuto = this.isFromAuto; if (this.saveTimeOut$) { this.saveTimeOut$.unsubscribe(); } this.saveTimeOut$ = timer(this.aniTime()) .pipe(switchMap(() => { // if it is any loop carousel, the next event need wait the timeout complete if (this.aniTime() === this.speed()) { this.removeContainerTransition(); this.left = -((this._currentIndex - state) * this.elmWidth) + this.alignDistance; return timer(50).pipe(tap(() => { this.drawView(this.currentIndex, this.hasInitWriteValue, isFromAuto); })); } else { this.drawView(this.currentIndex, false); } return of(null); }), takeUntil(this.stopEvent)) .subscribe(); } } } outOfBound(type) { switch (type) { case 'panleft': return this.currentIndex >= this.maxRightIndex; case 'panright': return this.currentIndex <= 0; } } runProgress(betweenTime) { return this._zone.runOutsideAngular(() => { const speed = this.speed(); const howTimes = speed / betweenTime; const everyIncrease = (100 / speed) * betweenTime; return interval(betweenTime).pipe(tap((t) => { this.progressWidth = (t % howTimes) * everyIncrease; }), bufferCount(howTimes)); }); } getAutoNum() { const currWidth = this.rootElmWidth; // check user has had set breakpoint if (this.breakpoint().length > 0) { // get the last biggest point const now = this.breakpoint().find((b) => { return b.width >= currWidth; }); // if find value, it is current width if (now) { return now.number; } return this.breakpoint()[this.breakpoint().length - 1].number; } // system init show number const initNum = 3; // 610 if (currWidth > 300) { return Math.floor(initNum + currWidth / 200); } return initNum; } addStyle(elm, style) { if (style) { Object.keys(style).forEach((key) => { const value = style[key]; this._renderer.setStyle(elm, key, value); }); } } setStyle(elm, style, value) { if (isPlatformBrowser(this.platformId)) { this._renderer.setStyle(elm, style, `${value}px`); } else { this._renderer.setStyle(elm, style, `${value}%`); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NgxHmCarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.3", type: NgxHmCarouselComponent, isStandalone: true, selector: "ngx-hm-carousel", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, aniTime: { classPropertyName: "aniTime", publicName: "aniTime", isSignal: true, isRequired: false, transformFunction: null }, aniClass: { classPropertyName: "aniClass", publicName: "aniClass", isSignal: true, isRequired: false, transformFunction: null }, aniClassAuto: { classPropertyName: "aniClassAuto", publicName: "aniClassAuto", isSignal: true, isRequired: false, transformFunction: null }, panBoundary: { classPropertyName: "panBoundary", publicName: "pan-boundary", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, notDrag: { classPropertyName: "notDrag", publicName: "not-follow-pan", isSignal: true, isRequired: false, transformFunction: null }, mouseEnable: { classPropertyName: "mouseEnable", publicName: "mouse-enable", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "between-delay", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "autoplay-direction", isSignal: true, isRequired: false, transformFunction: null }, scrollNum: { classPropertyName: "scrollNum", publicName: "scroll-num", isSignal: true, isRequired: false, transformFunction: null }, isDragMany: { classPropertyName: "isDragMany", publicName: "drag-many", isSignal: true, isRequired: false, transformFunction: null }, swipeVelocity: { classPropertyName: "swipeVelocity", publicName: "swipe-velocity", isSignal: true, isRequired: false, transformFunction: null }, breakpoint: { classPropertyName: "breakpoint", publicName: "breakpoint", isSignal: true, isRequired: false, transformFunction: null }, disableDrag: { classPropertyName: "disableDrag", publicName: "disable-drag", isSignal: true, isRequired: false, transformFunction: null }, infinite: { classPropertyName: "infinite", publicName: "infinite", isSignal: true, isRequired: false, transformFunction: null }, speed: { classPropertyName: "speed", publicName: "autoplay-speed", isSignal: true, isRequired: false, transformFunction: null }, recognizers: { classPropertyName: "recognizers", publicName: "recognizers", isSignal: true, isRequired: false, transformFunction: null }, stopPanListener: { classPropertyName: "stopPanListener", publicName: "stopPanListener", isSignal: true, isRequired: false, transformFunction: null }, showNum: { classPropertyName: "showNum", publicName: "show-num", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null } }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxHmCarouselComponent), multi: true, }, ], queries: [{ propertyName: "itemElms", predicate: NgxHmCarouselItemDirective, descendants: true, read: ElementRef, isSignal: true }, { propertyName: "contentPrev", first: true, predicate: ["carouselPrev"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "contentNext", first: true, predicate: ["carouselNext"], descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "dotElm", first: true, predicate: ["carouselDot"], descendants: true, isSignal: true }, { propertyName: "progressElm", first: true, predicate: ["carouselProgress"], descendants: true, isSignal: true }, { propertyName: "infiniteContainer", first: true, predicate: ["infiniteContainer"], descendants: true, read: ViewContainerRef, isSignal: true }, { propertyName: "contentContent", first: true, predicate: ["carouselContent"], descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "container", first: true, predicate: ["containerElm"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "btnPrev", first: true, predicate: ["prev"], descendants: true, isSignal: true }, { propertyName: "btnNext", first: true, predicate: ["next"], descendants: true, isSignal: true }, { propertyName: "progressContainerElm", first: true, predicate: ["progress"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div #containerElm class=\"carousel\">\n <!-- main content -->\n <ng-content select=\"[ngx-hm-carousel-container]\" />\n <!-- left -->\n @if (contentPrev(); as prevVar) {\n <div #prev class=\"direction left\">\n <ng-container *ngTemplateOutlet=\"prevVar\" />\n </div>\n }\n <!-- right -->\n @if (contentNext(); as nextVar) {\n <div #next class=\"direction right\">\n <ng-container *ngTemplateOutlet=\"nextVar\" />\n </div>\n }\n <!-- indicators -->\n @if (dotElm(); as dotVar) {\n <ul class=\"indicators\">\n @for (dot of itemElms(); track dot; let i = $index) {\n <li (click)=\"currentIndex = i\">\n <ng-container\n *ngTemplateOutlet=\"\n dotVar;\n context: {\n $implicit: {\n index: i,\n currentIndex: currentIndex\n }\n }\n \"\n />\n </li>\n }\n </ul>\n }\n <!-- progress -->\n @if (autoplay() && progressElm(); as progressVar) {\n <div #progress>\n <ng-container *ngTemplateOutlet=\"progressVar\" />\n </div>\n }\n\n @if (grabbing) {\n <div class=\"mask\">\n @if (leaveObs$ | async) {}\n </div>\n }\n</div>\n", styles: [":host{display:block;height:100%}.ngx-hm-carousel-display-nowrap{display:flex!important;flex-wrap:nowrap!important;flex-direction:row!important;overflow:hidden!important}.carousel{overflow:hidden;position:relative;width:100%;height:100%}.carousel ul.indicators{list-style:none;bottom:1rem;left:0;margin:0;padding:0;position:absolute;text-align:center;width:100%}.carousel ul.indicators li{cursor:pointer;display:inline-block;position:relative;padding:.5rem}.carousel .direction{position:absolute;height:100%;cursor:pointer;display:flex;align-items:center;justify-content:center;top:0}.carousel .direction.left{left:0}.carousel .direction.right{position:absolute;right:0}.grab{cursor:grab}.grabbing{cursor:grabbing}.mask{position:absolute;inset:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.3", ngImport: i0, type: NgxHmCarouselComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-hm-carousel', providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxHmCarouselComponent), multi: true, }, ], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [NgTemplateOutlet, AsyncPipe], template: "<div #containerElm class=\"carousel\">\n <!-- main content -->\n <ng-content select=\"[ngx-hm-carousel-container]\" />\n <!-- left -->\n @if (contentPrev(); as prevVar) {\n <div #prev class=\"direction left\">\n <ng-container *ngTemplateOutlet=\"prevVar\" />\n </div>\n }\n <!-- right -->\n @if (contentNext(); as nextVar) {\n <div #next class=\"direction right\">\n <ng-container *ngTemplateOutlet=\"nextVar\" />\n </div>\n }\n <!-- indicators -->\n @if (dotElm(); as dotVar) {\n <ul class=\"indicators\">\n @for (dot of itemElms(); track dot; let i = $index) {\n <li (click)=\"currentIndex = i\">\n <ng-container\n *ngTemplateOutlet=\"\n dotVar;\n context: {\n $implicit: {\n index: i,\n currentIndex: currentIndex\n }\n }\n \"\n />\n </li>\n }\n </ul>\n }\n <!-- progress -->\n @if (autoplay() && progressElm(); as progressVar) {\n <div #progress>\n <ng-container *ngTemplateOutlet=\"progressVar\" />\n </div>\n }\n\n @if (grabbing) {\n <div class=\"mask\">\n @if (leaveObs$ | async) {}\n </div>\n }\n</div>\n", styles: [":host{display:block;height:100%}.ngx-hm-carousel-display-nowrap{display:flex!important;flex-wrap:nowrap!important;flex-direction:row!important;overflow:hidden!important}.carousel{overflow:hidden;position:relative;width:100%;height:100%}.carousel ul.indicators{list-style:none;bottom:1rem;left:0;margin:0;padding:0;position:absolute;text-align:center;width:100%}.carousel ul.indicators li{cursor:pointer;display:inline-block;position:relative;padding:.5rem}.carousel .direction{position:absolute;height:100%;cursor:pointer;display:flex;align-items:center;justify-content:center;top:0}.carousel .direction.left{left:0}.carousel .direction.right{position:absolute;right:0}.grab{cursor:grab}.grabbing{cursor:grabbing}.mask{position:absolute;inset:0}\n"] }] }], ctorParameters: () => [] }); /** * Generated bundle index. Do not edit. */ export { NgxHmCarouselComponent, NgxHmCarouselDynamicDirective, NgxHmCarouselItemDirective }; //# sourceMappingURL=ngx-hm-carousel.mjs.map