UNPKG

@pi0/framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

372 lines (356 loc) 13.1 kB
import $ from 'dom7'; import Utils from '../../utils/utils'; const Navbar = { size(el) { const app = this; if (app.theme !== 'ios') return; let $el = $(el); if ($el.hasClass('navbar')) { $el = $el.children('.navbar-inner').each((index, navbarEl) => { app.navbar.size(navbarEl); }); return; } if ( $el.hasClass('stacked') || $el.parents('.stacked').length > 0 || $el.parents('.tab:not(.tab-active)').length > 0 || $el.parents('.popup:not(.modal-in)').length > 0 ) { return; } const $viewEl = $el.parents('.view').eq(0); const left = app.rtl ? $el.children('.right') : $el.children('.left'); const right = app.rtl ? $el.children('.left') : $el.children('.right'); const title = $el.children('.title'); const subnavbar = $el.children('.subnavbar'); const noLeft = left.length === 0; const noRight = right.length === 0; const leftWidth = noLeft ? 0 : left.outerWidth(true); const rightWidth = noRight ? 0 : right.outerWidth(true); const titleWidth = title.outerWidth(true); const navbarStyles = $el.styles(); const navbarWidth = $el[0].offsetWidth; const navbarInnerWidth = navbarWidth - parseInt(navbarStyles.paddingLeft, 10) - parseInt(navbarStyles.paddingRight, 10); const isPrevious = $el.hasClass('navbar-previous'); const sliding = $el.hasClass('sliding'); let router; let dynamicNavbar; let separateNavbar; let separateNavbarRightOffset = 0; let separateNavbarLeftOffset = 0; if ($viewEl.length > 0 && $viewEl[0].f7View) { router = $viewEl[0].f7View.router; dynamicNavbar = router && router.dynamicNavbar; separateNavbar = router && router.separateNavbar; if (!separateNavbar) { separateNavbarRightOffset = navbarWidth; separateNavbarLeftOffset = navbarWidth / 5; } } let currLeft; let diff; if (noRight) { currLeft = navbarInnerWidth - titleWidth; } if (noLeft) { currLeft = 0; } if (!noLeft && !noRight) { currLeft = ((navbarInnerWidth - rightWidth - titleWidth) + leftWidth) / 2; } let requiredLeft = (navbarInnerWidth - titleWidth) / 2; if (navbarInnerWidth - leftWidth - rightWidth > titleWidth) { if (requiredLeft < leftWidth) { requiredLeft = leftWidth; } if (requiredLeft + titleWidth > navbarInnerWidth - rightWidth) { requiredLeft = navbarInnerWidth - rightWidth - titleWidth; } diff = requiredLeft - currLeft; } else { diff = 0; } // RTL inverter const inverter = app.rtl ? -1 : 1; if (dynamicNavbar) { if (title.hasClass('sliding') || (title.length > 0 && sliding)) { let titleLeftOffset = (-(currLeft + diff) * inverter) + separateNavbarLeftOffset; const titleRightOffset = ((navbarInnerWidth - currLeft - diff - titleWidth) * inverter) - separateNavbarRightOffset; if (isPrevious) { if (router && router.params.iosAnimateNavbarBackIcon) { const activeNavbarBackLink = $el.parent().find('.navbar-current').children('.left.sliding').find('.back .icon ~ span'); if (activeNavbarBackLink.length > 0) { titleLeftOffset += activeNavbarBackLink[0].offsetLeft; } } } title[0].f7NavbarLeftOffset = titleLeftOffset; title[0].f7NavbarRightOffset = titleRightOffset; } if (!noLeft && (left.hasClass('sliding') || sliding)) { if (app.rtl) { left[0].f7NavbarLeftOffset = (-(navbarInnerWidth - left[0].offsetWidth) / 2) * inverter; left[0].f7NavbarRightOffset = leftWidth * inverter; } else { left[0].f7NavbarLeftOffset = -leftWidth + separateNavbarLeftOffset; left[0].f7NavbarRightOffset = ((navbarInnerWidth - left[0].offsetWidth) / 2) - separateNavbarRightOffset; if (router && router.params.iosAnimateNavbarBackIcon && left.find('.back .icon').length > 0) { left[0].f7NavbarRightOffset -= left.find('.back .icon')[0].offsetWidth; } } } if (!noRight && (right.hasClass('sliding') || sliding)) { if (app.rtl) { right[0].f7NavbarLeftOffset = -rightWidth * inverter; right[0].f7NavbarRightOffset = ((navbarInnerWidth - right[0].offsetWidth) / 2) * inverter; } else { right[0].f7NavbarLeftOffset = (-(navbarInnerWidth - right[0].offsetWidth) / 2) + separateNavbarLeftOffset; right[0].f7NavbarRightOffset = rightWidth - separateNavbarRightOffset; } } if (subnavbar.length && (subnavbar.hasClass('sliding') || sliding)) { subnavbar[0].f7NavbarLeftOffset = app.rtl ? subnavbar[0].offsetWidth : (-subnavbar[0].offsetWidth + separateNavbarLeftOffset); subnavbar[0].f7NavbarRightOffset = (-subnavbar[0].f7NavbarLeftOffset - separateNavbarRightOffset) + separateNavbarLeftOffset; } } // Title left if (app.params.navbar.iosCenterTitle) { let titleLeft = diff; if (app.rtl && noLeft && noRight && title.length > 0) titleLeft = -titleLeft; title.css({ left: `${titleLeft}px` }); } }, hide(el, animate = true) { let $el = $(el); if ($el.hasClass('navbar-inner')) $el = $el.parents('.navbar'); if (!$el.length) return; if ($el.hasClass('navbar-hidden')) return; const className = `navbar-hidden${animate ? ' navbar-transitioning' : ''}`; $el.transitionEnd(() => { $el.removeClass('navbar-transitioning'); }); $el.addClass(className); }, show(el = '.navbar-hidden', animate = true) { let $el = $(el); if ($el.hasClass('navbar-inner')) $el = $el.parents('.navbar'); if (!$el.length) return; if (!$el.hasClass('navbar-hidden')) return; if (animate) { $el.addClass('navbar-transitioning'); $el.transitionEnd(() => { $el.removeClass('navbar-transitioning'); }); } $el.removeClass('navbar-hidden'); }, getElByPage(page) { let $pageEl; let $navbarEl; let pageData; if (page.$navbarEl || page.$el) { pageData = page; $pageEl = page.$el; } else { $pageEl = $(page); if ($pageEl.length > 0) pageData = $pageEl[0].f7Page; } if (pageData && pageData.$navbarEl && pageData.$navbarEl.length > 0) { $navbarEl = pageData.$navbarEl; } else if ($pageEl) { $navbarEl = $pageEl.children('.navbar').children('.navbar-inner'); } if (!$navbarEl || ($navbarEl && $navbarEl.length === 0)) return undefined; return $navbarEl[0]; }, getPageByEl(navbarInnerEl) { let $navbarInnerEl = $(navbarInnerEl); if ($navbarInnerEl.hasClass('navbar')) { $navbarInnerEl = $navbarInnerEl.find('.navbar-inner'); if ($navbarInnerEl.length > 1) return undefined; } return $navbarInnerEl[0].f7Page; }, initHideNavbarOnScroll(pageEl, navbarInnerEl) { const app = this; const $pageEl = $(pageEl); const $navbarEl = $(navbarInnerEl || app.navbar.getElByPage(pageEl)).closest('.navbar'); let previousScrollTop; let currentScrollTop; let scrollHeight; let offsetHeight; let reachEnd; let action; let navbarHidden; function handleScroll() { const scrollContent = this; if ($pageEl.hasClass('page-previous')) return; currentScrollTop = scrollContent.scrollTop; scrollHeight = scrollContent.scrollHeight; offsetHeight = scrollContent.offsetHeight; reachEnd = currentScrollTop + offsetHeight >= scrollHeight; navbarHidden = $navbarEl.hasClass('navbar-hidden'); if (reachEnd) { if (app.params.navbar.showOnPageScrollEnd) { action = 'show'; } } else if (previousScrollTop > currentScrollTop) { if (app.params.navbar.showOnPageScrollTop || currentScrollTop <= 44) { action = 'show'; } else { action = 'hide'; } } else if (currentScrollTop > 44) { action = 'hide'; } else { action = 'show'; } if (action === 'show' && navbarHidden) { app.navbar.show($navbarEl); navbarHidden = false; } else if (action === 'hide' && !navbarHidden) { app.navbar.hide($navbarEl); navbarHidden = true; } previousScrollTop = currentScrollTop; } $pageEl.on('scroll', '.page-content', handleScroll, true); $pageEl[0].f7ScrollNavbarHandler = handleScroll; }, }; export default { name: 'navbar', create() { const app = this; Utils.extend(app, { navbar: { size: Navbar.size.bind(app), hide: Navbar.hide.bind(app), show: Navbar.show.bind(app), getElByPage: Navbar.getElByPage.bind(app), initHideNavbarOnScroll: Navbar.initHideNavbarOnScroll.bind(app), }, }); }, params: { navbar: { scrollTopOnTitleClick: true, iosCenterTitle: true, hideOnPageScroll: false, showOnPageScrollEnd: true, showOnPageScrollTop: true, }, }, on: { resize() { const app = this; if (app.theme !== 'ios') return; $('.navbar').each((index, navbarEl) => { app.navbar.size(navbarEl); }); }, pageBeforeRemove(page) { if (page.$el[0].f7ScrollNavbarHandler) { page.$el.off('scroll', '.page-content', page.$el[0].f7ScrollNavbarHandler, true); } }, pageBeforeIn(page) { const app = this; if (app.theme !== 'ios') return; let $navbarEl; const view = page.$el.parents('.view')[0].f7View; const navbarInnerEl = app.navbar.getElByPage(page); if (!navbarInnerEl) { $navbarEl = page.$el.parents('.view').children('.navbar'); } else { $navbarEl = $(navbarInnerEl).parents('.navbar'); } if (page.$el.hasClass('no-navbar') || (view.router.dynamicNavbar && !navbarInnerEl)) { const animate = !!(page.pageFrom && page.router.history.length > 1); app.navbar.hide($navbarEl, animate); } else { app.navbar.show($navbarEl); } }, pageReinit(page) { const app = this; if (app.theme !== 'ios') return; const $navbarEl = $(app.navbar.getElByPage(page)); if (!$navbarEl || $navbarEl.length === 0) return; app.navbar.size($navbarEl); }, pageInit(page) { const app = this; const $navbarEl = $(app.navbar.getElByPage(page)); if (!$navbarEl || $navbarEl.length === 0) return; if (app.theme === 'ios') { app.navbar.size($navbarEl); } if (app.params.navbar.hideOnPageScroll || page.$el.find('.hide-navbar-on-scroll').length || page.$el.hasClass('hide-navbar-on-scroll') || page.$el.find('.hide-bars-on-scroll').length) { if (page.$el.find('.keep-navbar-on-scroll').length || page.$el.find('.keep-bars-on-scroll').length) return; app.navbar.initHideNavbarOnScroll(page.el, $navbarEl[0]); } }, modalOpen(modal) { const app = this; if (app.theme !== 'ios') return; modal.$el.find('.navbar:not(.navbar-previous):not(.stacked)').each((index, navbarEl) => { app.navbar.size(navbarEl); }); }, panelOpen(panel) { const app = this; if (app.theme !== 'ios') return; panel.$el.find('.navbar:not(.navbar-previous):not(.stacked)').each((index, navbarEl) => { app.navbar.size(navbarEl); }); }, panelSwipeOpen(panel) { const app = this; if (app.theme !== 'ios') return; panel.$el.find('.navbar:not(.navbar-previous):not(.stacked)').each((index, navbarEl) => { app.navbar.size(navbarEl); }); }, tabShow(tabEl) { const app = this; $(tabEl).find('.navbar:not(.navbar-previous):not(.stacked)').each((index, navbarEl) => { app.navbar.size(navbarEl); }); }, }, clicks: { '.navbar .title': function onTitleClick($clickedEl) { const app = this; if (!app.params.navbar.scrollTopOnTitleClick) return; if ($clickedEl.closest('a').length > 0) { return; } let pageContent; // Find active page const navbar = $clickedEl.parents('.navbar'); // Static Layout pageContent = navbar.parents('.page-content'); if (pageContent.length === 0) { // Fixed Layout if (navbar.parents('.page').length > 0) { pageContent = navbar.parents('.page').find('.page-content'); } // Through Layout if (pageContent.length === 0) { if (navbar.nextAll('.page-current:not(.stacked)').length > 0) { pageContent = navbar.nextAll('.page-current:not(.stacked)').find('.page-content'); } } } if (pageContent && pageContent.length > 0) { // Check for tab if (pageContent.hasClass('tab')) { pageContent = pageContent.parent('.tabs').children('.page-content.tab-active'); } if (pageContent.length > 0) pageContent.scrollTop(0, 300); } }, }, };