dropdown.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /*!
  2. * Bootstrap dropdown.js v4.4.1 (https://getbootstrap.com/)
  3. * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('popper.js'), require('./util.js')) :
  8. typeof define === 'function' && define.amd ? define(['jquery', 'popper.js', './util.js'], factory) :
  9. (global = global || self, global.Dropdown = factory(global.jQuery, global.Popper, global.Util));
  10. }(this, (function ($, Popper, Util) { 'use strict';
  11. $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
  12. Popper = Popper && Popper.hasOwnProperty('default') ? Popper['default'] : Popper;
  13. Util = Util && Util.hasOwnProperty('default') ? Util['default'] : Util;
  14. function _defineProperties(target, props) {
  15. for (var i = 0; i < props.length; i++) {
  16. var descriptor = props[i];
  17. descriptor.enumerable = descriptor.enumerable || false;
  18. descriptor.configurable = true;
  19. if ("value" in descriptor) descriptor.writable = true;
  20. Object.defineProperty(target, descriptor.key, descriptor);
  21. }
  22. }
  23. function _createClass(Constructor, protoProps, staticProps) {
  24. if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  25. if (staticProps) _defineProperties(Constructor, staticProps);
  26. return Constructor;
  27. }
  28. function _defineProperty(obj, key, value) {
  29. if (key in obj) {
  30. Object.defineProperty(obj, key, {
  31. value: value,
  32. enumerable: true,
  33. configurable: true,
  34. writable: true
  35. });
  36. } else {
  37. obj[key] = value;
  38. }
  39. return obj;
  40. }
  41. function ownKeys(object, enumerableOnly) {
  42. var keys = Object.keys(object);
  43. if (Object.getOwnPropertySymbols) {
  44. var symbols = Object.getOwnPropertySymbols(object);
  45. if (enumerableOnly) symbols = symbols.filter(function (sym) {
  46. return Object.getOwnPropertyDescriptor(object, sym).enumerable;
  47. });
  48. keys.push.apply(keys, symbols);
  49. }
  50. return keys;
  51. }
  52. function _objectSpread2(target) {
  53. for (var i = 1; i < arguments.length; i++) {
  54. var source = arguments[i] != null ? arguments[i] : {};
  55. if (i % 2) {
  56. ownKeys(Object(source), true).forEach(function (key) {
  57. _defineProperty(target, key, source[key]);
  58. });
  59. } else if (Object.getOwnPropertyDescriptors) {
  60. Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
  61. } else {
  62. ownKeys(Object(source)).forEach(function (key) {
  63. Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
  64. });
  65. }
  66. }
  67. return target;
  68. }
  69. /**
  70. * ------------------------------------------------------------------------
  71. * Constants
  72. * ------------------------------------------------------------------------
  73. */
  74. var NAME = 'dropdown';
  75. var VERSION = '4.4.1';
  76. var DATA_KEY = 'bs.dropdown';
  77. var EVENT_KEY = "." + DATA_KEY;
  78. var DATA_API_KEY = '.data-api';
  79. var JQUERY_NO_CONFLICT = $.fn[NAME];
  80. var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
  81. var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
  82. var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
  83. var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
  84. var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
  85. var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
  86. var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE);
  87. var Event = {
  88. HIDE: "hide" + EVENT_KEY,
  89. HIDDEN: "hidden" + EVENT_KEY,
  90. SHOW: "show" + EVENT_KEY,
  91. SHOWN: "shown" + EVENT_KEY,
  92. CLICK: "click" + EVENT_KEY,
  93. CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY,
  94. KEYDOWN_DATA_API: "keydown" + EVENT_KEY + DATA_API_KEY,
  95. KEYUP_DATA_API: "keyup" + EVENT_KEY + DATA_API_KEY
  96. };
  97. var ClassName = {
  98. DISABLED: 'disabled',
  99. SHOW: 'show',
  100. DROPUP: 'dropup',
  101. DROPRIGHT: 'dropright',
  102. DROPLEFT: 'dropleft',
  103. MENURIGHT: 'dropdown-menu-right',
  104. MENULEFT: 'dropdown-menu-left',
  105. POSITION_STATIC: 'position-static'
  106. };
  107. var Selector = {
  108. DATA_TOGGLE: '[data-toggle="dropdown"]',
  109. FORM_CHILD: '.dropdown form',
  110. MENU: '.dropdown-menu',
  111. NAVBAR_NAV: '.navbar-nav',
  112. VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
  113. };
  114. var AttachmentMap = {
  115. TOP: 'top-start',
  116. TOPEND: 'top-end',
  117. BOTTOM: 'bottom-start',
  118. BOTTOMEND: 'bottom-end',
  119. RIGHT: 'right-start',
  120. RIGHTEND: 'right-end',
  121. LEFT: 'left-start',
  122. LEFTEND: 'left-end'
  123. };
  124. var Default = {
  125. offset: 0,
  126. flip: true,
  127. boundary: 'scrollParent',
  128. reference: 'toggle',
  129. display: 'dynamic',
  130. popperConfig: null
  131. };
  132. var DefaultType = {
  133. offset: '(number|string|function)',
  134. flip: 'boolean',
  135. boundary: '(string|element)',
  136. reference: '(string|element)',
  137. display: 'string',
  138. popperConfig: '(null|object)'
  139. };
  140. /**
  141. * ------------------------------------------------------------------------
  142. * Class Definition
  143. * ------------------------------------------------------------------------
  144. */
  145. var Dropdown =
  146. /*#__PURE__*/
  147. function () {
  148. function Dropdown(element, config) {
  149. this._element = element;
  150. this._popper = null;
  151. this._config = this._getConfig(config);
  152. this._menu = this._getMenuElement();
  153. this._inNavbar = this._detectNavbar();
  154. this._addEventListeners();
  155. } // Getters
  156. var _proto = Dropdown.prototype;
  157. // Public
  158. _proto.toggle = function toggle() {
  159. if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) {
  160. return;
  161. }
  162. var isActive = $(this._menu).hasClass(ClassName.SHOW);
  163. Dropdown._clearMenus();
  164. if (isActive) {
  165. return;
  166. }
  167. this.show(true);
  168. };
  169. _proto.show = function show(usePopper) {
  170. if (usePopper === void 0) {
  171. usePopper = false;
  172. }
  173. if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || $(this._menu).hasClass(ClassName.SHOW)) {
  174. return;
  175. }
  176. var relatedTarget = {
  177. relatedTarget: this._element
  178. };
  179. var showEvent = $.Event(Event.SHOW, relatedTarget);
  180. var parent = Dropdown._getParentFromElement(this._element);
  181. $(parent).trigger(showEvent);
  182. if (showEvent.isDefaultPrevented()) {
  183. return;
  184. } // Disable totally Popper.js for Dropdown in Navbar
  185. if (!this._inNavbar && usePopper) {
  186. /**
  187. * Check for Popper dependency
  188. * Popper - https://popper.js.org
  189. */
  190. if (typeof Popper === 'undefined') {
  191. throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)');
  192. }
  193. var referenceElement = this._element;
  194. if (this._config.reference === 'parent') {
  195. referenceElement = parent;
  196. } else if (Util.isElement(this._config.reference)) {
  197. referenceElement = this._config.reference; // Check if it's jQuery element
  198. if (typeof this._config.reference.jquery !== 'undefined') {
  199. referenceElement = this._config.reference[0];
  200. }
  201. } // If boundary is not `scrollParent`, then set position to `static`
  202. // to allow the menu to "escape" the scroll parent's boundaries
  203. // https://github.com/twbs/bootstrap/issues/24251
  204. if (this._config.boundary !== 'scrollParent') {
  205. $(parent).addClass(ClassName.POSITION_STATIC);
  206. }
  207. this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig());
  208. } // If this is a touch-enabled device we add extra
  209. // empty mouseover listeners to the body's immediate children;
  210. // only needed because of broken event delegation on iOS
  211. // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
  212. if ('ontouchstart' in document.documentElement && $(parent).closest(Selector.NAVBAR_NAV).length === 0) {
  213. $(document.body).children().on('mouseover', null, $.noop);
  214. }
  215. this._element.focus();
  216. this._element.setAttribute('aria-expanded', true);
  217. $(this._menu).toggleClass(ClassName.SHOW);
  218. $(parent).toggleClass(ClassName.SHOW).trigger($.Event(Event.SHOWN, relatedTarget));
  219. };
  220. _proto.hide = function hide() {
  221. if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED) || !$(this._menu).hasClass(ClassName.SHOW)) {
  222. return;
  223. }
  224. var relatedTarget = {
  225. relatedTarget: this._element
  226. };
  227. var hideEvent = $.Event(Event.HIDE, relatedTarget);
  228. var parent = Dropdown._getParentFromElement(this._element);
  229. $(parent).trigger(hideEvent);
  230. if (hideEvent.isDefaultPrevented()) {
  231. return;
  232. }
  233. if (this._popper) {
  234. this._popper.destroy();
  235. }
  236. $(this._menu).toggleClass(ClassName.SHOW);
  237. $(parent).toggleClass(ClassName.SHOW).trigger($.Event(Event.HIDDEN, relatedTarget));
  238. };
  239. _proto.dispose = function dispose() {
  240. $.removeData(this._element, DATA_KEY);
  241. $(this._element).off(EVENT_KEY);
  242. this._element = null;
  243. this._menu = null;
  244. if (this._popper !== null) {
  245. this._popper.destroy();
  246. this._popper = null;
  247. }
  248. };
  249. _proto.update = function update() {
  250. this._inNavbar = this._detectNavbar();
  251. if (this._popper !== null) {
  252. this._popper.scheduleUpdate();
  253. }
  254. } // Private
  255. ;
  256. _proto._addEventListeners = function _addEventListeners() {
  257. var _this = this;
  258. $(this._element).on(Event.CLICK, function (event) {
  259. event.preventDefault();
  260. event.stopPropagation();
  261. _this.toggle();
  262. });
  263. };
  264. _proto._getConfig = function _getConfig(config) {
  265. config = _objectSpread2({}, this.constructor.Default, {}, $(this._element).data(), {}, config);
  266. Util.typeCheckConfig(NAME, config, this.constructor.DefaultType);
  267. return config;
  268. };
  269. _proto._getMenuElement = function _getMenuElement() {
  270. if (!this._menu) {
  271. var parent = Dropdown._getParentFromElement(this._element);
  272. if (parent) {
  273. this._menu = parent.querySelector(Selector.MENU);
  274. }
  275. }
  276. return this._menu;
  277. };
  278. _proto._getPlacement = function _getPlacement() {
  279. var $parentDropdown = $(this._element.parentNode);
  280. var placement = AttachmentMap.BOTTOM; // Handle dropup
  281. if ($parentDropdown.hasClass(ClassName.DROPUP)) {
  282. placement = AttachmentMap.TOP;
  283. if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
  284. placement = AttachmentMap.TOPEND;
  285. }
  286. } else if ($parentDropdown.hasClass(ClassName.DROPRIGHT)) {
  287. placement = AttachmentMap.RIGHT;
  288. } else if ($parentDropdown.hasClass(ClassName.DROPLEFT)) {
  289. placement = AttachmentMap.LEFT;
  290. } else if ($(this._menu).hasClass(ClassName.MENURIGHT)) {
  291. placement = AttachmentMap.BOTTOMEND;
  292. }
  293. return placement;
  294. };
  295. _proto._detectNavbar = function _detectNavbar() {
  296. return $(this._element).closest('.navbar').length > 0;
  297. };
  298. _proto._getOffset = function _getOffset() {
  299. var _this2 = this;
  300. var offset = {};
  301. if (typeof this._config.offset === 'function') {
  302. offset.fn = function (data) {
  303. data.offsets = _objectSpread2({}, data.offsets, {}, _this2._config.offset(data.offsets, _this2._element) || {});
  304. return data;
  305. };
  306. } else {
  307. offset.offset = this._config.offset;
  308. }
  309. return offset;
  310. };
  311. _proto._getPopperConfig = function _getPopperConfig() {
  312. var popperConfig = {
  313. placement: this._getPlacement(),
  314. modifiers: {
  315. offset: this._getOffset(),
  316. flip: {
  317. enabled: this._config.flip
  318. },
  319. preventOverflow: {
  320. boundariesElement: this._config.boundary
  321. }
  322. }
  323. }; // Disable Popper.js if we have a static display
  324. if (this._config.display === 'static') {
  325. popperConfig.modifiers.applyStyle = {
  326. enabled: false
  327. };
  328. }
  329. return _objectSpread2({}, popperConfig, {}, this._config.popperConfig);
  330. } // Static
  331. ;
  332. Dropdown._jQueryInterface = function _jQueryInterface(config) {
  333. return this.each(function () {
  334. var data = $(this).data(DATA_KEY);
  335. var _config = typeof config === 'object' ? config : null;
  336. if (!data) {
  337. data = new Dropdown(this, _config);
  338. $(this).data(DATA_KEY, data);
  339. }
  340. if (typeof config === 'string') {
  341. if (typeof data[config] === 'undefined') {
  342. throw new TypeError("No method named \"" + config + "\"");
  343. }
  344. data[config]();
  345. }
  346. });
  347. };
  348. Dropdown._clearMenus = function _clearMenus(event) {
  349. if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
  350. return;
  351. }
  352. var toggles = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE));
  353. for (var i = 0, len = toggles.length; i < len; i++) {
  354. var parent = Dropdown._getParentFromElement(toggles[i]);
  355. var context = $(toggles[i]).data(DATA_KEY);
  356. var relatedTarget = {
  357. relatedTarget: toggles[i]
  358. };
  359. if (event && event.type === 'click') {
  360. relatedTarget.clickEvent = event;
  361. }
  362. if (!context) {
  363. continue;
  364. }
  365. var dropdownMenu = context._menu;
  366. if (!$(parent).hasClass(ClassName.SHOW)) {
  367. continue;
  368. }
  369. if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) {
  370. continue;
  371. }
  372. var hideEvent = $.Event(Event.HIDE, relatedTarget);
  373. $(parent).trigger(hideEvent);
  374. if (hideEvent.isDefaultPrevented()) {
  375. continue;
  376. } // If this is a touch-enabled device we remove the extra
  377. // empty mouseover listeners we added for iOS support
  378. if ('ontouchstart' in document.documentElement) {
  379. $(document.body).children().off('mouseover', null, $.noop);
  380. }
  381. toggles[i].setAttribute('aria-expanded', 'false');
  382. if (context._popper) {
  383. context._popper.destroy();
  384. }
  385. $(dropdownMenu).removeClass(ClassName.SHOW);
  386. $(parent).removeClass(ClassName.SHOW).trigger($.Event(Event.HIDDEN, relatedTarget));
  387. }
  388. };
  389. Dropdown._getParentFromElement = function _getParentFromElement(element) {
  390. var parent;
  391. var selector = Util.getSelectorFromElement(element);
  392. if (selector) {
  393. parent = document.querySelector(selector);
  394. }
  395. return parent || element.parentNode;
  396. } // eslint-disable-next-line complexity
  397. ;
  398. Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) {
  399. // If not input/textarea:
  400. // - And not a key in REGEXP_KEYDOWN => not a dropdown command
  401. // If input/textarea:
  402. // - If space key => not a dropdown command
  403. // - If key is other than escape
  404. // - If key is not up or down => not a dropdown command
  405. // - If trigger inside the menu => not a dropdown command
  406. if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(Selector.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
  407. return;
  408. }
  409. event.preventDefault();
  410. event.stopPropagation();
  411. if (this.disabled || $(this).hasClass(ClassName.DISABLED)) {
  412. return;
  413. }
  414. var parent = Dropdown._getParentFromElement(this);
  415. var isActive = $(parent).hasClass(ClassName.SHOW);
  416. if (!isActive && event.which === ESCAPE_KEYCODE) {
  417. return;
  418. }
  419. if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
  420. if (event.which === ESCAPE_KEYCODE) {
  421. var toggle = parent.querySelector(Selector.DATA_TOGGLE);
  422. $(toggle).trigger('focus');
  423. }
  424. $(this).trigger('click');
  425. return;
  426. }
  427. var items = [].slice.call(parent.querySelectorAll(Selector.VISIBLE_ITEMS)).filter(function (item) {
  428. return $(item).is(':visible');
  429. });
  430. if (items.length === 0) {
  431. return;
  432. }
  433. var index = items.indexOf(event.target);
  434. if (event.which === ARROW_UP_KEYCODE && index > 0) {
  435. // Up
  436. index--;
  437. }
  438. if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) {
  439. // Down
  440. index++;
  441. }
  442. if (index < 0) {
  443. index = 0;
  444. }
  445. items[index].focus();
  446. };
  447. _createClass(Dropdown, null, [{
  448. key: "VERSION",
  449. get: function get() {
  450. return VERSION;
  451. }
  452. }, {
  453. key: "Default",
  454. get: function get() {
  455. return Default;
  456. }
  457. }, {
  458. key: "DefaultType",
  459. get: function get() {
  460. return DefaultType;
  461. }
  462. }]);
  463. return Dropdown;
  464. }();
  465. /**
  466. * ------------------------------------------------------------------------
  467. * Data Api implementation
  468. * ------------------------------------------------------------------------
  469. */
  470. $(document).on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler).on(Event.CLICK_DATA_API + " " + Event.KEYUP_DATA_API, Dropdown._clearMenus).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
  471. event.preventDefault();
  472. event.stopPropagation();
  473. Dropdown._jQueryInterface.call($(this), 'toggle');
  474. }).on(Event.CLICK_DATA_API, Selector.FORM_CHILD, function (e) {
  475. e.stopPropagation();
  476. });
  477. /**
  478. * ------------------------------------------------------------------------
  479. * jQuery
  480. * ------------------------------------------------------------------------
  481. */
  482. $.fn[NAME] = Dropdown._jQueryInterface;
  483. $.fn[NAME].Constructor = Dropdown;
  484. $.fn[NAME].noConflict = function () {
  485. $.fn[NAME] = JQUERY_NO_CONFLICT;
  486. return Dropdown._jQueryInterface;
  487. };
  488. return Dropdown;
  489. })));
  490. //# sourceMappingURL=dropdown.js.map