From ab2fc63d08b8c53d6f29bcfd73b7f2d5ceaacacd Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Sun, 22 May 2016 02:16:27 -0400 Subject: Dropdown: remove dependency to role="menu", role="listbox" a and li elements => fix keyboard navigation --- js/src/dropdown.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 92f841bc4..a9786a534 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -44,11 +44,9 @@ const Dropdown = (($) => { BACKDROP : '.dropdown-backdrop', DATA_TOGGLE : '[data-toggle="dropdown"]', FORM_CHILD : '.dropdown form', - ROLE_MENU : '[role="menu"]', - ROLE_LISTBOX : '[role="listbox"]', + MENU : '.dropdown-menu', NAVBAR_NAV : '.navbar-nav', - VISIBLE_ITEMS : '[role="menu"] li:not(.disabled) a, ' - + '[role="listbox"] li:not(.disabled) a' + VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)' } @@ -268,8 +266,7 @@ const Dropdown = (($) => { $(document) .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) - .on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler) - .on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler) + .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler) .on(Event.CLICK_DATA_API, Dropdown._clearMenus) .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle) .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => { -- cgit v1.2.3 From bbb0d2b573c28793358be66a1c05f2d68ded4db6 Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Mon, 2 Jan 2017 17:44:27 -0500 Subject: Dropdown: close menu when focusing outside element (#21375) * Close dropdown menu when focusing an outside element * Update unit test to new markup --- js/src/dropdown.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 97bba1c76..29c4efe04 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -35,6 +35,7 @@ const Dropdown = (($) => { SHOWN : `shown${EVENT_KEY}`, CLICK : `click${EVENT_KEY}`, CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`, + FOCUSIN_DATA_API : `focusin${EVENT_KEY}${DATA_API_KEY}`, KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}` } @@ -180,9 +181,9 @@ const Dropdown = (($) => { continue } - if (event && event.type === 'click' && - /input|textarea/i.test(event.target.tagName) && - $.contains(parent, event.target)) { + if (event && (event.type === 'click' && + /input|textarea/i.test(event.target.tagName) || event.type === 'focusin') + && $.contains(parent, event.target)) { continue } @@ -275,7 +276,7 @@ const Dropdown = (($) => { .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) .on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler) .on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler) - .on(Event.CLICK_DATA_API, Dropdown._clearMenus) + .on(`${Event.CLICK_DATA_API} ${Event.FOCUSIN_DATA_API}`, Dropdown._clearMenus) .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle) .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => { e.stopPropagation() -- cgit v1.2.3 From 67958f35e80af425c08fa367e8a6f1eeefb830bc Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Wed, 4 Jan 2017 12:24:33 -0500 Subject: Merge conflict --- js/src/dropdown.js | 6 ------ 1 file changed, 6 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 46287fb90..873c106a7 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -272,14 +272,8 @@ const Dropdown = (($) => { $(document) .on(Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown._dataApiKeydownHandler) -<<<<<<< HEAD .on(Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown._dataApiKeydownHandler) - .on(Event.CLICK_DATA_API, Dropdown._clearMenus) -======= - .on(Event.KEYDOWN_DATA_API, Selector.ROLE_MENU, Dropdown._dataApiKeydownHandler) - .on(Event.KEYDOWN_DATA_API, Selector.ROLE_LISTBOX, Dropdown._dataApiKeydownHandler) .on(`${Event.CLICK_DATA_API} ${Event.FOCUSIN_DATA_API}`, Dropdown._clearMenus) ->>>>>>> twbs/v4-dev .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle) .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => { e.stopPropagation() -- cgit v1.2.3 From 045888fa3887f5e65658499da11c8ad737222759 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Fri, 6 Jan 2017 08:38:04 -0800 Subject: version bump --- js/src/dropdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 29c4efe04..36305df46 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.5): dropdown.js + * Bootstrap (v4.0.0-alpha.6): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ const Dropdown = (($) => { */ const NAME = 'dropdown' - const VERSION = '4.0.0-alpha.5' + const VERSION = '4.0.0-alpha.6' const DATA_KEY = 'bs.dropdown' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -- cgit v1.2.3 From c80e13a48a50caecc3e0c7b2904655e8bdc95d9c Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Sat, 14 Jan 2017 21:42:24 -0500 Subject: Use existing keycode constants in dropdown. (#21697) --- js/src/dropdown.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 36305df46..1660d4257 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -24,9 +24,11 @@ const Dropdown = (($) => { const DATA_API_KEY = '.data-api' const JQUERY_NO_CONFLICT = $.fn[NAME] const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key + const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) + const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}|${SPACE_KEYCODE}`) const Event = { HIDE : `hide${EVENT_KEY}`, @@ -213,7 +215,7 @@ const Dropdown = (($) => { } static _dataApiKeydownHandler(event) { - if (!/(38|40|27|32)/.test(event.which) || + if (!REGEXP_KEYDOWN.test(event.which) || /input|textarea/i.test(event.target.tagName)) { return } -- cgit v1.2.3 From 403f55fba976908cd1fd9dfd8ea4a98d035daaf2 Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Sun, 22 Jan 2017 17:34:54 -0500 Subject: Fix spacebar key in Firefox for button elements --- js/src/dropdown.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 200f31569..5c204f054 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -28,7 +28,7 @@ const Dropdown = (($) => { const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) - const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}|${SPACE_KEYCODE}`) + const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) const Event = { HIDE : `hide${EVENT_KEY}`, @@ -213,7 +213,7 @@ const Dropdown = (($) => { } static _dataApiKeydownHandler(event) { - if (!REGEXP_KEYDOWN.test(event.which) || + if (!REGEXP_KEYDOWN.test(event.which) && /button/i.test(event.target.tagName) && event.which === SPACE_KEYCODE || /input|textarea/i.test(event.target.tagName)) { return } @@ -228,8 +228,8 @@ const Dropdown = (($) => { const parent = Dropdown._getParentFromElement(this) const isActive = $(parent).hasClass(ClassName.SHOW) - if (!isActive && event.which !== ESCAPE_KEYCODE || - isActive && event.which === ESCAPE_KEYCODE) { + if (!isActive && (event.which !== ESCAPE_KEYCODE || event.which !== SPACE_KEYCODE) || + isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { if (event.which === ESCAPE_KEYCODE) { const toggle = $(parent).find(Selector.DATA_TOGGLE)[0] -- cgit v1.2.3 From 4ab576a41941b720e2f6fa77724203f66a1d3a17 Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Wed, 8 Feb 2017 18:51:50 -0500 Subject: Fixes #21941 --- js/src/dropdown.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 5c204f054..1e85c2530 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -25,6 +25,7 @@ const Dropdown = (($) => { const JQUERY_NO_CONFLICT = $.fn[NAME] const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key + const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) @@ -37,8 +38,8 @@ const Dropdown = (($) => { SHOWN : `shown${EVENT_KEY}`, CLICK : `click${EVENT_KEY}`, CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`, - FOCUSIN_DATA_API : `focusin${EVENT_KEY}${DATA_API_KEY}`, - KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}` + KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`, + KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}` } const ClassName = { @@ -160,7 +161,8 @@ const Dropdown = (($) => { } static _clearMenus(event) { - if (event && event.which === RIGHT_MOUSE_BUTTON_WHICH) { + if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || + event.type === 'keyup' && event.which !== TAB_KEYCODE)) { return } @@ -182,7 +184,7 @@ const Dropdown = (($) => { } if (event && (event.type === 'click' && - /input|textarea/i.test(event.target.tagName) || event.type === 'focusin') + /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) { continue } @@ -213,7 +215,7 @@ const Dropdown = (($) => { } static _dataApiKeydownHandler(event) { - if (!REGEXP_KEYDOWN.test(event.which) && /button/i.test(event.target.tagName) && event.which === SPACE_KEYCODE || + if (!REGEXP_KEYDOWN.test(event.which) || /button/i.test(event.target.tagName) && event.which === SPACE_KEYCODE || /input|textarea/i.test(event.target.tagName)) { return } @@ -275,7 +277,7 @@ const Dropdown = (($) => { $(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.FOCUSIN_DATA_API}`, Dropdown._clearMenus) + .on(`${Event.CLICK_DATA_API} ${Event.KEYUP_DATA_API}`, Dropdown._clearMenus) .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, Dropdown.prototype.toggle) .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => { e.stopPropagation() -- cgit v1.2.3 From f2f805128508e82f0adc6e57b421dfb46d65a434 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Sat, 18 Mar 2017 20:41:13 -0400 Subject: Fix backdrop for dropdown menu on mobile (#21578) - Create backdrop only if the menu is actually open (do not create it if the show event is prevented) - Drop the backdrop only when the corresponding menu is closed (do not remove if there is no menu to close or if the hide event is prevented) --- js/src/dropdown.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 1660d4257..644273a0a 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -97,16 +97,6 @@ const Dropdown = (($) => { 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 - const dropdown = document.createElement('div') - dropdown.className = ClassName.BACKDROP - $(dropdown).insertBefore(this) - $(dropdown).on('click', Dropdown._clearMenus) - } - const relatedTarget = { relatedTarget : this } @@ -118,6 +108,17 @@ const Dropdown = (($) => { return false } + // set the backdrop only if the dropdown menu will be opened + if ('ontouchstart' in document.documentElement && + !$(parent).closest(Selector.NAVBAR_NAV).length) { + + // if mobile we use a backdrop because click events don't delegate + const dropdown = document.createElement('div') + dropdown.className = ClassName.BACKDROP + $(dropdown).insertBefore(this) + $(dropdown).on('click', Dropdown._clearMenus) + } + this.focus() this.setAttribute('aria-expanded', true) @@ -166,11 +167,6 @@ const Dropdown = (($) => { return } - const backdrop = $(Selector.BACKDROP)[0] - if (backdrop) { - backdrop.parentNode.removeChild(backdrop) - } - const toggles = $.makeArray($(Selector.DATA_TOGGLE)) for (let i = 0; i < toggles.length; i++) { @@ -195,6 +191,12 @@ const Dropdown = (($) => { continue } + // remove backdrop only if the dropdown menu will be hidden + const backdrop = $(parent).find(Selector.BACKDROP)[0] + if (backdrop) { + backdrop.parentNode.removeChild(backdrop) + } + toggles[i].setAttribute('aria-expanded', 'false') $(parent) -- cgit v1.2.3 From 81e12d5715d675b44da4c7d6547583f1d546e491 Mon Sep 17 00:00:00 2001 From: Pierre-Denis Vanduynslager Date: Wed, 12 Apr 2017 09:41:27 -0400 Subject: Indent --- js/src/dropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index d7eebd8c9..fc2908e7d 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -29,7 +29,7 @@ const Dropdown = (($) => { const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) - const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) + const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) const Event = { HIDE : `hide${EVENT_KEY}`, -- cgit v1.2.3 From 3275ca4b30383390d3475beb8c4f43343ab31f5c Mon Sep 17 00:00:00 2001 From: "Patrick H. Lauke" Date: Wed, 12 Apr 2017 13:54:16 +0100 Subject: Reword "mobile" to "touch-enabled" ...as touch is not exclusive to "mobile" anymore nowadays. also explicitly clarifies this is a fix for iOS, and that it impacts touch laptops etc as well. lastly, renames the variable from "dropdown" to "backdrop" for clarity/consistency --- js/src/dropdown.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 644273a0a..96b7c8773 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -112,11 +112,12 @@ const Dropdown = (($) => { if ('ontouchstart' in document.documentElement && !$(parent).closest(Selector.NAVBAR_NAV).length) { - // if mobile we use a backdrop because click events don't delegate - const dropdown = document.createElement('div') - dropdown.className = ClassName.BACKDROP - $(dropdown).insertBefore(this) - $(dropdown).on('click', Dropdown._clearMenus) + // if touch-enabled device we use a backdrop because click events + // don't delegate on iOS - see https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + const backdrop = document.createElement('div') + backdrop.className = ClassName.BACKDROP + $(backdrop).insertBefore(this) + $(backdrop).on('click', Dropdown._clearMenus) } this.focus() -- cgit v1.2.3 From 6d64afe508bfd0bcfb5831a9a4708cef4ad88334 Mon Sep 17 00:00:00 2001 From: "Patrick H. Lauke" Date: Fri, 14 Apr 2017 09:19:00 +0100 Subject: Replace dropdown backdrop hack with cleaner JS-only hack * Replace backdrop with simple noop mouse listener As discussed in https://github.com/twbs/bootstrap/pull/22422 the current approach of injecting a backdrop (to work around iOS' broken event delegation for the `click` event) has annoying consequences on touch-enabled laptop/desktop devices. Instead of a backdrop `
`, here we simply add extra empty/noop mouse listeners to the immediate children of `` (and remove them when the dropdown is closed) in order to force iOS to properly bubble a `click` resulting from a tap (essentially, method 2 from https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html) This is sufficient (except in rare cases where the user does manage to tap on the body itself, rather than any child elements of body - which is not very likely in an iOS phone/tablet scenario for most layouts) to get iOS to get a grip and do the correct event bubbling/delegation, meaning the regular "click" event will bubble back to the `` when tapping outside of the dropdown, and the dropdown will close properly (just like it already does, even without this fix, in non-iOS touchscreen devices/browsers, like Chrome/Android and Windows on a touch laptop). This approach, though a bit hacky, has no impact on the DOM structure, and has no unforeseen side effects on touch-enabled laptops/desktops. And crucially, it works just fine in iOS. * Remove dropdown backdrop styles * Update doc for dropdowns and touch-enabled devices --- js/src/dropdown.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index b616186f3..812e718a8 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -43,13 +43,11 @@ const Dropdown = (($) => { } const ClassName = { - BACKDROP : 'dropdown-backdrop', DISABLED : 'disabled', SHOW : 'show' } const Selector = { - BACKDROP : '.dropdown-backdrop', DATA_TOGGLE : '[data-toggle="dropdown"]', FORM_CHILD : '.dropdown form', MENU : '.dropdown-menu', @@ -107,16 +105,13 @@ const Dropdown = (($) => { return false } - // set the backdrop only if the dropdown menu will be opened + // if this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement && !$(parent).closest(Selector.NAVBAR_NAV).length) { - - // if touch-enabled device we use a backdrop because click events - // don't delegate on iOS - see https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html - const backdrop = document.createElement('div') - backdrop.className = ClassName.BACKDROP - $(backdrop).insertBefore(this) - $(backdrop).on('click', Dropdown._clearMenus) + $('body').children().on('mouseover', '*', $.noop) } this.focus() @@ -192,10 +187,10 @@ const Dropdown = (($) => { continue } - // remove backdrop only if the dropdown menu will be hidden - const backdrop = $(parent).find(Selector.BACKDROP)[0] - if (backdrop) { - backdrop.parentNode.removeChild(backdrop) + // if this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + $('body').children().off('mouseover', '*', $.noop) } toggles[i].setAttribute('aria-expanded', 'false') -- cgit v1.2.3 From 1f37c536b2691e4a98310982f9b58ede506f11d8 Mon Sep 17 00:00:00 2001 From: "Patrick H. Lauke" Date: Thu, 20 Apr 2017 14:08:40 +0100 Subject: Tweak iOS hack for dropdown Tweak to https://github.com/twbs/bootstrap/pull/22426, where the wrong selector slipped through the net (selecting all of ``s grand-children rather than children) --- js/src/dropdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 812e718a8..eb536dc7d 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -111,7 +111,7 @@ const Dropdown = (($) => { // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement && !$(parent).closest(Selector.NAVBAR_NAV).length) { - $('body').children().on('mouseover', '*', $.noop) + $('body').children().on('mouseover', null, $.noop) } this.focus() @@ -190,7 +190,7 @@ const Dropdown = (($) => { // if this is a touch-enabled device we remove the extra // empty mouseover listeners we added for iOS support if ('ontouchstart' in document.documentElement) { - $('body').children().off('mouseover', '*', $.noop) + $('body').children().off('mouseover', null, $.noop) } toggles[i].setAttribute('aria-expanded', 'false') -- cgit v1.2.3 From 54a8ab40111dacdf50fad22e6f36d2801ba653c9 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Fri, 14 Apr 2017 11:25:53 +0200 Subject: Begin to use Popper for Dropdown --- js/src/dropdown.js | 98 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 8 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index eb536dc7d..a2b5561c2 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -10,6 +10,13 @@ import Util from './util' const Dropdown = (($) => { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + if (typeof Popper === 'undefined') { + throw new Error('Bootstrap dropdown require Popper (https://popper.js.org)') + } /** * ------------------------------------------------------------------------ @@ -55,6 +62,20 @@ const Dropdown = (($) => { VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)' } + const Default = { + animation : true, + trigger : 'click', + placement : 'bottom', + offset : '0 0' + } + + const DefaultType = { + animation : 'boolean', + trigger : 'string', + placement : 'string', + offset : 'string' + } + /** * ------------------------------------------------------------------------ @@ -64,8 +85,11 @@ const Dropdown = (($) => { class Dropdown { - constructor(element) { + constructor(element, config) { this._element = element + this._popper = null + this._config = this._getConfig(config) + this._menu = this._getMenuElement() this._addEventListeners() } @@ -77,16 +101,30 @@ const Dropdown = (($) => { return VERSION } + static get Default() { + return Default + } + + static get DefaultType() { + return DefaultType + } + // public toggle() { - if (this.disabled || $(this).hasClass(ClassName.DISABLED)) { + let context = $(this).data(DATA_KEY) + if (!context) { + context = new Dropdown(this) + $(this).data(DATA_KEY, context) + } + + if (context.disabled || $(this).hasClass(ClassName.DISABLED)) { return false } const parent = Dropdown._getParentFromElement(this) - const isActive = $(parent).hasClass(ClassName.SHOW) + const isActive = $(context._menu).hasClass(ClassName.SHOW) Dropdown._clearMenus() @@ -97,7 +135,7 @@ const Dropdown = (($) => { const relatedTarget = { relatedTarget : this } - const showEvent = $.Event(Event.SHOW, relatedTarget) + const showEvent = $.Event(Event.SHOW, relatedTarget) $(parent).trigger(showEvent) @@ -105,6 +143,13 @@ const Dropdown = (($) => { return false } + this._popper = new Popper(this, context._menu, { + placement : context._config.placement, + offsets : { + popper : context._config.offset + } + }) + // if this is a touch-enabled device we add extra // empty mouseover listeners to the body's immediate children; // only needed because of broken event delegation on iOS @@ -117,8 +162,10 @@ const Dropdown = (($) => { this.focus() this.setAttribute('aria-expanded', true) - $(parent).toggleClass(ClassName.SHOW) - $(parent).trigger($.Event(Event.SHOWN, relatedTarget)) + $(context._menu).toggleClass(ClassName.SHOW) + $(parent) + .toggleClass(ClassName.SHOW) + .trigger($.Event(Event.SHOWN, relatedTarget)) return false } @@ -127,6 +174,10 @@ const Dropdown = (($) => { $.removeData(this._element, DATA_KEY) $(this._element).off(EVENT_KEY) this._element = null + this._menu = null + if (this._popper !== null) { + this._popper.destroy() + } } @@ -136,15 +187,40 @@ const Dropdown = (($) => { $(this._element).on(Event.CLICK, this.toggle) } + _getConfig(config) { + config = $.extend( + {}, + this.constructor.Default, + $(this._element).data(), + config + ) + + Util.typeCheckConfig( + NAME, + config, + this.constructor.DefaultType + ) + + return config + } + + _getMenuElement() { + if (!this._menu) { + let parent = Dropdown._getParentFromElement(this._element) + this._menu = $(parent).find(Selector.MENU)[0] + } + return this._menu + } // static static _jQueryInterface(config) { return this.each(function () { let data = $(this).data(DATA_KEY) + let _config = typeof config === 'object' ? config : null if (!data) { - data = new Dropdown(this) + data = new Dropdown(this, _config) $(this).data(DATA_KEY, data) } @@ -164,13 +240,18 @@ const Dropdown = (($) => { } const toggles = $.makeArray($(Selector.DATA_TOGGLE)) - for (let i = 0; i < toggles.length; i++) { const parent = Dropdown._getParentFromElement(toggles[i]) + let context = $(toggles[i]).data(DATA_KEY) const relatedTarget = { relatedTarget : toggles[i] } + if (!context) { + continue + } + + let dropdownMenu = context._menu if (!$(parent).hasClass(ClassName.SHOW)) { continue } @@ -195,6 +276,7 @@ const Dropdown = (($) => { toggles[i].setAttribute('aria-expanded', 'false') + $(dropdownMenu).removeClass(ClassName.SHOW) $(parent) .removeClass(ClassName.SHOW) .trigger($.Event(Event.HIDDEN, relatedTarget)) -- cgit v1.2.3 From c21a2b0d92ca78b63bb9ec21ca0fbf9e9206a18e Mon Sep 17 00:00:00 2001 From: Johann-S Date: Fri, 14 Apr 2017 12:05:54 +0200 Subject: Remove constraints option and check every options --- js/src/dropdown.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index a2b5561c2..ad5ee8d9f 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -63,17 +63,13 @@ const Dropdown = (($) => { } const Default = { - animation : true, - trigger : 'click', placement : 'bottom', - offset : '0 0' + offset : {} } const DefaultType = { - animation : 'boolean', - trigger : 'string', placement : 'string', - offset : 'string' + offset : 'number' } @@ -145,8 +141,10 @@ const Dropdown = (($) => { this._popper = new Popper(this, context._menu, { placement : context._config.placement, - offsets : { - popper : context._config.offset + modifiers : { + offset : { + offset : this.config.offset + } } }) -- cgit v1.2.3 From 69de65180f750cadf5f96a774d2524f69d19beab Mon Sep 17 00:00:00 2001 From: Johann-S Date: Fri, 14 Apr 2017 13:30:55 +0200 Subject: Fix unit tests + Update Popper to 1.6.0 --- js/src/dropdown.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index ad5ee8d9f..ce25e9671 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -1,3 +1,5 @@ +/* global Popper */ + import Util from './util' @@ -64,7 +66,7 @@ const Dropdown = (($) => { const Default = { placement : 'bottom', - offset : {} + offset : 0 } const DefaultType = { @@ -143,7 +145,7 @@ const Dropdown = (($) => { placement : context._config.placement, modifiers : { offset : { - offset : this.config.offset + offset : context._config.offset } } }) @@ -204,7 +206,7 @@ const Dropdown = (($) => { _getMenuElement() { if (!this._menu) { - let parent = Dropdown._getParentFromElement(this._element) + const parent = Dropdown._getParentFromElement(this._element) this._menu = $(parent).find(Selector.MENU)[0] } return this._menu @@ -215,7 +217,7 @@ const Dropdown = (($) => { static _jQueryInterface(config) { return this.each(function () { let data = $(this).data(DATA_KEY) - let _config = typeof config === 'object' ? config : null + const _config = typeof config === 'object' ? config : null if (!data) { data = new Dropdown(this, _config) @@ -240,7 +242,7 @@ const Dropdown = (($) => { const toggles = $.makeArray($(Selector.DATA_TOGGLE)) for (let i = 0; i < toggles.length; i++) { const parent = Dropdown._getParentFromElement(toggles[i]) - let context = $(toggles[i]).data(DATA_KEY) + const context = $(toggles[i]).data(DATA_KEY) const relatedTarget = { relatedTarget : toggles[i] } @@ -249,7 +251,7 @@ const Dropdown = (($) => { continue } - let dropdownMenu = context._menu + const dropdownMenu = context._menu if (!$(parent).hasClass(ClassName.SHOW)) { continue } -- cgit v1.2.3 From 53ee455bc735af9fc54de586ed4d23ac56b69ab8 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Mon, 17 Apr 2017 14:26:40 +0200 Subject: Handle dropup for Dropdown --- js/src/dropdown.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index ce25e9671..33171cf20 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -64,8 +64,13 @@ const Dropdown = (($) => { VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled)' } + const AttachmentMap = { + TOP : 'top', + BOTTOM : 'bottom' + } + const Default = { - placement : 'bottom', + placement : AttachmentMap.BOTTOM, offset : 0 } @@ -141,8 +146,10 @@ const Dropdown = (($) => { return false } + // Handle dropup + const dropdownPlacement = $(this).parent().hasClass('dropup') ? AttachmentMap.TOP : context._config.placement this._popper = new Popper(this, context._menu, { - placement : context._config.placement, + placement : dropdownPlacement, modifiers : { offset : { offset : context._config.offset -- cgit v1.2.3 From d5fabf8de50710c5a09ecd91f4b9e961e67d7937 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Tue, 18 Apr 2017 14:02:24 +0200 Subject: Remove totaly Tether from documentation + dependencies --- js/src/dropdown.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 33171cf20..d678a3a02 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -17,7 +17,7 @@ const Dropdown = (($) => { * Popper - https://popper.js.org */ if (typeof Popper === 'undefined') { - throw new Error('Bootstrap dropdown require Popper (https://popper.js.org)') + throw new Error('Bootstrap dropdown require Popper.js (https://popper.js.org)') } /** @@ -75,8 +75,8 @@ const Dropdown = (($) => { } const DefaultType = { - placement : 'string', - offset : 'number' + placement : 'string', + offset : '(number|string)' } -- cgit v1.2.3 From 18e4e851e2829aedc6d1ff2b703ba5cae2c0a288 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Wed, 19 Apr 2017 10:20:50 +0200 Subject: Better placement for Dropdown + Handle flip of Tooltip/Popover --- js/src/dropdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index d678a3a02..613a23812 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -65,8 +65,8 @@ const Dropdown = (($) => { } const AttachmentMap = { - TOP : 'top', - BOTTOM : 'bottom' + TOP : 'top-start', + BOTTOM : 'bottom-start' } const Default = { -- cgit v1.2.3 From e5a0471b0b618409e30529a5e02933d5a74a4cc5 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Wed, 19 Apr 2017 15:08:06 +0200 Subject: Add an update method to allow to update position for Tooltip/Popover/Dropdown manually --- js/src/dropdown.js | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 613a23812..0c082edd6 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -185,8 +185,14 @@ const Dropdown = (($) => { if (this._popper !== null) { this._popper.destroy() } + this._popper = null } + update() { + if (this._popper !== null) { + this._popper.scheduleUpdate() + } + } // private -- cgit v1.2.3 From 4f882a840c9f2b62f543c32d56b3e904582ced90 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Fri, 21 Apr 2017 10:49:04 +0200 Subject: Allow to disable flip behaviour on Dropdown + documentation --- js/src/dropdown.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 0c082edd6..71247728a 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -71,12 +71,14 @@ const Dropdown = (($) => { const Default = { placement : AttachmentMap.BOTTOM, - offset : 0 + offset : 0, + flip : true } const DefaultType = { placement : 'string', - offset : '(number|string)' + offset : '(number|string)', + flip : 'boolean' } @@ -153,6 +155,9 @@ const Dropdown = (($) => { modifiers : { offset : { offset : context._config.offset + }, + flip : { + enabled : context._config.flip } } }) @@ -201,6 +206,11 @@ const Dropdown = (($) => { } _getConfig(config) { + const elementData = $(this._element).data() + if (elementData.placement !== undefined) { + elementData.placement = AttachmentMap[elementData.placement.toUpperCase()] + } + config = $.extend( {}, this.constructor.Default, -- cgit v1.2.3 From 0ae9d28ba3e9c4f119a3ba0b370717993350be2c Mon Sep 17 00:00:00 2001 From: Johann-S Date: Fri, 5 May 2017 21:22:55 +0200 Subject: Add fallbackPlacement option for Tooltip and Popover --- js/src/dropdown.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 71247728a..1da2098dd 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -157,7 +157,8 @@ const Dropdown = (($) => { offset : context._config.offset }, flip : { - enabled : context._config.flip + enabled : context._config.flip, + behavior : [AttachmentMap.TOP, AttachmentMap.BOTTOM] } } }) -- cgit v1.2.3 From 0cdf176f7a4cdc66aa6c84898c6795c8fd4bc58a Mon Sep 17 00:00:00 2001 From: Johann-S Date: Thu, 11 May 2017 11:29:46 +0200 Subject: Use _jQueryInterface for Dropdown to call toggle method --- js/src/dropdown.js | 54 ++++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 1da2098dd..39a4a86ca 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -114,51 +114,43 @@ const Dropdown = (($) => { return DefaultType } - // public toggle() { - let context = $(this).data(DATA_KEY) - if (!context) { - context = new Dropdown(this) - $(this).data(DATA_KEY, context) - } - - if (context.disabled || $(this).hasClass(ClassName.DISABLED)) { - return false + if (this._element.disabled || $(this._element).hasClass(ClassName.DISABLED)) { + return } - const parent = Dropdown._getParentFromElement(this) - const isActive = $(context._menu).hasClass(ClassName.SHOW) + const parent = Dropdown._getParentFromElement(this._element) + const isActive = $(this._menu).hasClass(ClassName.SHOW) Dropdown._clearMenus() if (isActive) { - return false + return } const relatedTarget = { - relatedTarget : this + relatedTarget : this._element } const showEvent = $.Event(Event.SHOW, relatedTarget) $(parent).trigger(showEvent) if (showEvent.isDefaultPrevented()) { - return false + return } // Handle dropup - const dropdownPlacement = $(this).parent().hasClass('dropup') ? AttachmentMap.TOP : context._config.placement - this._popper = new Popper(this, context._menu, { + const dropdownPlacement = $(this._element).parent().hasClass('dropup') ? AttachmentMap.TOP : this._config.placement + this._popper = new Popper(this._element, this._menu, { placement : dropdownPlacement, modifiers : { offset : { - offset : context._config.offset + offset : this._config.offset }, flip : { - enabled : context._config.flip, - behavior : [AttachmentMap.TOP, AttachmentMap.BOTTOM] + enabled : this._config.flip } } }) @@ -172,15 +164,13 @@ const Dropdown = (($) => { $('body').children().on('mouseover', null, $.noop) } - this.focus() - this.setAttribute('aria-expanded', true) + this._element.focus() + this._element.setAttribute('aria-expanded', true) - $(context._menu).toggleClass(ClassName.SHOW) + $(this._menu).toggleClass(ClassName.SHOW) $(parent) .toggleClass(ClassName.SHOW) .trigger($.Event(Event.SHOWN, relatedTarget)) - - return false } dispose() { @@ -203,7 +193,11 @@ const Dropdown = (($) => { // private _addEventListeners() { - $(this._element).on(Event.CLICK, this.toggle) + $(this._element).on(Event.CLICK, (event) => { + event.preventDefault() + event.stopPropagation() + this.toggle() + }) } _getConfig(config) { @@ -252,7 +246,7 @@ const Dropdown = (($) => { if (data[config] === undefined) { throw new Error(`No method named "${config}"`) } - data[config].call(this) + data[config]() } }) } @@ -266,7 +260,7 @@ const Dropdown = (($) => { const toggles = $.makeArray($(Selector.DATA_TOGGLE)) for (let i = 0; i < toggles.length; i++) { const parent = Dropdown._getParentFromElement(toggles[i]) - const context = $(toggles[i]).data(DATA_KEY) + const context = $(toggles[i]).data(DATA_KEY) const relatedTarget = { relatedTarget : toggles[i] } @@ -382,7 +376,11 @@ const Dropdown = (($) => { .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, Dropdown.prototype.toggle) + .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { + event.preventDefault() + event.stopPropagation() + Dropdown._jQueryInterface.call($(this), 'toggle') + }) .on(Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => { e.stopPropagation() }) -- cgit v1.2.3 From b36d8ae6cb9dcd2e4183202a747d53706f1c1c8a Mon Sep 17 00:00:00 2001 From: Johann-S Date: Wed, 17 May 2017 22:03:11 +0200 Subject: Use popper to align dropdown menu instead of using css with important --- js/src/dropdown.js | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 39a4a86ca..846746384 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -52,8 +52,11 @@ const Dropdown = (($) => { } const ClassName = { - DISABLED : 'disabled', - SHOW : 'show' + DISABLED : 'disabled', + SHOW : 'show', + DROPUP : 'dropup', + MENURIGHT : 'dropdown-menu-right', + MENULEFT : 'dropdown-menu-left' } const Selector = { @@ -142,7 +145,7 @@ const Dropdown = (($) => { } // Handle dropup - const dropdownPlacement = $(this._element).parent().hasClass('dropup') ? AttachmentMap.TOP : this._config.placement + const dropdownPlacement = $(this._element).parent().hasClass(ClassName.DROPUP) ? AttachmentMap.TOP : this._config.placement this._popper = new Popper(this._element, this._menu, { placement : dropdownPlacement, modifiers : { @@ -151,6 +154,11 @@ const Dropdown = (($) => { }, flip : { enabled : this._config.flip + }, + beforeApplyStyle: { + order: 899, // 900 is the order of applyStyle + enabled: true, + fn: this._beforePopperApplyStyle } } }) @@ -230,6 +238,23 @@ const Dropdown = (($) => { return this._menu } + _beforePopperApplyStyle(data) { + if ($(data.instance.popper).hasClass(ClassName.MENURIGHT)) { + data.styles = { + right: 0, + left: 'auto' + } + } + + if ($(data.instance.popper).hasClass(ClassName.MENULEFT)) { + data.styles = { + right: 'auto', + left: 0 + } + } + return data + } + // static static _jQueryInterface(config) { -- cgit v1.2.3 From 70f4a30defaa1d3269385ce713a5926ab84d7727 Mon Sep 17 00:00:00 2001 From: Johann-S Date: Mon, 22 May 2017 16:14:10 +0200 Subject: Better management of dropdown/dropup with alignment --- js/src/dropdown.js | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'js/src/dropdown.js') diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 846746384..acc3ed453 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -68,8 +68,10 @@ const Dropdown = (($) => { } const AttachmentMap = { - TOP : 'top-start', - BOTTOM : 'bottom-start' + TOP : 'top-start', + TOPEND : 'top-end', + BOTTOM : 'bottom-start', + BOTTOMEND : 'bottom-end' } const Default = { @@ -144,21 +146,21 @@ const Dropdown = (($) => { return } - // Handle dropup - const dropdownPlacement = $(this._element).parent().hasClass(ClassName.DROPUP) ? AttachmentMap.TOP : this._config.placement - this._popper = new Popper(this._element, this._menu, { - placement : dropdownPlacement, + let element = this._element + // for dropup with alignment we use the parent as popper container + if ($(parent).hasClass(ClassName.DROPUP)) { + if ($(this._menu).hasClass(ClassName.MENULEFT) || $(this._menu).hasClass(ClassName.MENURIGHT)) { + element = parent + } + } + this._popper = new Popper(element, this._menu, { + placement : this._getPlacement(), modifiers : { offset : { offset : this._config.offset }, flip : { enabled : this._config.flip - }, - beforeApplyStyle: { - order: 899, // 900 is the order of applyStyle - enabled: true, - fn: this._beforePopperApplyStyle } } }) @@ -238,21 +240,23 @@ const Dropdown = (($) => { return this._menu } - _beforePopperApplyStyle(data) { - if ($(data.instance.popper).hasClass(ClassName.MENURIGHT)) { - data.styles = { - right: 0, - left: 'auto' + _getPlacement() { + const $parentDropdown = $(this._element).parent() + let placement = this._config.placement + + // Handle dropup + if ($parentDropdown.hasClass(ClassName.DROPUP) || this._config.placement === AttachmentMap.TOP) { + placement = AttachmentMap.TOP + if ($(this._menu).hasClass(ClassName.MENURIGHT)) { + placement = AttachmentMap.TOPEND } } - - if ($(data.instance.popper).hasClass(ClassName.MENULEFT)) { - data.styles = { - right: 'auto', - left: 0 + else { + if ($(this._menu).hasClass(ClassName.MENURIGHT)) { + placement = AttachmentMap.BOTTOMEND } } - return data + return placement } // static -- cgit v1.2.3