Skip to content

Commit bb50510

Browse files
committed
fix(b-toast): show and hide handling during active transitions [WIP]
1 parent 1e6b369 commit bb50510

File tree

7 files changed

+334
-230
lines changed

7 files changed

+334
-230
lines changed

src/components/modal/helpers/bv-modal.js

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { NAME_MODAL, NAME_MSG_BOX } from '../../../constants/components'
33
import {
44
EVENT_NAME_HIDDEN,
55
EVENT_NAME_HIDE,
6+
EVENT_NAME_SHOW,
7+
EVENT_NAME_TOGGLE,
68
HOOK_EVENT_NAME_BEFORE_DESTROY,
79
HOOK_EVENT_NAME_DESTROYED
810
} from '../../../constants/events'
@@ -21,6 +23,7 @@ import {
2123
readonlyDescriptor
2224
} from '../../../utils/object'
2325
import { pluginFactory } from '../../../utils/plugins'
26+
import { pluckProps } from '../../../utils/props'
2427
import { warn, warnNotClient, warnNoPromiseSupport } from '../../../utils/warn'
2528
import { BModal, props as modalProps } from '../modal'
2629

@@ -51,16 +54,6 @@ const propsToSlots = {
5154

5255
// --- Helper methods ---
5356

54-
// Method to filter only recognized props that are not undefined
55-
const filterOptions = options => {
56-
return BASE_PROPS.reduce((memo, key) => {
57-
if (!isUndefined(options[key])) {
58-
memo[key] = options[key]
59-
}
60-
return memo
61-
}, {})
62-
}
63-
6457
// Method to install `$bvModal` VM injection
6558
const plugin = Vue => {
6659
// Create a private sub-component that extends BModal
@@ -116,7 +109,7 @@ const plugin = Vue => {
116109
parent: $parent,
117110
// Preset the prop values
118111
propsData: {
119-
...filterOptions(getComponentConfig(NAME_MODAL)),
112+
...pluckProps(BASE_PROPS, getComponentConfig(NAME_MODAL)),
120113
// Defaults that user can override
121114
hideHeaderClose: true,
122115
hideHeader: !(props.title || props.titleHtml),
@@ -166,7 +159,7 @@ const plugin = Vue => {
166159

167160
// Private utility method to open a user defined message box and returns a promise.
168161
// Not to be used directly by consumers, as this method may change calling syntax
169-
const makeMsgBox = ($parent, content, options = {}, resolver = null) => {
162+
const makeMsgBox = ($parent, content, props = {}, resolver = null) => {
170163
if (
171164
!content ||
172165
warnNoPromiseSupport(PROP_NAME) ||
@@ -176,7 +169,14 @@ const plugin = Vue => {
176169
/* istanbul ignore next */
177170
return
178171
}
179-
return asyncMsgBox($parent, { ...filterOptions(options), msgBoxContent: content }, resolver)
172+
return asyncMsgBox(
173+
$parent,
174+
{
175+
...pluckProps(BASE_PROPS, props),
176+
msgBoxContent: content
177+
},
178+
resolver
179+
)
180180
}
181181

182182
// BvModal instance class
@@ -193,17 +193,24 @@ const plugin = Vue => {
193193

194194
// --- Instance methods ---
195195

196-
// Show modal with the specified ID args are for future use
196+
// Show modal with the specified ID
197197
show(id, ...args) {
198-
if (id && this._root) {
199-
this._root.$emit(getRootActionEventName(NAME_MODAL, 'show'), id, ...args)
198+
if (id) {
199+
this._root.$emit(getRootActionEventName(NAME_MODAL, EVENT_NAME_SHOW), id, ...args)
200200
}
201201
}
202202

203-
// Hide modal with the specified ID args are for future use
203+
// Hide modal with the specified ID
204204
hide(id, ...args) {
205-
if (id && this._root) {
206-
this._root.$emit(getRootActionEventName(NAME_MODAL, 'hide'), id, ...args)
205+
if (id) {
206+
this._root.$emit(getRootActionEventName(NAME_MODAL, EVENT_NAME_HIDE), id, ...args)
207+
}
208+
}
209+
210+
// Toggle modal with the specified ID
211+
toggle(id, ...args) {
212+
if (id) {
213+
this._root.$emit(getRootActionEventName(NAME_MODAL, EVENT_NAME_TOGGLE), id, ...args)
207214
}
208215
}
209216

@@ -212,38 +219,44 @@ const plugin = Vue => {
212219
// should have a Polyfill loaded (which they need anyways for IE 11 support)
213220

214221
// Open a message box with OK button only and returns a promise
215-
msgBoxOk(message, options = {}) {
216-
// Pick the modal props we support from options
217-
const props = {
218-
...options,
219-
// Add in overrides and our content prop
220-
okOnly: true,
221-
okDisabled: false,
222-
hideFooter: false,
223-
msgBoxContent: message
224-
}
225-
return makeMsgBox(this._vm, message, props, () => {
226-
// Always resolve to true for OK
227-
return true
228-
})
222+
msgBoxOk(message, props = {}) {
223+
return makeMsgBox(
224+
this._vm,
225+
message,
226+
{
227+
...props,
228+
// Add in overrides and our content prop
229+
okOnly: true,
230+
okDisabled: false,
231+
hideFooter: false,
232+
msgBoxContent: message
233+
},
234+
() => {
235+
// Always resolve to true for OK
236+
return true
237+
}
238+
)
229239
}
230240

231241
// Open a message box modal with OK and CANCEL buttons
232242
// and returns a promise
233-
msgBoxConfirm(message, options = {}) {
234-
// Set the modal props we support from options
235-
const props = {
236-
...options,
237-
// Add in overrides and our content prop
238-
okOnly: false,
239-
okDisabled: false,
240-
cancelDisabled: false,
241-
hideFooter: false
242-
}
243-
return makeMsgBox(this._vm, message, props, bvModalEvent => {
244-
const trigger = bvModalEvent.trigger
245-
return trigger === 'ok' ? true : trigger === 'cancel' ? false : null
246-
})
243+
msgBoxConfirm(message, props = {}) {
244+
return makeMsgBox(
245+
this._vm,
246+
message,
247+
{
248+
...props,
249+
// Add in overrides and our content prop
250+
okOnly: false,
251+
okDisabled: false,
252+
cancelDisabled: false,
253+
hideFooter: false
254+
},
255+
bvModalEvent => {
256+
const trigger = bvModalEvent.trigger
257+
return trigger === 'ok' ? true : trigger === 'cancel' ? false : null
258+
}
259+
)
247260
}
248261
}
249262

src/components/modal/modal.js

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,8 @@ export const BModal = /*#__PURE__*/ Vue.extend({
369369
// Listen for `bv:modal::show events`, and close ourselves if the
370370
// opening modal not us
371371
this.listenOnRoot(getRootEventName(NAME_MODAL, EVENT_NAME_SHOW), this.modalListener)
372-
// Initially show modal?
373-
if (this[MODEL_PROP_NAME] === true) {
372+
// Initially show modal
373+
if (this[MODEL_PROP_NAME]) {
374374
this.$nextTick(this.show)
375375
}
376376
},
@@ -384,6 +384,41 @@ export const BModal = /*#__PURE__*/ Vue.extend({
384384
}
385385
},
386386
methods: {
387+
// Private method to get the current document active element
388+
getActiveElement() {
389+
// Returning focus to `document.body` may cause unwanted scrolls,
390+
// so we exclude setting focus on body
391+
const activeElement = getActiveElement(IS_BROWSER ? [document.body] : [])
392+
// Preset the fallback return focus value if it is not set
393+
// `document.activeElement` should be the trigger element that was clicked or
394+
// in the case of using the v-model, which ever element has current focus
395+
// Will be overridden by some commands such as toggle, etc.
396+
// Note: On IE 11, `document.activeElement` may be `null`
397+
// So we test it for truthiness first
398+
// https://github.com/bootstrap-vue/bootstrap-vue/issues/3206
399+
return activeElement && activeElement.focus ? activeElement : null
400+
},
401+
buildEvent(type, options = {}) {
402+
return new BvModalEvent(type, {
403+
// Default options
404+
cancelable: false,
405+
target: this.$refs.modal || this.$el || null,
406+
relatedTarget: null,
407+
trigger: null,
408+
// Supplied options
409+
...options,
410+
// Options that can't be overridden
411+
vueTarget: this,
412+
componentId: this.modalId
413+
})
414+
},
415+
emitEvent(bvEvent) {
416+
const { type } = bvEvent
417+
// We emit on `$root` first in case a global listener wants to cancel
418+
// the event first before the instance emits its event
419+
this.emitOnRoot(getRootEventName(NAME_MODAL, type), bvEvent, bvEvent.componentId)
420+
this.$emit(type, bvEvent)
421+
},
387422
setObserver(on = false) {
388423
this.$_observer && this.$_observer.disconnect()
389424
this.$_observer = null
@@ -395,32 +430,16 @@ export const BModal = /*#__PURE__*/ Vue.extend({
395430
)
396431
}
397432
},
398-
// Private method to update the v-model
433+
// Private method to update the `v-model`
399434
updateModel(value) {
400435
if (value !== this[MODEL_PROP_NAME]) {
401436
this.$emit(MODEL_EVENT_NAME, value)
402437
}
403438
},
404-
// Private method to create a BvModalEvent object
405-
buildEvent(type, options = {}) {
406-
return new BvModalEvent(type, {
407-
// Default options
408-
cancelable: false,
409-
target: this.$refs.modal || this.$el || null,
410-
relatedTarget: null,
411-
trigger: null,
412-
// Supplied options
413-
...options,
414-
// Options that can't be overridden
415-
vueTarget: this,
416-
componentId: this.modalId
417-
})
418-
},
419-
// Public method to show modal
420439
show() {
440+
// If already open, or in the process of opening, do nothing
441+
/* istanbul ignore next */
421442
if (this.isVisible || this.isOpening) {
422-
// If already open, or in the process of opening, do nothing
423-
/* istanbul ignore next */
424443
return
425444
}
426445
/* istanbul ignore next */
@@ -448,10 +467,10 @@ export const BModal = /*#__PURE__*/ Vue.extend({
448467
// Show the modal
449468
this.doShow()
450469
},
451-
// Public method to hide modal
452470
hide(trigger = '') {
471+
// If already closed, or in the process of closing, do nothing
472+
/* istanbul ignore next */
453473
if (!this.isVisible || this.isClosing) {
454-
/* istanbul ignore next */
455474
return
456475
}
457476
this.isClosing = true
@@ -482,7 +501,6 @@ export const BModal = /*#__PURE__*/ Vue.extend({
482501
// Update the v-model
483502
this.updateModel(false)
484503
},
485-
// Public method to toggle modal visibility
486504
toggle(triggerEl) {
487505
if (triggerEl) {
488506
this.$_returnFocus = triggerEl
@@ -493,20 +511,6 @@ export const BModal = /*#__PURE__*/ Vue.extend({
493511
this.show()
494512
}
495513
},
496-
// Private method to get the current document active element
497-
getActiveElement() {
498-
// Returning focus to `document.body` may cause unwanted scrolls,
499-
// so we exclude setting focus on body
500-
const activeElement = getActiveElement(IS_BROWSER ? [document.body] : [])
501-
// Preset the fallback return focus value if it is not set
502-
// `document.activeElement` should be the trigger element that was clicked or
503-
// in the case of using the v-model, which ever element has current focus
504-
// Will be overridden by some commands such as toggle, etc.
505-
// Note: On IE 11, `document.activeElement` may be `null`
506-
// So we test it for truthiness first
507-
// https://github.com/bootstrap-vue/bootstrap-vue/issues/3206
508-
return activeElement && activeElement.focus ? activeElement : null
509-
},
510514
// Private method to finish showing modal
511515
doShow() {
512516
/* istanbul ignore next: commenting out for now until we can test stacking */
@@ -588,13 +592,6 @@ export const BModal = /*#__PURE__*/ Vue.extend({
588592
this.emitEvent(this.buildEvent(EVENT_NAME_HIDDEN))
589593
})
590594
},
591-
emitEvent(bvEvent) {
592-
const { type } = bvEvent
593-
// We emit on `$root` first in case a global listener wants to cancel
594-
// the event first before the instance emits its event
595-
this.emitOnRoot(getRootEventName(NAME_MODAL, type), bvEvent, bvEvent.componentId)
596-
this.$emit(type, bvEvent)
597-
},
598595
// UI event handlers
599596
onDialogMousedown() {
600597
// Watch to see if the matching mouseup event occurs outside the dialog

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy