UNPKG

@herdwatch/ngx-ionic-image-viewer

Version:

The angular workspace to develop the ngx-ionic-image-viewer component

403 lines (394 loc) 22.4 kB
import * as i0 from '@angular/core'; import { Injectable, Component, CUSTOM_ELEMENTS_SCHEMA, Input, ViewChild, ViewEncapsulation, Directive, HostListener, makeEnvironmentProviders } from '@angular/core'; import * as i1 from '@ionic/angular/standalone'; import { IonHeader, IonToolbar, IonButton, IonButtons, IonIcon, IonTitle, IonContent, IonFooter, IonText } from '@ionic/angular/standalone'; import { NgClass } from '@angular/common'; import { addIcons } from 'ionicons'; import { close } from 'ionicons/icons'; import { register } from 'swiper/element/bundle'; class NgxIonicImageViewerService { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerService, decorators: [{ type: Injectable }] }); class ViewerModalComponent { // @ViewChild('sliderRef', { static: true }) slides!: IonSlides; constructor(modalController) { this.modalController = modalController; this.alt = ''; this.scheme = 'auto'; this.slideOptions = {}; this.srcFallback = ''; this.srcHighRes = ''; this.swipeToClose = true; this.text = ''; this.title = ''; this.titleSize = ''; this.defaultSlideOptions = { zoom: { enabled: true, }, }; this.options = {}; this.swipeState = { phase: 'init', direction: 'none', swipeType: 'none', startX: 0, startY: 0, distance: 0, distanceX: 0, distanceY: 0, threshold: 150, // required min distance traveled to be considered swipe restraint: 100, // maximum distance allowed at the same time in perpendicular direction allowedTime: 500, // maximum time allowed to travel that distance elapsedTime: 0, startTime: 0, }; addIcons({ close }); } async ngOnInit() { this.options = { ...this.defaultSlideOptions, ...this.slideOptions }; this.src = this.srcHighRes || this.src; this.setStyle(); this.setScheme(this.scheme); this.initSwipeToClose(this.swipeToClose); /** * Current Workaround * See reported bug: https://github.com/ionic-team/ionic/issues/19638#issuecomment-584828315 * Hint: Comment in '<ion-slide>' in component */ } setStyle() { const el = document.querySelector('.ion-img-viewer'); el?.style.setProperty('--height', '100%'); el?.style.setProperty('--width', '100%'); el?.style.setProperty('--border-radius', '0'); } setScheme(scheme) { if (scheme && scheme === 'auto') { return; } const el = document.querySelector('.ion-img-viewer'); if (this.scheme === 'light') { el?.style.setProperty('--ion-background-color', '#ffffff'); el?.style.setProperty('--ion-background-color-rgb', '255, 255, 255'); el?.style.setProperty('--ion-text-color', '#000'); el?.style.setProperty('--ion-text-color-rgb', '0,0,0'); } if (this.scheme === 'dark') { if (el?.classList.contains('ios')) { el?.style.setProperty('--ion-background-color', '#000000'); el?.style.setProperty('--ion-background-color-rgb', '0, 0, 0'); } else { el?.style.setProperty('--ion-background-color', '#121212'); el?.style.setProperty('--ion-background-color-rgb', '18,18,18'); } el?.style.setProperty('--ion-text-color', '#ffffff'); el?.style.setProperty('--ion-text-color-rgb', '255,255,255'); } } /** * @see http://www.javascriptkit.com/javatutors/touchevents3.shtml */ initSwipeToClose(isActive = true) { if (!isActive) { return; } const el = document.querySelector('ion-modal'); el?.addEventListener('mousedown', (event) => this.swipeStart(event), true); el?.addEventListener('mousemove', (event) => this.swipeMove(event), true); el?.addEventListener('mouseup', () => this.swipeEnd(), true); el?.addEventListener('touchstart', (event) => this.swipeStart(event), true); el?.addEventListener('touchmove', (event) => this.swipeMove(event), true); el?.addEventListener('touchend', () => this.swipeEnd(), true); this.modalController.getTop().then((modal) => { modal?.onWillDismiss().then(() => { document.removeEventListener('mousedown', this.swipeStart, true); document.removeEventListener('mousemove', this.swipeMove, true); document.removeEventListener('mouseup', this.swipeMove, true); document.removeEventListener('touchstart', this.swipeStart, true); document.removeEventListener('touchmove', this.swipeMove, true); document.removeEventListener('touchend', this.swipeMove, true); }); }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any swipeStart(event) { const { pageX, pageY } = event.type === 'touchstart' && event?.changedTouches ? event.changedTouches[0] : event; this.swipeState = { ...this.swipeState, phase: 'start', direction: 'none', distance: 0, startX: pageX, startY: pageY, startTime: new Date().getTime(), }; } // eslint-disable-next-line @typescript-eslint/no-explicit-any swipeMove(event) { const { pageX, pageY } = event.type === 'touchmove' && event?.changedTouches ? event.changedTouches[0] : event; // get horizontal dist traveled by finger while in contact with surface const distanceX = pageX - this.swipeState.startX; // get vertical dist traveled by finger while in contact with surface const distanceY = pageY - this.swipeState.startY; let direction; let distance; if (Math.abs(distanceX) > Math.abs(distanceY)) { // if distance traveled horizontally is greater than vertically, consider this a horizontal swipe direction = distanceX < 0 ? 'left' : 'right'; distance = distanceX; } else { // else consider this a vertical swipe direction = distanceY < 0 ? 'up' : 'down'; distance = distanceY; } this.swipeState = { ...this.swipeState, phase: 'move', direction, distance, distanceX, distanceY, }; if (event.cancelable) { event.preventDefault(); } } swipeEnd() { if (this.swipeState.phase === 'none') { return; } const { allowedTime, direction, restraint, startTime, threshold, distanceX, distanceY, } = this.swipeState; let swipeType = null; const elapsedTime = new Date().getTime() - startTime; // get time elapsed if (elapsedTime <= allowedTime) { // first condition for a swipe met if (Math.abs(distanceX) >= threshold && Math.abs(distanceY) <= restraint) { // 2nd condition for horizontal swipe met swipeType = direction; // set swipeType to either "left" or "right" } else if (Math.abs(distanceY) >= threshold && Math.abs(distanceX) <= restraint) { // 2nd condition for vertical swipe met swipeType = direction; // set swipeType to either "top" or "down" } } this.swipeState = { ...this.swipeState, phase: 'end', swipeType: swipeType ?? '', }; if (swipeType === 'down') { return this.closeModal(); } } closeModal() { this.modalController.dismiss(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: ViewerModalComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.10", type: ViewerModalComponent, isStandalone: true, selector: "ion-viewer-modal", inputs: { alt: "alt", scheme: "scheme", slideOptions: "slideOptions", src: "src", srcFallback: "srcFallback", srcHighRes: "srcHighRes", swipeToClose: "swipeToClose", text: "text", title: "title", titleSize: "titleSize" }, viewQueries: [{ propertyName: "swiperRef", first: true, predicate: ["swiper"], descendants: true }], ngImport: i0, template: "<ion-header [ngClass]=\"{ 'no-title': title && title.length <= 0 }\">\n <ion-toolbar>\n <ion-buttons slot=\"primary\">\n <ion-button data-cy=\"closeModal\" (click)=\"closeModal()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title [size]=\"titleSize\">{{ title }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content [forceOverscroll]=\"false\">\n <swiper-container [options]=\"options\" centeredSlides=\"true\" passiveListeners=\"false\" zoom=\"true\" #swiper>\n <swiper-slide>\n <div class=\"swiper-zoom-container\">\n <img [alt]=\"this.alt\" [src]=\"this.src\"/>\n </div>\n </swiper-slide>\n </swiper-container>\n</ion-content>\n\n<ion-footer [ngClass]=\"{ 'no-text': text && text.length <= 0 }\">\n <ion-toolbar class=\"ion-text-center\">\n <ion-text>{{ text }}</ion-text>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-slides{--height: 100%;height:100%}ion-toolbar{--border-style: none;--background: rgba(var(--ion-background-color-rgb, (255, 255, 255)), .6);background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.6)}ion-header{opacity:1;position:absolute}ion-header.no-title:after{content:none}ion-header.no-title ion-toolbar{--background: rgba(0, 0, 0, 0);background:#0000}ion-footer{position:absolute;bottom:0}ion-footer.no-text:before{content:none}ion-footer.no-text ion-toolbar{--background: rgba(0, 0, 0, 0);background:#0000}swiper-container{width:100%;height:100%}swiper-slide{overflow:hidden}\n"], dependencies: [{ kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: ViewerModalComponent, decorators: [{ type: Component, args: [{ selector: 'ion-viewer-modal', standalone: true, schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [ IonHeader, IonToolbar, IonButton, NgClass, IonButtons, IonIcon, IonTitle, IonContent, IonFooter, IonText, ], template: "<ion-header [ngClass]=\"{ 'no-title': title && title.length <= 0 }\">\n <ion-toolbar>\n <ion-buttons slot=\"primary\">\n <ion-button data-cy=\"closeModal\" (click)=\"closeModal()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title [size]=\"titleSize\">{{ title }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content [forceOverscroll]=\"false\">\n <swiper-container [options]=\"options\" centeredSlides=\"true\" passiveListeners=\"false\" zoom=\"true\" #swiper>\n <swiper-slide>\n <div class=\"swiper-zoom-container\">\n <img [alt]=\"this.alt\" [src]=\"this.src\"/>\n </div>\n </swiper-slide>\n </swiper-container>\n</ion-content>\n\n<ion-footer [ngClass]=\"{ 'no-text': text && text.length <= 0 }\">\n <ion-toolbar class=\"ion-text-center\">\n <ion-text>{{ text }}</ion-text>\n </ion-toolbar>\n</ion-footer>\n", styles: ["ion-slides{--height: 100%;height:100%}ion-toolbar{--border-style: none;--background: rgba(var(--ion-background-color-rgb, (255, 255, 255)), .6);background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.6)}ion-header{opacity:1;position:absolute}ion-header.no-title:after{content:none}ion-header.no-title ion-toolbar{--background: rgba(0, 0, 0, 0);background:#0000}ion-footer{position:absolute;bottom:0}ion-footer.no-text:before{content:none}ion-footer.no-text ion-toolbar{--background: rgba(0, 0, 0, 0);background:#0000}swiper-container{width:100%;height:100%}swiper-slide{overflow:hidden}\n"] }] }], ctorParameters: () => [{ type: i1.ModalController }], propDecorators: { alt: [{ type: Input }], scheme: [{ type: Input }], slideOptions: [{ type: Input }], src: [{ type: Input }], srcFallback: [{ type: Input }], srcHighRes: [{ type: Input }], swipeToClose: [{ type: Input }], text: [{ type: Input }], title: [{ type: Input }], titleSize: [{ type: Input }], swiperRef: [{ type: ViewChild, args: ['swiper'] }] } }); register(); class NgxIonicImageViewerComponent { constructor(modalController) { this.modalController = modalController; } async viewImage(src, srcFallback = '', srcHighRes = '', title = '', titleSize = '', text = '', scheme = 'auto', slideOptions = {}, swipeToClose = true) { const modal = await this.modalController.create({ component: ViewerModalComponent, componentProps: { src, srcFallback, srcHighRes, title, titleSize, text, scheme, slideOptions, swipeToClose }, cssClass: this.cssClass && this.cssClass instanceof Array ? ['ion-img-viewer', ...this.cssClass] : ['ion-img-viewer', this.cssClass], keyboardClose: true, showBackdrop: true }); return await modal.present(); } ngOnInit() { /* do nothing */ } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.10", type: NgxIonicImageViewerComponent, isStandalone: true, selector: "ion-img-viewer", inputs: { alt: "alt", cssClass: "cssClass", scheme: "scheme", slideOptions: "slideOptions", src: "src", srcFallback: "srcFallback", srcHighRes: "srcHighRes", swipeToClose: "swipeToClose", text: "text", title: "title", titleSize: "titleSize" }, ngImport: i0, template: "<img\n [alt]=\"alt\"\n [src]=\"src\"\n (click)=\"viewImage(src, srcFallback, srcHighRes, title, titleSize, text, scheme, slideOptions, swipeToClose)\"\n (error)=\"src = srcFallback\"\n/>\n", styles: [":host{display:block}\n"] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerComponent, decorators: [{ type: Component, args: [{ selector: 'ion-img-viewer', encapsulation: ViewEncapsulation.Emulated, standalone: true, template: "<img\n [alt]=\"alt\"\n [src]=\"src\"\n (click)=\"viewImage(src, srcFallback, srcHighRes, title, titleSize, text, scheme, slideOptions, swipeToClose)\"\n (error)=\"src = srcFallback\"\n/>\n", styles: [":host{display:block}\n"] }] }], ctorParameters: () => [{ type: i1.ModalController }], propDecorators: { alt: [{ type: Input }], cssClass: [{ type: Input }], scheme: [{ type: Input }], slideOptions: [{ type: Input }], src: [{ type: Input }], srcFallback: [{ type: Input }], srcHighRes: [{ type: Input }], swipeToClose: [{ type: Input }], text: [{ type: Input }], title: [{ type: Input }], titleSize: [{ type: Input }] } }); class NgxIonicImageViewerDirective { constructor(el, renderer, modalController) { this.el = el; this.renderer = renderer; this.modalController = modalController; } onClick() { this.viewImage(this.src, this.srcFallback, this.srcHighRes, this.title, this.titleSize, this.text, this.scheme, this.slideOptions, this.swipeToClose); } onError() { if (this.src !== this.el.nativeElement.src) { this.src = this.el.nativeElement.src; } if (this.srcFallback) { this.renderer.setAttribute(this.el.nativeElement, 'src', this.srcFallback); } } ngOnInit() { if (!this.el.nativeElement.hasAttribute('src')) { this.renderer.setAttribute(this.el.nativeElement, 'src', this.src); } } async viewImage(src, srcFallback = '', srcHighRes = '', title = '', titleSize = '', text = '', scheme = 'auto', slideOptions = {}, swipeToClose = true) { const modal = await this.modalController.create({ component: ViewerModalComponent, componentProps: { src, srcFallback, srcHighRes, title, titleSize, text, scheme, slideOptions, swipeToClose }, cssClass: this.cssClass && this.cssClass instanceof Array ? ['ion-img-viewer', ...this.cssClass] : ['ion-img-viewer', this.cssClass], keyboardClose: true, showBackdrop: true }); return await modal.present(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.10", type: NgxIonicImageViewerDirective, isStandalone: true, selector: "[ionImgViewer]", inputs: { cssClass: "cssClass", scheme: "scheme", slideOptions: "slideOptions", src: "src", srcFallback: "srcFallback", srcHighRes: "srcHighRes", swipeToClose: "swipeToClose", text: "text", title: "title", titleSize: "titleSize" }, host: { listeners: { "click": "onClick()", "error": "onError($event)" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.10", ngImport: i0, type: NgxIonicImageViewerDirective, decorators: [{ type: Directive, args: [{ selector: '[ionImgViewer]', standalone: true, }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1.ModalController }], propDecorators: { cssClass: [{ type: Input }], scheme: [{ type: Input }], slideOptions: [{ type: Input }], src: [{ type: Input }], srcFallback: [{ type: Input }], srcHighRes: [{ type: Input }], swipeToClose: [{ type: Input }], text: [{ type: Input }], title: [{ type: Input }], titleSize: [{ type: Input }], onClick: [{ type: HostListener, args: ['click'] }], onError: [{ type: HostListener, args: ['error', ['$event']] }] } }); function provideNgxIonicImageViewer() { return [ NgxIonicImageViewerService ]; } function provideEnvironmentNgxIonicImageViewer() { return makeEnvironmentProviders(provideNgxIonicImageViewer()); } /* * Public API Surface of ngx-ionic-image-viewer */ /** * Generated bundle index. Do not edit. */ export { NgxIonicImageViewerComponent, NgxIonicImageViewerDirective, NgxIonicImageViewerService, ViewerModalComponent, provideEnvironmentNgxIonicImageViewer, provideNgxIonicImageViewer }; //# sourceMappingURL=herdwatch-ngx-ionic-image-viewer.mjs.map