@pi0/framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
372 lines (356 loc) • 13.1 kB
JavaScript
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);
}
},
},
};