rizzo-next
Version:
The next generation of Lonely Planet's style guide and pattern library.
241 lines (194 loc) • 7.44 kB
JavaScript
import { Component } from "../../core/bane";
import { authSetupWithDefaults } from "@lonelyplanet/dotcom-core/dist/helpers";
import Overlay from "../overlay";
import Notification from "../notification/notification";
import waitForTransition from "../../core/utils/waitForTransition";
import NavigationActions from "./navigation_actions";
import NavigationState from "./navigation_state";
import subscribe from "../../core/decorators/subscribe";
import matchMedia from "../../core/utils/matchMedia";
import breakpoints from "../../core/utils/breakpoints";
let userPanelTemplate = require("./user_panel.hbs");
let userAvatarTemplate = require("./user_avatar.hbs");
let signOutLinkTemplate = require("./sign_out_link.hbs");
let avatarMarkerTemplate = require("./avatar_marker.hbs");
let profileSettingsButtonTemplate = require("./profile_settings_button.hbs");
let thornTreeLinkTemplate = require("./thorn_tree_link.hbs");
class NavigationComponent extends Component {
initialize() {
this.state = NavigationState.getState();
this.overlay = new Overlay();
this.cartUrl = "http://shop.lonelyplanet.com/cart/update";
let notificationLabel = this.state.cartItemCount === 1 ? "item" : "items";
this.notification = new Notification({
target: this.$el.find(".js-cart-notification"),
content: this.state.cartItemCount,
className: "notification-badge--shop",
label: `${this.state.cartItemCount} ${notificationLabel} in your cart`,
href: this.cartUrl,
});
matchMedia(`(min-width: ${breakpoints.min["720"]})`, (query) => {
if (query.matches) {
this.notification.$el.find(".js-notification-badge")
.removeClass("notification-badge--shop-inline")
.addClass("notification-badge--shop");
} else {
this.notification.$el.find(".js-notification-badge")
.removeClass("notification-badge--shop")
.addClass("notification-badge--shop-inline")
.text("");
}
});
this.name = "navigation";
this.$mobileNavigation = this.$el.find(".mobile-navigation").detach();
this.$mobileNavigation.removeClass("mobile-navigation--hidden");
this.$mobileNavigation.on("click", ".js-close", this._clickNav.bind(this));
this.$mobileNavigation.on("click", ".js-nav-item", this._handleClick.bind(this));
this.$el.on("touchstart", ".js-nav-item", this._handleClick.bind(this));
this.updateShopUrl();
// SubNavigation hover
this.handleHover();
// Events
this.listenTo(NavigationState, "changed:nav", this.toggleNav);
this.listenTo(this.overlay, "click", this._clickNav);
this.subscribe();
}
/**
* Set up hover events to trigger the sub menu's opening and closing.
* Use event delegation here because the user login is dynamically added.
* @return {[type]} [description]
*/
_handleClick(e) {
let $target = $(e.currentTarget);
$target.hasClass("navigation__item") ? this._handleSubNav($target[0]) : this._handleMobileSubNav($target[0]);
if (
$target.find(".mobile-sub-navigation").length &&
!$(e.target).hasClass("sub-navigation__link") &&
!$(e.target).closest("a").hasClass("sub-navigation-feature")
) {
e.preventDefault();
}
}
_handleMobileSubNav(el) {
let $navItem = $(el).find(".mobile-sub-navigation");
if ( $(".is-expanded").length && !$navItem.hasClass("is-expanded") ) {
this.$mobileNavigation.find(".mobile-sub-navigation").removeClass("is-expanded");
this.$mobileNavigation.find(".js-nav-item").removeClass("clicked");
}
$(el).toggleClass("clicked");
$navItem.toggleClass("is-expanded");
}
_handleSubNav(el) {
if ($(el).find(".sub-navigation").hasClass("sub-navigation--visible")) {
this._closeSubNav(el);
} else {
this._openSubNav(el);
}
}
_openSubNav(el) {
clearTimeout(this.hideTimer);
// Always clear the currently active one
this.$el.find(".sub-navigation").removeClass("sub-navigation--visible");
this.showTimer = setTimeout(() => {
$(el).find(".sub-navigation").addClass("sub-navigation--visible");
}, 0);
}
_closeSubNav(el) {
clearTimeout(this.showTimer);
this.hideTimer = setTimeout(() => {
$(el).find(".sub-navigation").removeClass("sub-navigation--visible");
}, 100);
}
handleHover() {
this.$el.on("mouseenter", ".js-nav-item", (e) => this._openSubNav(e.currentTarget));
this.$el.on("mouseleave", ".js-nav-item", (e) => this._closeSubNav(e.currentTarget));
}
toggleNav() {
if(this.state.isNavOpen) {
this.show();
} else {
this.hide();
}
}
show() {
if(!this.state.isNavOpen){
return Promise.all([]);
}
if(this.$mobileNavigation.parents().length === 0) {
this.$mobileNavigation.appendTo(document.body);
}
this.overlay.show();
setTimeout(() => {
this.$mobileNavigation.addClass("mobile-navigation--visible");
}, 20);
return waitForTransition(this.$mobileNavigation, { fallbackTime: 2000 });
}
hide() {
if(this.state.isNavOpen) {
return Promise.all([]);
}
this.$mobileNavigation.removeClass("mobile-navigation--visible");
this.overlay.hide();
return waitForTransition(this.$mobileNavigation, { fallbackTime: 2000 })
.then(() => {
this.$mobileNavigation.detach();
});
}
_clickNav() {
NavigationActions.clickNav();
}
/**
* Change shop URL depending on if there are items in the cart
*/
updateShopUrl() {
const shopUrl = this.state.cartItemCount ?
this.cartUrl :
"http://shop.lonelyplanet.com/";
$(".js-cart-notification")
.find(".navigation__link")
.attr("href", shopUrl);
}
userStatusUpdate(user) {
let $li = this.$el.find(".navigation__item--user"),
$liMobile = this.$mobileNavigation.find(".mobile-navigation__item--user");
let $mobileNavigationHeader = this.$mobileNavigation.find(".js-mobile-navigation-header");
if (!user.id) {
if (this.showNewLoginLink(user.variant)) {
// lp.require is a way to detect if this is a legacy app or not (/¯◡ ‿ ◡)/¯ ~ ┻━┻
const loginLink = window.lp.require ? this.buildLegacyAuthUrl() : "#login";
this.$el.find(".navigation__link[href*='sign_in']").attr("href", loginLink);
this.$mobileNavigation.find(".mobile-navigation__link[href*='sign_in']").attr("href", loginLink);
}
return;
}
$li.html(userAvatarTemplate({
user
})).append(userPanelTemplate({
className: "sub-navigation",
user
}));
$liMobile.html(signOutLinkTemplate({
user
})).before(thornTreeLinkTemplate());
$mobileNavigationHeader.append(avatarMarkerTemplate({
user
})).append(profileSettingsButtonTemplate({
user
}));
}
userNotificationUpdate(user) {
this.userStatusUpdate(user);
}
showNewLoginLink(variant) {
return document.cookie.indexOf(variant) > -1;
}
buildLegacyAuthUrl() {
const windowExists = typeof window !== "undefined";
const { builder } = authSetupWithDefaults( windowExists && window.lp.auth);
const targetLinkUri = windowExists && window.location.origin + window.location.pathname;
return builder.getLoginUrl(null, targetLinkUri);
}
}
export default NavigationComponent;