1
1
import Vue from '../../utils/vue'
2
-
import { modalManager } from './helpers/modal-manager'
3
-
import { BvModalEvent } from './helpers/bv-modal-event.class'
4
-
import idMixin from '../../mixins/id'
5
-
import listenOnRootMixin from '../../mixins/listen-on-root'
6
-
import normalizeSlotMixin from '../../mixins/normalize-slot'
7
-
import scopedStyleAttrsMixin from '../../mixins/scoped-style-attrs'
8
2
import BVTransition from '../../utils/bv-transition'
9
3
import KeyCodes from '../../utils/key-codes'
10
4
import observeDom from '../../utils/observe-dom'
11
-
import { BTransporterSingle } from '../../utils/transporter'
12
-
import { isBrowser } from '../../utils/env'
13
-
import { isString } from '../../utils/inspect'
5
+
import { arrayIncludes } from '../../utils/array'
14
6
import { getComponentConfig } from '../../utils/config'
7
+
import {
8
+
contains,
9
+
eventOff,
10
+
eventOn,
11
+
isVisible,
12
+
requestAF,
13
+
select,
14
+
selectAll
15
+
} from '../../utils/dom'
16
+
import { isBrowser } from '../../utils/env'
15
17
import { stripTags } from '../../utils/html'
16
-
import { contains, eventOff, eventOn, isVisible, select, selectAll } from '../../utils/dom'
18
+
import { isString, isUndefinedOrNull } from '../../utils/inspect'
19
+
import { BTransporterSingle } from '../../utils/transporter'
20
+
import idMixin from '../../mixins/id'
21
+
import listenOnRootMixin from '../../mixins/listen-on-root'
22
+
import normalizeSlotMixin from '../../mixins/normalize-slot'
23
+
import scopedStyleAttrsMixin from '../../mixins/scoped-style-attrs'
17
24
import { BButton } from '../button/button'
18
25
import { BButtonClose } from '../button/button-close'
26
+
import { modalManager } from './helpers/modal-manager'
27
+
import { BvModalEvent } from './helpers/bv-modal-event.class'
19
28
20
29
// --- Constants ---
21
30
@@ -255,6 +264,14 @@ export const props = {
255
264
static: {
256
265
type: Boolean,
257
266
default: false
267
+
},
268
+
autoFocusButton: {
269
+
type: String,
270
+
default: null,
271
+
validator: val => {
272
+
/* istanbul ignore next */
273
+
return isUndefinedOrNull(val) || arrayIncludes(['ok', 'cancel', 'close'], val)
274
+
}
258
275
}
259
276
}
260
277
@@ -576,10 +593,18 @@ export const BModal = /*#__PURE__*/ Vue.extend({
576
593
this.checkModalOverflow()
577
594
this.isShow = true
578
595
this.isTransitioning = false
579
-
this.$nextTick(() => {
596
+
// We use `requestAF()` to allow transition hooks to complete
597
+
// before passing control over to the other handlers
598
+
// This will allow users to not have to use `$nextTick()` or `requestAF()`
599
+
// when trying to pre-focus an element
600
+
requestAF(() => {
580
601
this.emitEvent(this.buildEvent('shown'))
581
-
this.focusFirst()
582
602
this.setEnforceFocus(true)
603
+
this.$nextTick(() => {
604
+
// Delayed in a `$nextTick()` to allow users time to pre-focus
605
+
// an element if the wish
606
+
this.focusFirst()
607
+
})
583
608
})
584
609
},
585
610
onBeforeLeave() {
@@ -731,18 +756,32 @@ export const BModal = /*#__PURE__*/ Vue.extend({
731
756
focusFirst() {
732
757
// Don't try and focus if we are SSR
733
758
if (isBrowser) {
734
-
const modal = this.$refs.modal
735
-
const content = this.$refs.content
736
-
const activeElement = this.getActiveElement()
737
-
// If the modal contains the activeElement, we don't do anything
738
-
if (modal && content && !(activeElement && contains(content, activeElement))) {
739
-
// Make sure top of modal is showing (if longer than the viewport)
740
-
// and focus the modal content wrapper
741
-
this.$nextTick(() => {
742
-
modal.scrollTop = 0
743
-
content.focus()
744
-
})
745
-
}
759
+
requestAF(() => {
760
+
const modal = this.$refs.modal
761
+
const content = this.$refs.content
762
+
const activeElement = this.getActiveElement()
763
+
// If the modal contains the activeElement, we don't do anything
764
+
if (modal && content && !(activeElement && contains(content, activeElement))) {
765
+
const ok = this.$refs['ok-button']
766
+
const cancel = this.$refs['cancel-button']
767
+
const close = this.$refs['close-button']
768
+
// Focus the appropriate button or modal content wrapper
769
+
const autoFocus = this.autoFocusButton
770
+
const el =
771
+
autoFocus === 'ok' && ok
772
+
? ok.$el || ok
773
+
: autoFocus === 'cancel' && cancel
774
+
? cancel.$el || cancel
775
+
: autoFocus === 'close' && close
776
+
? close.$el || close
777
+
: content
778
+
// Make sure top of modal is showing (if longer than the viewport)
779
+
if (el === content) {
780
+
modal.scrollTop = 0
781
+
}
782
+
attemptFocus(el)
783
+
}
784
+
})
746
785
}
747
786
},
748
787
returnFocusTo() {
@@ -777,6 +816,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({
777
816
closeButton = h(
778
817
BButtonClose,
779
818
{
819
+
ref: 'close-button',
780
820
props: {
781
821
disabled: this.isTransitioning,
782
822
ariaLabel: this.headerCloseLabel,
@@ -840,6 +880,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({
840
880
cancelButton = h(
841
881
BButton,
842
882
{
883
+
ref: 'cancel-button',
843
884
props: {
844
885
variant: this.cancelVariant,
845
886
size: this.buttonSize,
@@ -857,6 +898,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({
857
898
const okButton = h(
858
899
BButton,
859
900
{
901
+
ref: 'ok-button',
860
902
props: {
861
903
variant: this.okVariant,
862
904
size: this.buttonSize,
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4