API Docs for:
Show:

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;