UNPKG

mmenu-js

Version:

The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and webapp.

224 lines (185 loc) 6.59 kB
import Mmenu from '../../core/oncanvas/mmenu.oncanvas'; import options from './_options'; import configs from './_configs'; import { extendShorthandOptions } from './_options'; import * as DOM from '../../_modules/dom'; import { extend, originalId } from '../../_modules/helpers'; // Add the options and configs. Mmenu.options.dropdown = options; Mmenu.configs.dropdown = configs; export default function(this: Mmenu) { if (!this.opts.offCanvas) { return; } var options = extendShorthandOptions(this.opts.dropdown); this.opts.dropdown = extend(options, Mmenu.options.dropdown); var configs = this.conf.dropdown; if (!options.drop) { return; } var button: HTMLElement; this.bind('initMenu:after', () => { this.node.menu.classList.add('mm-menu_dropdown'); if (typeof options.position.of != 'string') { let id = originalId(this.node.menu.id); if (id) { options.position.of = '[href="#' + id + '"]'; } } if (typeof options.position.of != 'string') { return; } // Get the button to put the menu next to button = DOM.find(document.body, options.position.of)[0]; // Emulate hover effect var events = options.event.split(' '); if (events.length == 1) { events[1] = events[0]; } if (events[0] == 'hover') { button.addEventListener( 'mouseenter', () => { this.open(); }, { passive: true } ); } if (events[1] == 'hover') { this.node.menu.addEventListener( 'mouseleave', () => { this.close(); }, { passive: true } ); } }); // Add/remove classname and style when opening/closing the menu this.bind('open:start', () => { this.node.menu['mmStyle'] = this.node.menu.getAttribute('style'); this.node.wrpr.classList.add('mm-wrapper_dropdown'); }); this.bind('close:finish', () => { this.node.menu.setAttribute('style', this.node.menu['mmStyle']); this.node.wrpr.classList.remove('mm-wrapper_dropdown'); }); /** * Find the position (x, y) and sizes (width, height) for the menu. * * @param {string} dir The direction to measure ("x" for horizontal, "y" for vertical) * @param {object} obj The object where (previously) measured values are stored. * @return {object} The object where measered values are stored. */ var getPosition = function( this: Mmenu, dir: string, obj: mmLooseObject ): mmLooseObject { var css = obj[0], cls = obj[1]; var _outerSize = dir == 'x' ? 'offsetWidth' : 'offsetHeight', _startPos = dir == 'x' ? 'left' : 'top', _stopPos = dir == 'x' ? 'right' : 'bottom', _size = dir == 'x' ? 'width' : 'height', _winSize = dir == 'x' ? 'innerWidth' : 'innerHeight', _maxSize = dir == 'x' ? 'maxWidth' : 'maxHeight', _position = null; var startPos = DOM.offset(button, _startPos), stopPos = startPos + button[_outerSize], windowSize = window[_winSize]; /** Offset for the menu relative to the button. */ var offs = configs.offset.button[dir] + configs.offset.viewport[dir]; // Position set in option if (options.position[dir]) { switch (options.position[dir]) { case 'left': case 'bottom': _position = 'after'; break; case 'right': case 'top': _position = 'before'; break; } } // Position not set in option, find most space if (_position === null) { _position = startPos + (stopPos - startPos) / 2 < windowSize / 2 ? 'after' : 'before'; } // Set position and max var val, max; if (_position == 'after') { val = dir == 'x' ? startPos : stopPos; max = windowSize - (val + offs); css[_startPos] = val + configs.offset.button[dir] + 'px'; css[_stopPos] = 'auto'; if (options.tip) { cls.push('mm-menu_tip-' + (dir == 'x' ? 'left' : 'top')); } } else { val = dir == 'x' ? stopPos : startPos; max = val - offs; css[_stopPos] = 'calc( 100% - ' + (val - configs.offset.button[dir]) + 'px )'; css[_startPos] = 'auto'; if (options.tip) { cls.push('mm-menu_tip-' + (dir == 'x' ? 'right' : 'bottom')); } } if (options.fitViewport) { css[_maxSize] = Math.min(configs[_size].max, max) + 'px'; } return [css, cls]; }; function position(this: Mmenu) { if (!this.vars.opened) { return; } this.node.menu.setAttribute('style', this.node.menu['mmStyle']); var obj: [mmLooseObject, string[]] = [{}, []]; obj = getPosition.call(this, 'y', obj); obj = getPosition.call(this, 'x', obj); for (let s in obj[0]) { this.node.menu.style[s] = obj[0][s]; } if (options.tip) { var classnames = [ 'mm-menu_tip-left', 'mm-menu_tip-right', 'mm-menu_tip-top', 'mm-menu_tip-bottom' ]; // IE11: classnames.forEach(classname => { this.node.menu.classList.remove(classname); }); obj[1].forEach(classname => { this.node.menu.classList.add(classname); }); // Better browsers: // this.node.menu.classList.remove(...classnames); // this.node.menu.classList.add(...obj[1]); } } this.bind('open:start', position); window.addEventListener( 'resize', evnt => { position.call(this); }, { passive: true } ); if (!this.opts.offCanvas.blockUI) { window.addEventListener( 'scroll', evnt => { position.call(this); }, { passive: true } ); } }