From f2f05f451ac12dce8e87cd7ab9fcdc7886f9f2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:06:45 +0200 Subject: [PATCH 01/10] Make dropdown show/hide events cancelable --- src/mixins/dropdown.js | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 29e39d7209c..2598212afc4 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -4,6 +4,7 @@ import listenOnRootMixin from './listen-on-root' import { from as arrayFrom } from '../utils/array' import { assign } from '../utils/object' import KeyCodes from '../utils/key-codes' +import BvEvent from '../utils/bv-event.class' import warn from '../utils/warn' import { isVisible, closest, selectAll, getAttr, eventOn, eventOff } from '../utils/dom' @@ -97,19 +98,29 @@ export default { this.removePopper() }, watch: { - visible (state, old) { - if (state === old) { - // Avoid duplicated emits - return - } - if (state) { - this.showMenu() - } else { - this.hideMenu() + visible (newValue, oldValue) { + if (newValue !== oldValue) { + const evtName = newValue ? 'show' : 'hide'; + const evt = new BvEvent(evtName, { + cancelable: true, + vueTarget: this, + target: this.$refs.menu, + relatedTarget: null + }) + this.emitEvent(evt) + if (evt.defaultPrevented) { + // Don't show if canceled + return + } + if (evtName === 'show') { + this.showMenu() + } else { + this.hideMenu() + } } }, - disabled (state, old) { - if (state !== old && state && this.visible) { + disabled (newValue, oldValue) { + if (newValue !== oldValue && newValue && this.visible) { // Hide dropdown if disabled changes to true this.visible = false } @@ -125,8 +136,6 @@ export default { if (this.disabled) { return } - // TODO: move emit show to visible watcher, to allow cancelling of show - this.$emit('show') // Ensure other menus are closed this.emitOnRoot('bv::dropdown::shown', this) @@ -157,8 +166,6 @@ export default { this.$nextTick(this.focusFirstItem) }, hideMenu () { - // TODO: move emit hide to visible watcher, to allow cancelling of hide - this.$emit('hide') this.setTouchStart(false) this.emitOnRoot('bv::dropdown::hidden', this) this.$emit('hidden') @@ -258,8 +265,6 @@ export default { // We only toggle on Click, Enter, Space, and Arrow Down return } - evt.preventDefault() - evt.stopPropagation() if (this.disabled) { this.visible = false return From 20a1e3a6ec825edecc29abb262fd86aa00650b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:07:27 +0200 Subject: [PATCH 02/10] Make dropdown toggle click cancelable --- src/mixins/dropdown.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 2598212afc4..97e8e2ca6f6 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -269,6 +269,13 @@ export default { this.visible = false return } + this.$emit('toggle', evt) + if (evt.defaultPrevented) { + // Don't show if canceled + return + } + evt.preventDefault() + evt.stopPropagation() // Toggle visibility this.visible = !this.visible }, From 0f63f152a8a2bdd3f6c11551b0a2b836c9a69171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:12:24 +0200 Subject: [PATCH 03/10] Not a const --- src/mixins/dropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 97e8e2ca6f6..c40fe49e8d8 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -101,7 +101,7 @@ export default { visible (newValue, oldValue) { if (newValue !== oldValue) { const evtName = newValue ? 'show' : 'hide'; - const evt = new BvEvent(evtName, { + let evt = new BvEvent(evtName, { cancelable: true, vueTarget: this, target: this.$refs.menu, From 94455a869742ef7acb1ab9fb803fa97a01e703e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:14:18 +0200 Subject: [PATCH 04/10] Fix lint --- src/mixins/dropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index c40fe49e8d8..64712d2f557 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -100,7 +100,7 @@ export default { watch: { visible (newValue, oldValue) { if (newValue !== oldValue) { - const evtName = newValue ? 'show' : 'hide'; + const evtName = newValue ? 'show' : 'hide' let evt = new BvEvent(evtName, { cancelable: true, vueTarget: this, From 6c2217e148c9694eb966d41a728660803dfa77c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:16:25 +0200 Subject: [PATCH 05/10] Better comments --- src/mixins/dropdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 64712d2f557..2633e72039c 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -109,7 +109,7 @@ export default { }) this.emitEvent(evt) if (evt.defaultPrevented) { - // Don't show if canceled + // Exit if canceled return } if (evtName === 'show') { @@ -271,7 +271,7 @@ export default { } this.$emit('toggle', evt) if (evt.defaultPrevented) { - // Don't show if canceled + // Exit if canceled return } evt.preventDefault() From 7334b67e3b8e4143fbba84ea051e1c28f923dbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:27:52 +0200 Subject: [PATCH 06/10] Improve dropdown event documentation --- src/components/dropdown/package.json | 32 ++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/components/dropdown/package.json b/src/components/dropdown/package.json index 34d5c55cb3d..93329b09fff 100755 --- a/src/components/dropdown/package.json +++ b/src/components/dropdown/package.json @@ -11,23 +11,47 @@ "bDropdownDivider" ], "events": [ + { + "event": "show", + "description": "Emitted just before dropdown is shown. Cancelable.", + "args": [ + { + "arg": "bvEvt", + "description": "BvEvent object. Call bvEvt.preventDefault() to cancel show." + } + ] + }, { "event": "shown", - "description": "Emitted When dropdown is shown" + "description": "Emitted when dropdown is shown." + }, + { + "event": "hide", + "description": "Emitted just before dropdown is hidden. Cancelable.", + "args": [ + { + "arg": "bvEvt", + "description": "BvEvent object. Call bvEvt.preventDefault() to cancel hide." + } + ] }, { "event": "hidden", - "description": "Emitted When dropdown is hidden" + "description": "Emitted when dropdown is hidden." + }, + { + "event": "toggle", + "description": "Emitted when toggle button is clicked." }, { "event": "click", - "description": "Emitted when split button clicked in split mode." + "description": "Emitted when split button is clicked in split mode." } ], "slots": [ { "name": "button-content", - "description": "Can be used to implement custom text with icons and more styling" + "description": "Can be used to implement custom text with icons and more styling." }, { "name": "text", From a7615e9a7278817e6566eff32233a81d82080d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:36:15 +0200 Subject: [PATCH 07/10] Add missing `emitEvent` method --- src/mixins/dropdown.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 2633e72039c..dc30f3f87a2 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -101,14 +101,14 @@ export default { visible (newValue, oldValue) { if (newValue !== oldValue) { const evtName = newValue ? 'show' : 'hide' - let evt = new BvEvent(evtName, { + let bvEvt = new BvEvent(evtName, { cancelable: true, vueTarget: this, target: this.$refs.menu, relatedTarget: null }) - this.emitEvent(evt) - if (evt.defaultPrevented) { + this.emitEvent(bvEvt) + if (bvEvt.defaultPrevented) { // Exit if canceled return } @@ -132,6 +132,12 @@ export default { } }, methods: { + // Event emitter + emitEvent (bvEvt) { + const type = bvEvt.type + this.$emit(type, bvEvt) + this.$root.$emit(`bv::dropdown::${type}`, bvEvt) + }, showMenu () { if (this.disabled) { return From 9f2ced0a5d665ebab5f2d4b1c45f6b926a48409e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 02:39:22 +0200 Subject: [PATCH 08/10] Use `emitOnRoot` method --- src/mixins/dropdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index dc30f3f87a2..82993d3928b 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -136,7 +136,7 @@ export default { emitEvent (bvEvt) { const type = bvEvt.type this.$emit(type, bvEvt) - this.$root.$emit(`bv::dropdown::${type}`, bvEvt) + this.emitOnRoot(`bv::dropdown::${type}`, bvEvt) }, showMenu () { if (this.disabled) { From 3d88e67aacef428b37c8d75560dd38c3cf6cff72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Wed, 9 May 2018 18:10:57 +0200 Subject: [PATCH 09/10] Make sure to actuelly reset the `visible` value to keep the menu state --- src/mixins/dropdown.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 82993d3928b..19bd15ef174 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -109,7 +109,8 @@ export default { }) this.emitEvent(bvEvt) if (bvEvt.defaultPrevented) { - // Exit if canceled + // Reset value and exit if canceled + this.visible = oldValue return } if (evtName === 'show') { From 57bce17da35141193a6f29910fba73572c5a8f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Fri, 11 May 2018 10:29:48 +0200 Subject: [PATCH 10/10] Fix duplicate event triggering when resetting the `visible` value --- src/mixins/dropdown.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mixins/dropdown.js b/src/mixins/dropdown.js index 19bd15ef174..7001d2cc208 100644 --- a/src/mixins/dropdown.js +++ b/src/mixins/dropdown.js @@ -69,7 +69,8 @@ export default { data () { return { visible: false, - inNavbar: null + inNavbar: null, + visibleChangePrevented: false } }, created () { @@ -99,6 +100,11 @@ export default { }, watch: { visible (newValue, oldValue) { + if (this.visibleChangePrevented) { + this.visibleChangePrevented = false + return + } + if (newValue !== oldValue) { const evtName = newValue ? 'show' : 'hide' let bvEvt = new BvEvent(evtName, { @@ -110,6 +116,7 @@ export default { this.emitEvent(bvEvt) if (bvEvt.defaultPrevented) { // Reset value and exit if canceled + this.visibleChangePrevented = true this.visible = oldValue return }