File: development/navigations/megadropdown/js/megadropdown.js
import $ from 'jquery';
import mediaQuery from '../../../js/utils/mq';
import {eventType} from '../../../js/utils/events';
import DOMModule from '../../../modules/__base/dommodule';
import config from './config';
import g from './globals';
import MegadropdownList from './megadropdownlist';
/**
* @module Navigations
*/
// PRIVATE METHODS
const handleBackClick = Symbol('_handleBackClick');
const handleClick = Symbol('_handleClick');
const handleResize = Symbol('_handleResize');
const $body = $('body');
// need to store window width to compare it in resize.
var windowWidth = $(window).width();
/**
* Megadropdown module
* @class Navigations.Megadropdown
* @constructor
* @extends Modules._DOMModule
*/
class Megadropdown extends DOMModule {
constructor(options) {
super(options);
this.$overlayNode = this.$findChilds('.megadropdown_overlay').first();
this.$triggerNode = $('[data-megadropdown-cmd="Burger::click"]');
}
/**
* Creates a new Megadropdown instance without the "new" keyword but does not
* initialize it
* @method of
* @static
* @for Navigations.Megadropdown
* @param {node|string|configs} node Node, CSS selector or configuration object
* @return {Megadropdown} A new instance
*
* @example
* import Megadropdown from './navigations/megadropdown/js/megadropdown';
*
* const myMegadropdown = Megadropdown.of('[data-widget=Megadropdown]');
* myMegadropdown.render().startUp();
*/
static of (node) {
return new Megadropdown(node);
}
/**
* Finds and initializes all Megadropdowns on a page at once
* @method findAll
* @static
* @for Navigations.Megadropdown
* @param {string} selector CSS selector to match nodes against
* @return {array} A list of instances
*
* @example
* import Megadropdown from './navigations/megadropdown/js/megadropdown';
*
* Megadropdown.findAll('[data-widget=Megadropdown]');
*/
static findAll(selector) {
return $(selector).toArray().map((node) => {
const navigation = Megadropdown.of(node);
navigation.render().startUp();
return navigation;
});
}
/**
* Does nothing
* @method render
* @for Navigations.Megadropdown
* @return {this} The instance
*/
render() {
return this;
}
/**
* Attaches all eventlisteners to the instance and prepares navigation for
* the initial viewport
* @method startUp
* @for Navigations.Megadropdown
* @return {undefined} Nothing
*
* @example
* import Megadropdown from './navigations/megadropdown/js/megadropdown';
*
* const myMegadropdown = Megadropdown.of('[data-widget=Megadropdown]');
* myMegadropdown.render().startUp();
*/
startUp() {
// attach listeners
this.onResize(handleResize);
this.onClick('[data-megadropdown-cmd="Level::jump-back"]', handleBackClick);
this.onClick('.mainnavi_list', handleClick);
$body.on(eventType.CLICK, '[data-megadropdown-cmd="Burger::click"]', (event) => {
event.stopPropagation();
$body.toggleClass('body--megadropdown-open');
});
$body.on(eventType.CLICK, this._resetLists.bind(this));
this.$data('megadropdown-close-channels').forEach((channel) => {
this.subscribe(channel, this._resetLists.bind(this));
});
// init stuff
this._adjustViewport();
this._switchMegadropdowns();
this._resetLists();
}
_adjustViewport() {
if (!mediaQuery(config.BP_TABLET).matches) {
g.setViewport(config.VIEWPORTS.S);
$body.
removeClass('has-desktop-megadropdown').
removeClass('has-tablet-megadropdown').
addClass('has-mobile-megadropdown');
return;
}
if (!mediaQuery(config.BP_DESKTOP).matches) {
g.setViewport(config.VIEWPORTS.M);
$body.
removeClass('has-mobile-megadropdown').
removeClass('has-desktop-megadropdown').
addClass('has-tablet-megadropdown');
return;
}
g.setViewport(config.VIEWPORTS.L);
$body.
removeClass('has-mobile-megadropdown').
removeClass('has-tablet-megadropdown').
addClass('has-desktop-megadropdown');
}
_switchMegadropdowns() {
if (g.getViewport() === config.VIEWPORTS.S || g.getViewport() === config.VIEWPORTS.M) {
if (!this.$$mobileList) {
this.$$mobileList = MegadropdownList.of({
container: this.$overlayNode.find('.megadropdown_section').first(),
mode: config.NAVMODES.MOB,
trigger: this.$triggerNode
});
this.$$mobileList.render().startUp();
this.$$mobileList.addObserver({ update: this._handleMegadropdownListChange.bind(this) });
}
return;
}
if (g.getViewport() === config.VIEWPORTS.L) {
if (!this.$$desktopList) {
this.$$desktopList = MegadropdownList.of({
container: this._buildDesktopDOM().find('.megadropdown_section').first(),
mode: config.NAVMODES.DESK,
trigger: null
});
this.$$desktopList.render().startUp();
this.$$desktopList.addObserver({ update: this._handleMegadropdownListChange.bind(this) });
}
if (!this._hasEnoughSpace()) {
g.setViewport(config.VIEWPORTS.M);
$body.
removeClass('has-mobile-megadropdown').
removeClass('has-desktop-megadropdown').
addClass('has-tablet-megadropdown');
this._switchMegadropdowns();
}
}
}
_resetLists() {
if (this.$$mobileList) {
if (g.getViewport() === config.VIEWPORTS.L) {
this.$$mobileList.close(config.ENTRYLEVELS[config.NAVMODES.MOB]);
} else {
this.$$mobileList.recalcDimensions();
this.$$mobileList.resetToInitialState();
}
}
if (this.$$desktopList) {
if (g.getViewport() === config.VIEWPORTS.L) {
this.$$desktopList.resetToInitialState();
this.$$desktopList.recalcDimensions();
} else {
this.$$desktopList.close(config.ENTRYLEVELS[config.NAVMODES.DESK]);
}
}
$body.removeClass('body--megadropdown-open');
}
_hasEnoughSpace() {
var $list = this.$findChilds('.megadropdown_section.lvl--1 > .mainnavi_list');
var $listItems = $list.find('> li');
var _innerW = $listItems.toArray().reduce(
(acc, item) => acc + item.getBoundingClientRect().width,
0
);
return _innerW <= $list[0].getBoundingClientRect().width; //.innerWidth();
}
_buildDesktopDOM () {
var _bar = $('<div>').addClass('megadropdown_bar');
var _barInner = $('<div>').addClass('megadropdown_bar_inner pagesize');
var _backBtnWrap = $('<div>').addClass('megadropdown_back-button-wrap pagesize').
html('<a href="#" class="megadropdown_back-button icontxt" data-megadropdown-cmd="Level::jump-back"><span class="icontxt_icon icontxt_icon--center fa fa-chevron-left"></span><span class="icontxt_content">Zurück</span></a>');
var _overlayInner = $('<div>').addClass('megadropdown_overlay_inner pagesize');
var _overlay = $('<div>').addClass('megadropdown_overlay');
var _clip = $('<div>').addClass('megadropdown_clip');
var _firstSection = this.$findChilds('.megadropdown_section').first().
clone().
addClass('lvl--1');
this.$findChilds('.megadropdown_section').find('.megadropdown_teaser--right').remove();
this.$findChilds('.megadropdown_section').find('.megadropdown_teaser--left').remove();
_firstSection.find('.metanavi').remove();
_firstSection.find('.mainnavi_link.is-active').removeClass('is-active');
_firstSection.find('> .mainnavi_list > li').each(function () {
let _this = $(this);
let _imageTeaser = _this.find('.megadropdown_teaser--right').first();
let _textTeaser = _this.find('.megadropdown_teaser--left').first();
let _section = _this.find('.megadropdown_section').first();
let _myClip = _clip.clone();
let _myOverlay = _overlay.clone();
let _myOverlayInner = _overlayInner.clone();
let _myBackBtnWrap = _backBtnWrap.clone()
_myClip.append(_section);
_myOverlayInner.append(
_textTeaser,
_myClip,
_imageTeaser
);
_myOverlay.append(
_myBackBtnWrap,
_myOverlayInner
);
_this.append(_myOverlay);
_section.find('> .mainnavi_list > li > .mainnavi_link--overview').first().parent().remove();
});
if ($('[data-barmegadropdown-search]').length > 0) {
_firstSection.append($('[data-barmegadropdown-search]').clone());
}
_barInner.append(_firstSection);
_bar.append(_barInner);
_bar.insertBefore(this.$overlayNode);
return _bar;
}
_handleMegadropdownListChange(data) {
if (data.action === 'MegadropdownList::open') { // sublist opened
this.publish('Megadropdown::open', { module: this });
}
}
// === EVENT HANDLERS ===
[handleBackClick](event) {
event.preventDefault();
event.stopPropagation();
if (g.getViewport() === config.VIEWPORTS.L) {
this.$$desktopList.close(g.getLevel());
return;
}
this.$$mobileList.close(g.getLevel());
}
[handleResize](event) {
// iOS fires a resize whenever the url-bar appears or disappears
// by scrolling. to prevent the megadropdown from closing, we check, if the
// width stayed the same. Otherwise we can close the megadropdown.
if ($(window).width() !== windowWidth){
this._adjustViewport();
this._switchMegadropdowns();
this._resetLists();
windowWidth = $(window).width();
}
return this;
}
[handleClick](event) {
event.stopPropagation();
}
}
export default Megadropdown;