API Docs for:
Show:

File: development/navigations/offcanvas/js/offcanvas.js

import $ from 'jquery';
import DOMModule from '../../../modules/__base/dommodule';
import {eventType} from '../../../js/utils/events';
import mediaQuery from '../../../js/utils/mq';

import OffCanvasMenuLevel from './offcanvaslevel';

/**
 * @module Navigations
 */

const BP_INITIALLY_VISIBLE = 'screen and (min-width: 1280px)';
const BP_FLUIDCONTENT = 'screen and (min-width: 1280px)';

const $body = $('body');

// PRIVATE METHODS
const handleResize = Symbol('_handleResize');

/**
 * OffCanvasMenu module
 * @class Navigations.OffCanvasMenu
 * @constructor
 * @extends Modules._DOMModule
 */
class OffCanvasMenu extends DOMModule {
    constructor(options) {
        super(options);
        this.$triggerNode = $('[data-offcanvas="'+ this.$data('offcanvas-id')+'"]');
        this._levelstack = [];



        this._bp_visible = this.$data('offcanvas-bp-visible') || BP_INITIALLY_VISIBLE; 
        this._bp_fluid = this.$data('offcanvas-bp-fluid') || BP_FLUIDCONTENT; 
    }

    /**
     * Creates a new OffCanvasMenu instance without the "new" keyword but does not
     *     initialize it
     * @method of
     * @static
     * @for Navigations.OffCanvasMenu
     * @param {node|string|configs} node Node, CSS selector or configuration object
     * @return {OffCanvasMenu} A new instance
     * 
     * @example
     *     import OffCanvasMenu from './navigations/offcanvas/js/offcanvas';
     *
     *     const myOffCanvas = OffCanvasMenu.of('[data-widget=OffCanvasMenu]');
     *     myOffCanvas.render().startUp();
     */
    static of (node) {
        return new OffCanvasMenu(node);
    }


    /**
     * Finds and initializes all OffCanvasMenus on a page at once
     * @method findAll
     * @static
     * @for Navigations.OffCanvasMenu
     * @param {string} selector CSS selector to match nodes against
     * @return {array} A list of instances
     * 
     * @example
     *     import OffCanvasMenu from './navigations/offcanvas/js/offcanvas';
     *
     *     OffCanvasMenu.findAll('[data-widget=OffCanvasMenu]');
     */
    static findAll(selector) {
        return $(selector).toArray().map((node) => {
            const navigation = OffCanvasMenu.of(node);
            navigation.render().startUp();
            return navigation;
        });
    }

    /**
     * Does nothing
     * @method render
     * @for Navigations.OffCanvasMenu
     * @return {this} The instance
     */
    render() {
        return this;
    }
    /**
     * Attaches all eventlisteners to the instance and prepares navigation for 
     *     the initial viewport
     * @method startUp
     * @for Navigations.OffCanvasMenu
     * @return {undefined} Nothing
     * 
     * @example
     *     import OffCanvasMenu from './navigations/offcanvas/js/offcanvas';
     *
     *     const myOffCanvas = OffCanvasMenu.of('[data-widget=OffCanvasMenu]');
     *     myOffCanvas.render().startUp();
     */
    startUp () {
        var _instance,
            _node;

        this.$levels = this.$findChilds('.offcanvas_menu_level').map((idx, rawNode) => {
            _node = $(rawNode);
            _instance = OffCanvasMenuLevel.of(rawNode);
            _instance.render().startUp();
            _instance.addObserver(this);

            if (!idx) { _node.addClass('offcanvas_menu_level--open'); }
 
            if (_node.hasClass('offcanvas_menu_level--open')) {
                this._levelstack.push(_instance);
            }

            return _instance;
        });
        
        this._manageOverflows();
        this[handleResize]();


        // bind functions
        this._toggleOpen = this._toggleOpen.bind(this);
        this._handleScrollerClick = this._handleScrollerClick.bind(this);

        // attach listeners
        this.onResize(handleResize);
        this.$triggerNode.on(eventType.CLICK, this._toggleOpen);

        $('.offcanvas_pusher').on(eventType.TRANSITION, () => {
            this.publish('offcanvas-state--changed');
        });
    }
    /**
     * Opens the navigation layer
     * @method open
     * @for Navigations.OffCanvasMenu
     * @return {undefined} Nothing
     * 
     * @example
     *     import OffCanvasMenu from './navigations/offcanvas/js/offcanvas';
     *
     *     const myOffCanvas = OffCanvasMenu.of('[data-widget=OffCanvasMenu]');
     *     myOffCanvas.render().startUp();
     *     myOffCanvas.open();
     */
    open () {
        $body.addClass('body--menu-open');
        this.$rootNode.addClass('offcanvas_menu--open');

        if (!mediaQuery(this._bp_fluid).matches) {
            $('.offcanvas_scroller').on(eventType.CLICK, this._handleScrollerClick);
        }

        this.publish('offcanvas-init-statechange', { state: 'open' });
    }
    /**
     * Closes the navigation layer
     * @method open
     * @for Navigations.OffCanvasMenu
     * @return {undefined} Nothing
     * 
     * @example
     *     import OffCanvasMenu from './navigations/offcanvas/js/offcanvas';
     *
     *     const myOffCanvas = OffCanvasMenu.of('[data-widget=OffCanvasMenu]');
     *     myOffCanvas.render().startUp();
     *     myOffCanvas.open();
     */
    close () {
        $body.removeClass('body--menu-open');
        this.$rootNode.removeClass('offcanvas_menu--open');

        $('.offcanvas_scroller').off(eventType.CLICK, this._handleScrollerClick);

        this.publish('offcanvas-init-statechange', { state: 'close' });
    }
    // === PRIVATE STUFF ===
    _manageOverflows () {
        this._levelstack.forEach((item, idx) => {
            if (idx < this._levelstack.length - 1) {
                item.setOverflow(false);
            } 
        });
    }
    update (data) {
        var curr;
        if (data.action === 'open') {
            curr = this._levelstack[this._levelstack.length - 1];
            this._levelstack.push(data.level);
            curr.setOverflow(false).
                done(data.level.open.bind(data.level));
        } else {
            this._levelstack.pop();
            curr = this._levelstack[this._levelstack.length - 1];
            curr.setOverflow(true).
                done(data.level.close.bind(data.level));
        }
    }
    // === EVENT HANDLERS ===
    _handleScrollerClick (evt) {
        evt.preventDefault();
        evt.stopPropagation();
        this.close();
    }
    _toggleOpen (evt) {
        evt.preventDefault();
        evt.stopPropagation();
        if ($body.hasClass('body--menu-open')) {
            this.close();
        } else {
            this.open();
        }
    }
    [handleResize]() {
        if (mediaQuery(this._bp_visible).matches) {
            this.open();
        }  else {
            this.close();
        }
        return this;
    }
}

export default OffCanvasMenu;