dropdown.js 17 KB

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