UNPKG

@pi0/framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

594 lines (547 loc) 18.6 kB
/* eslint indent: ["off"] */ import $ from 'dom7'; import Utils from '../../utils/utils'; import Framework7Class from '../../utils/class'; class PhotoBrowser extends Framework7Class { constructor(app, params = {}) { super(params, [app]); const pb = this; pb.app = app; const defaults = Utils.extend({ on: {}, }, app.modules.photoBrowser.params.photoBrowser); // Extend defaults with modules params pb.useModulesParams(defaults); pb.params = Utils.extend(defaults, params); Utils.extend(pb, { exposed: false, opened: false, activeIndex: pb.params.swiper.initialSlide, url: pb.params.url, view: pb.params.view || app.views.main, swipeToClose: { allow: true, isTouched: false, diff: undefined, start: undefined, current: undefined, started: false, activeSlide: undefined, timeStart: undefined, }, }); // Install Modules pb.useModules(); // Init pb.init(); } onTransitionStart(swiper) { const pb = this; pb.activeIndex = swiper.activeIndex; let current = swiper.activeIndex + 1; let total = swiper.slides.length; if (swiper.params.loop) { total -= 2; current -= swiper.loopedSlides; if (current < 1) current = total + current; if (current > total) current -= total; } let $currentEl = pb.$containerEl.find('.photo-browser-current'); let $totalEl = pb.$containerEl.find('.photo-browser-total'); if (pb.params.type === 'page' && pb.params.navbar && $currentEl.length === 0 && pb.app.theme === 'ios') { const navbarEl = pb.app.navbar.getElByPage(pb.$containerEl); if (navbarEl) { $currentEl = $(navbarEl).find('.photo-browser-current'); $totalEl = $(navbarEl).find('.photo-browser-total'); } } $currentEl.text(current); $totalEl.text(total); // Update captions if (pb.captions.length > 0) { const captionIndex = swiper.params.loop ? swiper.slides.eq(swiper.activeIndex).attr('data-swiper-slide-index') : pb.activeIndex; pb.$captionsContainerEl.find('.photo-browser-caption-active').removeClass('photo-browser-caption-active'); pb.$captionsContainerEl.find(`[data-caption-index="${captionIndex}"]`).addClass('photo-browser-caption-active'); } // Stop Video const previousSlideVideo = swiper.slides.eq(swiper.previousIndex).find('video'); if (previousSlideVideo.length > 0) { if ('pause' in previousSlideVideo[0]) previousSlideVideo[0].pause(); } } onTouchStart() { const pb = this; const swipeToClose = pb.swipeToClose; if (!swipeToClose.allow) return; swipeToClose.isTouched = true; } onTouchMove(e) { const pb = this; const swipeToClose = pb.swipeToClose; if (!swipeToClose.isTouched) return; if (!swipeToClose.started) { swipeToClose.started = true; swipeToClose.start = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; swipeToClose.activeSlide = pb.swiper.slides.eq(pb.swiper.activeIndex); swipeToClose.timeStart = Utils.now(); } e.preventDefault(); swipeToClose.current = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; swipeToClose.diff = swipeToClose.start - swipeToClose.current; const opacity = 1 - (Math.abs(swipeToClose.diff) / 300); const color = pb.exposed || pb.params.theme === 'dark' ? 0 : 255; swipeToClose.activeSlide.transform(`translate3d(0,${-swipeToClose.diff}px,0)`); pb.swiper.$el.css('background-color', `rgba(${color}, ${color}, ${color}, ${opacity})`).transition(0); } onTouchEnd() { const pb = this; const swipeToClose = pb.swipeToClose; swipeToClose.isTouched = false; if (!swipeToClose.started) { swipeToClose.started = false; return; } swipeToClose.started = false; swipeToClose.allow = false; const diff = Math.abs(swipeToClose.diff); const timeDiff = (new Date()).getTime() - swipeToClose.timeStart; if ((timeDiff < 300 && diff > 20) || (timeDiff >= 300 && diff > 100)) { Utils.nextTick(() => { if (pb.$containerEl) { if (swipeToClose.diff < 0) pb.$containerEl.addClass('swipe-close-to-bottom'); else pb.$containerEl.addClass('swipe-close-to-top'); } pb.emit('local::swipeToClose', pb); pb.close(); swipeToClose.allow = true; }); return; } if (diff !== 0) { swipeToClose.activeSlide.addClass('photo-browser-transitioning').transitionEnd(() => { swipeToClose.allow = true; swipeToClose.activeSlide.removeClass('photo-browser-transitioning'); }); } else { swipeToClose.allow = true; } pb.swiper.$el.transition('').css('background-color', ''); swipeToClose.activeSlide.transform(''); } // Render Functions renderNavbar() { const pb = this; if (pb.params.renderNavbar) return pb.params.renderNavbar.call(pb); let iconsColor = pb.params.iconsColor; if (!pb.params.iconsColor && pb.params.theme === 'dark') iconsColor = 'white'; const backLinkText = pb.app.theme === 'ios' && pb.params.backLinkText ? pb.params.backLinkText : ''; const isPopup = pb.params.type !== 'page'; const navbarHtml = ` <div class="navbar"> <div class="navbar-inner sliding"> <div class="left"> <a href="#" class="link ${isPopup ? 'popup-close' : ''} ${!backLinkText ? 'icon-only' : ''} ${!isPopup ? 'back' : ''}" ${isPopup ? 'data-popup=".photo-browser-popup"' : ''}> <i class="icon icon-back ${iconsColor ? `color-${iconsColor}` : ''}"></i> ${backLinkText ? `<span>${backLinkText}</span>` : ''} </a> </div> <div class="title"> <span class="photo-browser-current"></span> <span class="photo-browser-of">${pb.params.navbarOfText}</span> <span class="photo-browser-total"></span> </div> <div class="right"></div> </div> </div> `.trim(); return navbarHtml; } renderToolbar() { const pb = this; if (pb.params.renderToolbar) return pb.params.renderToolbar.call(pb); let iconsColor = pb.params.iconsColor; if (!pb.params.iconsColor && pb.params.theme === 'dark') iconsColor = 'white'; const toolbarHtml = ` <div class="toolbar tabbar toolbar-bottom-md"> <div class="toolbar-inner"> <a href="#" class="link photo-browser-prev"> <i class="icon icon-back ${iconsColor ? `color-${iconsColor}` : ''}"></i> </a> <a href="#" class="link photo-browser-next"> <i class="icon icon-forward ${iconsColor ? `color-${iconsColor}` : ''}"></i> </a> </div> </div> `.trim(); return toolbarHtml; } renderCaption(caption, index) { const pb = this; if (pb.params.renderCaption) return pb.params.renderCaption.call(pb, caption, index); const captionHtml = ` <div class="photo-browser-caption" data-caption-index="${index}"> ${caption} </div> `.trim(); return captionHtml; } renderObject(photo, index) { const pb = this; if (pb.params.renderObject) return pb.params.renderObject.call(pb, photo, index); const objHtml = ` <div class="photo-browser-slide photo-browser-object-slide swiper-slide">${photo.html ? photo.html : photo}</div> `; return objHtml; } renderLazyPhoto(photo, index) { const pb = this; if (pb.params.renderLazyPhoto) return pb.params.renderLazyPhoto.call(pb, photo, index); const photoHtml = ` <div class="photo-browser-slide photo-browser-slide-lazy swiper-slide"> <div class="preloader swiper-lazy-preloader ${pb.params.theme === 'dark' ? 'color-white' : ''}"></div> <span class="swiper-zoom-container"> <img data-src="${photo.url ? photo.url : photo}" class="swiper-lazy"> </span> </div> `.trim(); return photoHtml; } renderPhoto(photo, index) { const pb = this; if (pb.params.renderPhoto) return pb.params.renderPhoto.call(pb, photo, index); const photoHtml = ` <div class="photo-browser-slide swiper-slide"> <span class="swiper-zoom-container"> <img src="${photo.url ? photo.url : photo}"> </span> </div> `.trim(); return photoHtml; } render() { const pb = this; if (pb.params.render) return pb.params.render.call(pb, pb.params); const html = ` <div class="photo-browser photo-browser-${pb.params.theme}"> <div class="view"> <div class="page photo-browser-page photo-browser-page-${pb.params.theme} no-toolbar ${!pb.params.navbar ? 'no-navbar' : ''}" data-name="photo-browser-page"> ${pb.params.navbar ? pb.renderNavbar() : ''} ${pb.params.toolbar ? pb.renderToolbar() : ''} <div class="photo-browser-captions photo-browser-captions-${pb.params.captionsTheme || pb.params.theme}"> ${pb.params.photos.filter(photo => photo.caption).map((photo, index) => pb.renderCaption(photo.caption, index)).join(' ')} </div> <div class="photo-browser-swiper-container swiper-container"> <div class="photo-browser-swiper-wrapper swiper-wrapper"> ${pb.params.photos.map((photo, index) => { if (photo.html || ((typeof photo === 'string' || photo instanceof String) && photo.indexOf('<') >= 0 && photo.indexOf('>') >= 0)) { return pb.renderObject(photo, index); } else if (pb.params.swiper.lazy && pb.params.swiper.lazy.enabled) { return pb.renderLazyPhoto(photo, index); } return pb.renderPhoto(photo, index); }).join(' ')} </div> </div> </div> </div> </div> `.trim(); return html; } renderStandalone() { const pb = this; if (pb.params.renderStandalone) return pb.params.renderStandalone.call(pb); const standaloneHtml = `<div class="popup photo-browser-popup photo-browser-standalone popup-tablet-fullscreen">${pb.render()}</div>`; return standaloneHtml; } renderPage() { const pb = this; if (pb.params.renderPage) return pb.params.renderPage.call(pb); const pageHtml = pb.render(); return pageHtml; } renderPopup() { const pb = this; if (pb.params.renderPopup) return pb.params.renderPopup.call(pb); const popupHtml = `<div class="popup photo-browser-popup">${pb.render()}</div>`; return popupHtml; } // Callbacks onOpen(type, containerEl) { const pb = this; const app = pb.app; const $containerEl = $(containerEl); $containerEl[0].f7PhotoBrowser = pb; pb.$containerEl = $containerEl; pb.openedIn = type; pb.opened = true; pb.$swiperContainerEl = pb.$containerEl.find('.photo-browser-swiper-container'); pb.$swiperWrapperEl = pb.$containerEl.find('.photo-browser-swiper-wrapper'); pb.slides = pb.$containerEl.find('.photo-browser-slide'); pb.$captionsContainerEl = pb.$containerEl.find('.photo-browser-captions'); pb.captions = pb.$containerEl.find('.photo-browser-caption'); // Init Swiper const swiperSettings = Utils.extend({}, pb.params.swiper, { initialSlide: pb.activeIndex, on: { tap(e) { pb.emit('local::tap', e); }, click(e) { if (pb.params.exposition) { pb.expositionToggle(); } pb.emit('local::click', e); }, doubleTap(e) { pb.emit('local::doubleTap', e); }, transitionStart() { const swiper = this; pb.onTransitionStart(swiper); pb.emit('local::transitionStart', swiper); }, transitionEnd() { const swiper = this; pb.emit('local::transitionEnd', swiper); }, slideChangeStart() { const swiper = this; pb.emit('local::slideChangeStart', swiper); }, slideChangeEnd() { const swiper = this; pb.emit('local::slideChangeEnd', swiper); }, lazyImageLoad(slideEl, imgEl) { pb.emit('local::lazyImageLoad', slideEl, imgEl); }, lazyImageReady(slideEl, imgEl) { $(slideEl).removeClass('photo-browser-slide-lazy'); pb.emit('local::lazyImageReady', slideEl, imgEl); }, }, }); if (pb.params.swipeToClose && pb.params.type !== 'page') { Utils.extend(swiperSettings.on, { touchStart(e) { pb.onTouchStart(e); }, touchMoveOpposite(e) { pb.onTouchMove(e); }, touchEnd(e) { pb.onTouchEnd(e); }, }); } pb.swiper = app.swiper.create(pb.$swiperContainerEl, swiperSettings); if (pb.activeIndex === 0) { pb.onTransitionStart(pb.swiper); } pb.emit('local::open photoBrowserOpen', pb); } onOpened() { const pb = this; pb.emit('local::opened photoBrowserOpened', pb); } onClose() { const pb = this; if (pb.destroyed) return; // Destroy Swiper if (pb.swiper && pb.swiper.destroy) { pb.swiper.destroy(true, false); pb.swiper = null; delete pb.swiper; } pb.emit('local::close photoBrowserClose', pb); } onClosed() { const pb = this; if (pb.destroyed) return; pb.opened = false; pb.$containerEl = null; delete pb.$containerEl; pb.emit('local::closed photoBrowserClosed', pb); } // Open openPage() { const pb = this; if (pb.opened) return pb; const pageHtml = pb.renderPage(); pb.view.router.navigate(pb.url, { createRoute: { content: pageHtml, path: pb.url, options: { pageEvents: { pageBeforeIn(e, page) { pb.view.$el.addClass(`with-photo-browser-page with-photo-browser-page-${pb.params.theme}`); pb.onOpen('page', page.el); }, pageAfterIn(e, page) { pb.onOpened('page', page.el); }, pageBeforeOut(e, page) { pb.view.$el.removeClass(`with-photo-browser-page with-photo-browser-page-exposed with-photo-browser-page-${pb.params.theme}`); pb.onClose('page', page.el); }, pageAfterOut(e, page) { pb.onClosed('page', page.el); }, }, }, }, }); return pb; } openStandalone() { const pb = this; if (pb.opened) return pb; const standaloneHtml = pb.renderStandalone(); const popupParams = { backdrop: false, content: standaloneHtml, on: { popupOpen(popup) { pb.onOpen('popup', popup.el); }, popupOpened(popup) { pb.onOpened('popup', popup.el); }, popupClose(popup) { pb.onClose('popup', popup.el); }, popupClosed(popup) { pb.onClosed('popup', popup.el); }, }, }; if (pb.params.routableModals) { pb.view.router.navigate(pb.url, { createRoute: { path: pb.url, popup: popupParams, }, }); } else { pb.modal = pb.app.popup.create(popupParams).open(); } return pb; } openPopup() { const pb = this; if (pb.opened) return pb; const popupHtml = pb.renderPopup(); const popupParams = { content: popupHtml, on: { popupOpen(popup) { pb.onOpen('popup', popup.el); }, popupOpened(popup) { pb.onOpened('popup', popup.el); }, popupClose(popup) { pb.onClose('popup', popup.el); }, popupClosed(popup) { pb.onClosed('popup', popup.el); }, }, }; if (pb.params.routableModals) { pb.view.router.navigate(pb.url, { createRoute: { path: pb.url, popup: popupParams, }, }); } else { pb.modal = pb.app.popup.create(popupParams).open(); } return pb; } // Exposition expositionEnable() { const pb = this; if (pb.params.type === 'page') { pb.view.$el.addClass('with-photo-browser-page-exposed'); } if (pb.$containerEl) pb.$containerEl.addClass('photo-browser-exposed'); if (pb.params.expositionHideCaptions) pb.$captionsContainerEl.addClass('photo-browser-captions-exposed'); pb.exposed = true; return pb; } expositionDisable() { const pb = this; if (pb.params.type === 'page') { pb.view.$el.removeClass('with-photo-browser-page-exposed'); } if (pb.$containerEl) pb.$containerEl.removeClass('photo-browser-exposed'); if (pb.params.expositionHideCaptions) pb.$captionsContainerEl.removeClass('photo-browser-captions-exposed'); pb.exposed = false; return pb; } expositionToggle() { const pb = this; if (pb.params.type === 'page') { pb.view.$el.toggleClass('with-photo-browser-page-exposed'); } if (pb.$containerEl) pb.$containerEl.toggleClass('photo-browser-exposed'); if (pb.params.expositionHideCaptions) pb.$captionsContainerEl.toggleClass('photo-browser-captions-exposed'); pb.exposed = !pb.exposed; return pb; } open(index) { const pb = this; const type = pb.params.type; if (pb.opened) { if (pb.swiper && typeof index !== 'undefined') { pb.swiper.slideTo(parseInt(index, 10)); } return pb; } else if (typeof index !== 'undefined') { pb.activeIndex = index; } if (type === 'standalone') { pb.openStandalone(); } if (type === 'page') { pb.openPage(); } if (type === 'popup') { pb.openPopup(); } return pb; } close() { const pb = this; if (!pb.opened) return pb; if (pb.params.routableModals || pb.openedIn === 'page') { if (pb.view) pb.view.router.back(); } else { pb.modal.once('modalClosed', () => { Utils.nextTick(() => { pb.modal.destroy(); delete pb.modal; }); }); pb.modal.close(); } return pb; } // eslint-disable-next-line init() {} destroy() { let pb = this; pb.emit('local::beforeDestroy photoBrowserBeforeDestroy', pb); if (pb.$containerEl) { pb.$containerEl.trigger('photobrowser:beforedestroy'); delete pb.$containerEl[0].f7PhotoBrowser; } Utils.deleteProps(pb); pb = null; } } export default PhotoBrowser;