dropdown.js 18 KB

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