import Util from './util' /** * -------------------------------------------------------------------------- * Bootstrap (v4.0.0): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ const Dropdown = (($) => { /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ const NAME = 'dropdown' const VERSION = '4.0.0' const DATA_KEY = 'bs.dropdown' const JQUERY_NO_CONFLICT = $.fn[NAME] const Event = { HIDE   : 'hide.bs.dropdown', HIDDEN   : 'hidden.bs.dropdown', SHOW   : 'show.bs.dropdown', SHOWN   : 'shown.bs.dropdown', CLICK   : 'click.bs.dropdown', KEYDOWN   : 'keydown.bs.dropdown.data-api', CLICK_DATA : 'click.bs.dropdown.data-api' } const ClassName = { BACKDROP : 'dropdown-backdrop', DISABLED : 'disabled', OPEN : 'open' } const Selector = { BACKDROP : '.dropdown-backdrop', DATA_TOGGLE : '[data-toggle="dropdown"]', FORM_CHILD : '.dropdown form', ROLE_MENU : '[role="menu"]', ROLE_LISTBOX : '[role="listbox"]', NAVBAR_NAV : '.navbar-nav', VISIBLE_ITEMS : '[role="menu"] li:not(.disabled) a, ' + '[role="listbox"] li:not(.disabled) a' } /** * ------------------------------------------------------------------------ * Class Definition * ------------------------------------------------------------------------ */ class Dropdown { constructor(element) { $(element).on(Event.CLICK, this.toggle) } // getters static get VERSION() { return VERSION } // public toggle() { if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { return } let parent = Dropdown._getParentFromElement(this) let isActive = $(parent).hasClass(ClassName.OPEN) Dropdown._clearMenus() if (isActive) { return false } if ('ontouchstart' in document.documentElement && (!$(parent).closest(Selector.NAVBAR_NAV).length)) { // if mobile we use a backdrop because click events don't delegate let dropdown = document.createElement('div') dropdown.className = ClassName.BACKDROP $(dropdown).insertBefore(this) $(dropdown).on('click', Dropdown._clearMenus) } let relatedTarget = { relatedTarget : this } let showEvent = $.Event(Event.SHOW, relatedTarget) $(parent).trigger(showEvent) if (showEvent.isDefaultPrevented()) { return } this.focus() this.setAttribute('aria-expanded', 'true') $(parent).toggleClass(ClassName.OPEN) $(parent).trigger(Event.SHOWN, relatedTarget) return false } // static static _jQueryInterface(config) { return this.each(function () { let data = $(this).data(DATA_KEY) if (!data) { $(this).data(DATA_KEY, (data = new Dropdown(this))) } if (typeof config === 'string') { data[config].call(this) } }) } static _clearMenus(event) { if (event && event.which === 3) { return } let backdrop = $(Selector.BACKDROP)[0] if (backdrop) { backdrop.parentNode.removeChild(backdrop) } let toggles = $.makeArray($(Selector.DATA_TOGGLE)) for (let i = 0; i < toggles.length; i++) { let parent = Dropdown._getParentFromElement(toggles[i]) let relatedTarget = { relatedTarget : toggles[i] } if (!$(parent).hasClass(ClassName.OPEN)) { continue } if (event && event.type === 'click' && (/input|textarea/i.test(event.target.tagName)) && ($.contains(parent, event.target))) { continue } let hideEvent = $.Event(Event.HIDE, relatedTarget) $(parent).trigger(hideEvent) if (hideEvent.isDefaultPrevented()) { continue } toggles[i].setAttribute('aria-expanded', 'false') $(parent) .removeClass(ClassName.OPEN) .trigger(Event.HIDDEN, relatedTarget) } } static _getParentFromElement(element) { let parent let selector = Util.getSelectorFromElement(element) if (selector) { parent = $(selector)[0] } return parent || element.parentNode } static _dataApiKeydownHandler(event) { if (!/(38|40|27|32)/.test(event.which) || /input|textarea/i.test(event.target.tagName)) { return } event.preventDefault() event.stopPropagation() if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { return } let parent = Dropdown._getParentFromElement(this) let isActive = $(parent).hasClass(ClassName.OPEN) if ((!isActive && event.which !== 27) || (isActive && event.which === 27)) { if (event.which === 27) { let toggle = $(parent).find(Selector.DATA_TOGGLE)[0] $(toggle).trigger('focus') } $(this).trigger('click') return } let items = $.makeArray($(Selector.VISIBLE_ITEMS)) items = items.filter((item) => { return item.offsetWidth || item.offsetHeight }) if (!items.length) { return } let index = items.indexOf(event.target) if (event.which === 38 && index > 0) index-- // up if (event.which === 40 && index < items.length - 1) index++ // down if (!~index) index = 0 items[index].focus() } } /** * ------------------------------------------------------------------------ * Data Api implementation * ------------------------------------------------------------------------ */ $(document) .on(Event.KEYDOWN, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) .on(Event.KEYDOWN, Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler) .on(Event.KEYDOWN, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler) .on(Event.CLICK_DATA, Dropdown._clearMenus) .on(Event.CLICK_DATA, Selector.DATA_TOGGLE, Dropdown.prototype.toggle) .on(Event.CLICK_DATA, Selector.FORM_CHILD, function (e) { e.stopPropagation() }) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ */ $.fn[NAME] = Dropdown._jQueryInterface $.fn[NAME].Constructor = Dropdown $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT return Dropdown._jQueryInterface } return Dropdown })(jQuery) export default Dropdown