%PDF- %PDF-
Direktori : /home/ugotscom/bos_naturals/node_modules/bootstrap-vue/src/components/modal/ |
Current File : /home/ugotscom/bos_naturals/node_modules/bootstrap-vue/src/components/modal/modal.js |
import Vue from '../../vue' import { NAME_MODAL } from '../../constants/components' import { EVENT_OPTIONS_NO_CAPTURE } from '../../constants/events' import { CODE_ESC } from '../../constants/key-codes' import { SLOT_NAME_DEFAULT } from '../../constants/slot-names' import BVTransition from '../../utils/bv-transition' import identity from '../../utils/identity' import observeDom from '../../utils/observe-dom' import { arrayIncludes, concat } from '../../utils/array' import { makePropsConfigurable } from '../../utils/config' import { attemptFocus, closest, contains, getActiveElement, getTabables, requestAF, select } from '../../utils/dom' import { isBrowser } from '../../utils/env' import { eventOn, eventOff } from '../../utils/events' import { htmlOrText } from '../../utils/html' import { isString, isUndefinedOrNull } from '../../utils/inspect' import { HTMLElement } from '../../utils/safe-types' import { BTransporterSingle } from '../../utils/transporter' import attrsMixin from '../../mixins/attrs' import idMixin from '../../mixins/id' import listenOnDocumentMixin from '../../mixins/listen-on-document' import listenOnRootMixin from '../../mixins/listen-on-root' import listenOnWindowMixin from '../../mixins/listen-on-window' import normalizeSlotMixin from '../../mixins/normalize-slot' import scopedStyleAttrsMixin from '../../mixins/scoped-style-attrs' import { BButton } from '../button/button' import { BButtonClose } from '../button/button-close' import { modalManager } from './helpers/modal-manager' import { BvModalEvent } from './helpers/bv-modal-event.class' // --- Constants --- // ObserveDom config to detect changes in modal content // so that we can adjust the modal padding if needed const OBSERVER_CONFIG = { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ['style', 'class'] } // --- Props --- export const props = makePropsConfigurable( { size: { type: String, default: 'md' }, centered: { type: Boolean, default: false }, scrollable: { type: Boolean, default: false }, buttonSize: { type: String // default: '' }, noStacking: { type: Boolean, default: false }, noFade: { type: Boolean, default: false }, noCloseOnBackdrop: { type: Boolean, default: false }, noCloseOnEsc: { type: Boolean, default: false }, noEnforceFocus: { type: Boolean, default: false }, ignoreEnforceFocusSelector: { type: [Array, String], default: '' }, title: { type: String, default: '' }, titleHtml: { type: String }, titleTag: { type: String, default: 'h5' }, titleClass: { type: [String, Array, Object] // default: null }, titleSrOnly: { type: Boolean, default: false }, ariaLabel: { type: String // default: null }, headerBgVariant: { type: String // default: undefined }, headerBorderVariant: { type: String // default: undefined }, headerTextVariant: { type: String // default: undefined }, headerCloseVariant: { type: String // default: undefined }, headerClass: { type: [String, Array, Object] // default: null }, bodyBgVariant: { type: String // default: undefined }, bodyTextVariant: { type: String // default: undefined }, modalClass: { type: [String, Array, Object] // default: null }, dialogClass: { type: [String, Array, Object] // default: null }, contentClass: { type: [String, Array, Object] // default: null }, bodyClass: { type: [String, Array, Object] // default: null }, footerBgVariant: { type: String // default: undefined }, footerBorderVariant: { type: String // default: undefined }, footerTextVariant: { type: String // default: undefined }, footerClass: { type: [String, Array, Object] // default: null }, // TODO: Rename to `noHeader` and deprecate `hideHeader` hideHeader: { type: Boolean, default: false }, // TODO: Rename to `noFooter` and deprecate `hideFooter` hideFooter: { type: Boolean, default: false }, // TODO: Rename to `noHeaderClose` and deprecate `hideHeaderClose` hideHeaderClose: { type: Boolean, default: false }, // TODO: Rename to `noBackdrop` and deprecate `hideBackdrop` hideBackdrop: { type: Boolean, default: false }, okOnly: { type: Boolean, default: false }, okDisabled: { type: Boolean, default: false }, cancelDisabled: { type: Boolean, default: false }, visible: { type: Boolean, default: false }, returnFocus: { // HTML Element, CSS selector string or Vue component instance type: [HTMLElement, String, Object], default: null }, headerCloseContent: { type: String, default: '×' }, headerCloseLabel: { type: String, default: 'Close' }, cancelTitle: { type: String, default: 'Cancel' }, cancelTitleHtml: { type: String }, okTitle: { type: String, default: 'OK' }, okTitleHtml: { type: String }, cancelVariant: { type: String, default: 'secondary' }, okVariant: { type: String, default: 'primary' }, lazy: { type: Boolean, default: false }, busy: { type: Boolean, default: false }, static: { type: Boolean, default: false }, autoFocusButton: { type: String, default: null, /* istanbul ignore next */ validator(value) { return isUndefinedOrNull(value) || arrayIncludes(['ok', 'cancel', 'close'], value) } } }, NAME_MODAL ) // @vue/component export const BModal = /*#__PURE__*/ Vue.extend({ name: NAME_MODAL, mixins: [ attrsMixin, idMixin, listenOnDocumentMixin, listenOnRootMixin, listenOnWindowMixin, normalizeSlotMixin, scopedStyleAttrsMixin ], inheritAttrs: false, model: { prop: 'visible', event: 'change' }, props, data() { return { isHidden: true, // If modal should not be in document isVisible: false, // Controls modal visible state isTransitioning: false, // Used for style control isShow: false, // Used for style control isBlock: false, // Used for style control isOpening: false, // To signal that the modal is in the process of opening isClosing: false, // To signal that the modal is in the process of closing ignoreBackdropClick: false, // Used to signify if click out listener should ignore the click isModalOverflowing: false, return_focus: this.returnFocus || null, // The following items are controlled by the modalManager instance scrollbarWidth: 0, zIndex: modalManager.getBaseZIndex(), isTop: true, isBodyOverflowing: false } }, computed: { modalId() { return this.safeId() }, modalOuterId() { return this.safeId('__BV_modal_outer_') }, modalHeaderId() { return this.safeId('__BV_modal_header_') }, modalBodyId() { return this.safeId('__BV_modal_body_') }, modalTitleId() { return this.safeId('__BV_modal_title_') }, modalContentId() { return this.safeId('__BV_modal_content_') }, modalFooterId() { return this.safeId('__BV_modal_footer_') }, modalBackdropId() { return this.safeId('__BV_modal_backdrop_') }, modalClasses() { return [ { fade: !this.noFade, show: this.isShow }, this.modalClass ] }, modalStyles() { const sbWidth = `${this.scrollbarWidth}px` return { paddingLeft: !this.isBodyOverflowing && this.isModalOverflowing ? sbWidth : '', paddingRight: this.isBodyOverflowing && !this.isModalOverflowing ? sbWidth : '', // Needed to fix issue https://github.com/bootstrap-vue/bootstrap-vue/issues/3457 // Even though we are using v-show, we must ensure 'none' is restored in the styles display: this.isBlock ? 'block' : 'none' } }, dialogClasses() { return [ { [`modal-${this.size}`]: this.size, 'modal-dialog-centered': this.centered, 'modal-dialog-scrollable': this.scrollable }, this.dialogClass ] }, headerClasses() { return [ { [`bg-${this.headerBgVariant}`]: this.headerBgVariant, [`text-${this.headerTextVariant}`]: this.headerTextVariant, [`border-${this.headerBorderVariant}`]: this.headerBorderVariant }, this.headerClass ] }, titleClasses() { return [{ 'sr-only': this.titleSrOnly }, this.titleClass] }, bodyClasses() { return [ { [`bg-${this.bodyBgVariant}`]: this.bodyBgVariant, [`text-${this.bodyTextVariant}`]: this.bodyTextVariant }, this.bodyClass ] }, footerClasses() { return [ { [`bg-${this.footerBgVariant}`]: this.footerBgVariant, [`text-${this.footerTextVariant}`]: this.footerTextVariant, [`border-${this.footerBorderVariant}`]: this.footerBorderVariant }, this.footerClass ] }, modalOuterStyle() { // Styles needed for proper stacking of modals return { position: 'absolute', zIndex: this.zIndex } }, slotScope() { return { ok: this.onOk, cancel: this.onCancel, close: this.onClose, hide: this.hide, visible: this.isVisible } }, computeIgnoreEnforceFocusSelector() { // Normalize to an single selector with selectors separated by `,` return concat(this.ignoreEnforceFocusSelector) .filter(identity) .join(',') .trim() }, computedAttrs() { // If the parent has a scoped style attribute, and the modal // is portalled, add the scoped attribute to the modal wrapper const scopedStyleAttrs = !this.static ? this.scopedStyleAttrs : {} return { ...scopedStyleAttrs, ...this.bvAttrs, id: this.modalOuterId } }, computedModalAttrs() { const { isVisible, ariaLabel } = this return { id: this.modalId, role: 'dialog', 'aria-hidden': isVisible ? null : 'true', 'aria-modal': isVisible ? 'true' : null, 'aria-label': ariaLabel, 'aria-labelledby': this.hideHeader || ariaLabel || // TODO: Rename slot to `title` and deprecate `modal-title` !(this.hasNormalizedSlot('modal-title') || this.titleHtml || this.title) ? null : this.modalTitleId, 'aria-describedby': this.modalBodyId } } }, watch: { visible(newVal, oldVal) { if (newVal !== oldVal) { this[newVal ? 'show' : 'hide']() } } }, created() { // Define non-reactive properties this.$_observer = null }, mounted() { // Set initial z-index as queried from the DOM this.zIndex = modalManager.getBaseZIndex() // Listen for events from others to either open or close ourselves // and listen to all modals to enable/disable enforce focus this.listenOnRoot('bv::show::modal', this.showHandler) this.listenOnRoot('bv::hide::modal', this.hideHandler) this.listenOnRoot('bv::toggle::modal', this.toggleHandler) // Listen for `bv:modal::show events`, and close ourselves if the // opening modal not us this.listenOnRoot('bv::modal::show', this.modalListener) // Initially show modal? if (this.visible === true) { this.$nextTick(this.show) } }, beforeDestroy() { // Ensure everything is back to normal this.setObserver(false) if (this.isVisible) { this.isVisible = false this.isShow = false this.isTransitioning = false } }, methods: { setObserver(on = false) { this.$_observer && this.$_observer.disconnect() this.$_observer = null if (on) { this.$_observer = observeDom( this.$refs.content, this.checkModalOverflow.bind(this), OBSERVER_CONFIG ) } }, // Private method to update the v-model updateModel(val) { if (val !== this.visible) { this.$emit('change', val) } }, // Private method to create a BvModalEvent object buildEvent(type, options = {}) { return new BvModalEvent(type, { // Default options cancelable: false, target: this.$refs.modal || this.$el || null, relatedTarget: null, trigger: null, // Supplied options ...options, // Options that can't be overridden vueTarget: this, componentId: this.modalId }) }, // Public method to show modal show() { if (this.isVisible || this.isOpening) { // If already open, or in the process of opening, do nothing /* istanbul ignore next */ return } /* istanbul ignore next */ if (this.isClosing) { // If we are in the process of closing, wait until hidden before re-opening /* istanbul ignore next */ this.$once('hidden', this.show) /* istanbul ignore next */ return } this.isOpening = true // Set the element to return focus to when closed this.return_focus = this.return_focus || this.getActiveElement() const showEvt = this.buildEvent('show', { cancelable: true }) this.emitEvent(showEvt) // Don't show if canceled if (showEvt.defaultPrevented || this.isVisible) { this.isOpening = false // Ensure the v-model reflects the current state this.updateModel(false) return } // Show the modal this.doShow() }, // Public method to hide modal hide(trigger = '') { if (!this.isVisible || this.isClosing) { /* istanbul ignore next */ return } this.isClosing = true const hideEvt = this.buildEvent('hide', { cancelable: trigger !== 'FORCE', trigger: trigger || null }) // We emit specific event for one of the three built-in buttons if (trigger === 'ok') { this.$emit('ok', hideEvt) } else if (trigger === 'cancel') { this.$emit('cancel', hideEvt) } else if (trigger === 'headerclose') { this.$emit('close', hideEvt) } this.emitEvent(hideEvt) // Hide if not canceled if (hideEvt.defaultPrevented || !this.isVisible) { this.isClosing = false // Ensure v-model reflects current state this.updateModel(true) return } // Stop observing for content changes this.setObserver(false) // Trigger the hide transition this.isVisible = false // Update the v-model this.updateModel(false) }, // Public method to toggle modal visibility toggle(triggerEl) { if (triggerEl) { this.return_focus = triggerEl } if (this.isVisible) { this.hide('toggle') } else { this.show() } }, // Private method to get the current document active element getActiveElement() { // Returning focus to `document.body` may cause unwanted scrolls, // so we exclude setting focus on body const activeElement = getActiveElement(isBrowser ? [document.body] : []) // Preset the fallback return focus value if it is not set // `document.activeElement` should be the trigger element that was clicked or // in the case of using the v-model, which ever element has current focus // Will be overridden by some commands such as toggle, etc. // Note: On IE 11, `document.activeElement` may be `null` // So we test it for truthiness first // https://github.com/bootstrap-vue/bootstrap-vue/issues/3206 return activeElement && activeElement.focus ? activeElement : null }, // Private method to finish showing modal doShow() { /* istanbul ignore next: commenting out for now until we can test stacking */ if (modalManager.modalsAreOpen && this.noStacking) { // If another modal(s) is already open, wait for it(them) to close this.listenOnRootOnce('bv::modal::hidden', this.doShow) return } modalManager.registerModal(this) // Place modal in DOM this.isHidden = false this.$nextTick(() => { // We do this in `$nextTick()` to ensure the modal is in DOM first // before we show it this.isVisible = true this.isOpening = false // Update the v-model this.updateModel(true) this.$nextTick(() => { // Observe changes in modal content and adjust if necessary // In a `$nextTick()` in case modal content is lazy this.setObserver(true) }) }) }, // Transition handlers onBeforeEnter() { this.isTransitioning = true this.setResizeEvent(true) }, onEnter() { this.isBlock = true // We add the `show` class 1 frame later // `requestAF()` runs the callback before the next repaint, so we need // two calls to guarantee the next frame has been rendered requestAF(() => { requestAF(() => { this.isShow = true }) }) }, onAfterEnter() { this.checkModalOverflow() this.isTransitioning = false // We use `requestAF()` to allow transition hooks to complete // before passing control over to the other handlers // This will allow users to not have to use `$nextTick()` or `requestAF()` // when trying to pre-focus an element requestAF(() => { this.emitEvent(this.buildEvent('shown')) this.setEnforceFocus(true) this.$nextTick(() => { // Delayed in a `$nextTick()` to allow users time to pre-focus // an element if the wish this.focusFirst() }) }) }, onBeforeLeave() { this.isTransitioning = true this.setResizeEvent(false) this.setEnforceFocus(false) }, onLeave() { // Remove the 'show' class this.isShow = false }, onAfterLeave() { this.isBlock = false this.isTransitioning = false this.isModalOverflowing = false this.isHidden = true this.$nextTick(() => { this.isClosing = false modalManager.unregisterModal(this) this.returnFocusTo() // TODO: Need to find a way to pass the `trigger` property // to the `hidden` event, not just only the `hide` event this.emitEvent(this.buildEvent('hidden')) }) }, // Event emitter emitEvent(bvModalEvt) { const type = bvModalEvt.type // We emit on root first incase a global listener wants to cancel // the event first before the instance emits its event this.emitOnRoot(`bv::modal::${type}`, bvModalEvt, bvModalEvt.componentId) this.$emit(type, bvModalEvt) }, // UI event handlers onDialogMousedown() { // Watch to see if the matching mouseup event occurs outside the dialog // And if it does, cancel the clickOut handler const modal = this.$refs.modal const onceModalMouseup = evt => { eventOff(modal, 'mouseup', onceModalMouseup, EVENT_OPTIONS_NO_CAPTURE) if (evt.target === modal) { this.ignoreBackdropClick = true } } eventOn(modal, 'mouseup', onceModalMouseup, EVENT_OPTIONS_NO_CAPTURE) }, onClickOut(evt) { if (this.ignoreBackdropClick) { // Click was initiated inside the modal content, but finished outside. // Set by the above onDialogMousedown handler this.ignoreBackdropClick = false return } // Do nothing if not visible, backdrop click disabled, or element // that generated click event is no longer in document body if (!this.isVisible || this.noCloseOnBackdrop || !contains(document.body, evt.target)) { return } // If backdrop clicked, hide modal if (!contains(this.$refs.content, evt.target)) { this.hide('backdrop') } }, onOk() { this.hide('ok') }, onCancel() { this.hide('cancel') }, onClose() { this.hide('headerclose') }, onEsc(evt) { // If ESC pressed, hide modal if (evt.keyCode === CODE_ESC && this.isVisible && !this.noCloseOnEsc) { this.hide('esc') } }, // Document focusin listener focusHandler(evt) { // If focus leaves modal content, bring it back const content = this.$refs.content const { target } = evt if ( this.noEnforceFocus || !this.isTop || !this.isVisible || !content || document === target || contains(content, target) || (this.computeIgnoreEnforceFocusSelector && closest(this.computeIgnoreEnforceFocusSelector, target, true)) ) { return } const tabables = getTabables(this.$refs.content) const { bottomTrap, topTrap } = this.$refs if (bottomTrap && target === bottomTrap) { // If user pressed TAB out of modal into our bottom trab trap element // Find the first tabable element in the modal content and focus it if (attemptFocus(tabables[0])) { // Focus was successful return } } else if (topTrap && target === topTrap) { // If user pressed CTRL-TAB out of modal and into our top tab trap element // Find the last tabable element in the modal content and focus it if (attemptFocus(tabables[tabables.length - 1])) { // Focus was successful return } } // Otherwise focus the modal content container attemptFocus(content, { preventScroll: true }) }, // Turn on/off focusin listener setEnforceFocus(on) { this.listenDocument(on, 'focusin', this.focusHandler) }, // Resize listener setResizeEvent(on) { this.listenWindow(on, 'resize', this.checkModalOverflow) this.listenWindow(on, 'orientationchange', this.checkModalOverflow) }, // Root listener handlers showHandler(id, triggerEl) { if (id === this.modalId) { this.return_focus = triggerEl || this.getActiveElement() this.show() } }, hideHandler(id) { if (id === this.modalId) { this.hide('event') } }, toggleHandler(id, triggerEl) { if (id === this.modalId) { this.toggle(triggerEl) } }, modalListener(bvEvt) { // If another modal opens, close this one if stacking not permitted if (this.noStacking && bvEvt.vueTarget !== this) { this.hide() } }, // Focus control handlers focusFirst() { // Don't try and focus if we are SSR if (isBrowser) { requestAF(() => { const modal = this.$refs.modal const content = this.$refs.content const activeElement = this.getActiveElement() // If the modal contains the activeElement, we don't do anything if (modal && content && !(activeElement && contains(content, activeElement))) { const ok = this.$refs['ok-button'] const cancel = this.$refs['cancel-button'] const close = this.$refs['close-button'] // Focus the appropriate button or modal content wrapper const autoFocus = this.autoFocusButton /* istanbul ignore next */ const el = autoFocus === 'ok' && ok ? ok.$el || ok : autoFocus === 'cancel' && cancel ? cancel.$el || cancel : autoFocus === 'close' && close ? close.$el || close : content // Focus the element attemptFocus(el) if (el === content) { // Make sure top of modal is showing (if longer than the viewport) this.$nextTick(() => { modal.scrollTop = 0 }) } } }) } }, returnFocusTo() { // Prefer `returnFocus` prop over event specified // `return_focus` value let el = this.returnFocus || this.return_focus || null this.return_focus = null this.$nextTick(() => { // Is el a string CSS selector? el = isString(el) ? select(el) : el if (el) { // Possibly could be a component reference el = el.$el || el attemptFocus(el) } }) }, checkModalOverflow() { if (this.isVisible) { const modal = this.$refs.modal this.isModalOverflowing = modal.scrollHeight > document.documentElement.clientHeight } }, makeModal(h) { // Modal header let $header = h() if (!this.hideHeader) { // TODO: Rename slot to `header` and deprecate `modal-header` let $modalHeader = this.normalizeSlot('modal-header', this.slotScope) if (!$modalHeader) { let $closeButton = h() if (!this.hideHeaderClose) { $closeButton = h( BButtonClose, { props: { content: this.headerCloseContent, disabled: this.isTransitioning, ariaLabel: this.headerCloseLabel, textVariant: this.headerCloseVariant || this.headerTextVariant }, on: { click: this.onClose }, ref: 'close-button' }, // TODO: Rename slot to `header-close` and deprecate `modal-header-close` [this.normalizeSlot('modal-header-close')] ) } $modalHeader = [ h( this.titleTag, { staticClass: 'modal-title', class: this.titleClasses, attrs: { id: this.modalTitleId }, // TODO: Rename slot to `title` and deprecate `modal-title` domProps: this.hasNormalizedSlot('modal-title') ? {} : htmlOrText(this.titleHtml, this.title) }, // TODO: Rename slot to `title` and deprecate `modal-title` this.normalizeSlot('modal-title', this.slotScope) ), $closeButton ] } $header = h( 'header', { staticClass: 'modal-header', class: this.headerClasses, attrs: { id: this.modalHeaderId }, ref: 'header' }, [$modalHeader] ) } // Modal body const $body = h( 'div', { staticClass: 'modal-body', class: this.bodyClasses, attrs: { id: this.modalBodyId }, ref: 'body' }, this.normalizeSlot(SLOT_NAME_DEFAULT, this.slotScope) ) // Modal footer let $footer = h() if (!this.hideFooter) { // TODO: Rename slot to `footer` and deprecate `modal-footer` let $modalFooter = this.normalizeSlot('modal-footer', this.slotScope) if (!$modalFooter) { let $cancelButton = h() if (!this.okOnly) { $cancelButton = h( BButton, { props: { variant: this.cancelVariant, size: this.buttonSize, disabled: this.cancelDisabled || this.busy || this.isTransitioning }, // TODO: Rename slot to `cancel-button` and deprecate `modal-cancel` domProps: this.hasNormalizedSlot('modal-cancel') ? {} : htmlOrText(this.cancelTitleHtml, this.cancelTitle), on: { click: this.onCancel }, ref: 'cancel-button' }, // TODO: Rename slot to `cancel-button` and deprecate `modal-cancel` this.normalizeSlot('modal-cancel') ) } const $okButton = h( BButton, { props: { variant: this.okVariant, size: this.buttonSize, disabled: this.okDisabled || this.busy || this.isTransitioning }, // TODO: Rename slot to `ok-button` and deprecate `modal-ok` domProps: this.hasNormalizedSlot('modal-ok') ? {} : htmlOrText(this.okTitleHtml, this.okTitle), on: { click: this.onOk }, ref: 'ok-button' }, // TODO: Rename slot to `ok-button` and deprecate `modal-ok` this.normalizeSlot('modal-ok') ) $modalFooter = [$cancelButton, $okButton] } $footer = h( 'footer', { staticClass: 'modal-footer', class: this.footerClasses, attrs: { id: this.modalFooterId }, ref: 'footer' }, [$modalFooter] ) } // Assemble modal content const $modalContent = h( 'div', { staticClass: 'modal-content', class: this.contentClass, attrs: { id: this.modalContentId, tabindex: '-1' }, ref: 'content' }, [$header, $body, $footer] ) // Tab traps to prevent page from scrolling to next element in // tab index during enforce-focus tab cycle let $tabTrapTop = h() let $tabTrapBottom = h() if (this.isVisible && !this.noEnforceFocus) { $tabTrapTop = h('span', { ref: 'topTrap', attrs: { tabindex: '0' } }) $tabTrapBottom = h('span', { ref: 'bottomTrap', attrs: { tabindex: '0' } }) } // Modal dialog wrapper const $modalDialog = h( 'div', { staticClass: 'modal-dialog', class: this.dialogClasses, on: { mousedown: this.onDialogMousedown }, ref: 'dialog' }, [$tabTrapTop, $modalContent, $tabTrapBottom] ) // Modal let $modal = h( 'div', { staticClass: 'modal', class: this.modalClasses, style: this.modalStyles, attrs: this.computedModalAttrs, on: { keydown: this.onEsc, click: this.onClickOut }, directives: [{ name: 'show', value: this.isVisible }], ref: 'modal' }, [$modalDialog] ) // Wrap modal in transition // Sadly, we can't use `BVTransition` here due to the differences in // transition durations for `.modal` and `.modal-dialog` // At least until https://github.com/vuejs/vue/issues/9986 is resolved $modal = h( 'transition', { props: { enterClass: '', enterToClass: '', enterActiveClass: '', leaveClass: '', leaveActiveClass: '', leaveToClass: '' }, on: { beforeEnter: this.onBeforeEnter, enter: this.onEnter, afterEnter: this.onAfterEnter, beforeLeave: this.onBeforeLeave, leave: this.onLeave, afterLeave: this.onAfterLeave } }, [$modal] ) // Modal backdrop let $backdrop = h() if (!this.hideBackdrop && this.isVisible) { $backdrop = h( 'div', { staticClass: 'modal-backdrop', attrs: { id: this.modalBackdropId } }, // TODO: Rename slot to `backdrop` and deprecate `modal-backdrop` this.normalizeSlot('modal-backdrop') ) } $backdrop = h(BVTransition, { props: { noFade: this.noFade } }, [$backdrop]) // Assemble modal and backdrop in an outer <div> return h( 'div', { style: this.modalOuterStyle, attrs: this.computedAttrs, key: `modal-outer-${this._uid}` }, [$modal, $backdrop] ) } }, render(h) { if (this.static) { return this.lazy && this.isHidden ? h() : this.makeModal(h) } else { return this.isHidden ? h() : h(BTransporterSingle, [this.makeModal(h)]) } } })