"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

var _button = _interopRequireDefault(require("../button/button"));

var _buttonClose = _interopRequireDefault(require("../button/button-close"));

var _id = _interopRequireDefault(require("../../mixins/id"));

var _listenOnRoot = _interopRequireDefault(require("../../mixins/listen-on-root"));

var _observeDom = _interopRequireDefault(require("../../utils/observe-dom"));

var _warn = _interopRequireDefault(require("../../utils/warn"));

var _keyCodes = _interopRequireDefault(require("../../utils/key-codes"));

var _bvEvent = _interopRequireDefault(require("../../utils/bv-event.class"));

var _html = require("../../utils/html");

var _dom = require("../../utils/dom");

// Selectors for padding/margin adjustments
var Selector = {
  FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
  STICKY_CONTENT: '.sticky-top',
  NAVBAR_TOGGLER: '.navbar-toggler' // ObserveDom config

};
var OBSERVER_CONFIG = {
  subtree: true,
  childList: true,
  characterData: true,
  attributes: true,
  attributeFilter: ['style', 'class'] // modal wrapper ZINDEX offset incrememnt

};
var ZINDEX_OFFSET = 2000; // Modal open count helpers

function getModalOpenCount() {
  return parseInt((0, _dom.getAttr)(document.body, 'data-modal-open-count') || 0, 10);
}

function setModalOpenCount(count) {
  (0, _dom.setAttr)(document.body, 'data-modal-open-count', String(count));
  return count;
}

function incrementModalOpenCount() {
  return setModalOpenCount(getModalOpenCount() + 1);
}

function decrementModalOpenCount() {
  return setModalOpenCount(Math.max(getModalOpenCount() - 1, 0));
} // Returns the current visible modal highest z-index


function getModalMaxZIndex() {
  return (0, _dom.selectAll)('div.modal')
  /* find all modals that are in document */
  .filter(_dom.isVisible)
  /* filter only visible ones */
  .map(function (m) {
    return m.parentElement;
  })
  /* select the outer div */
  .reduce(function (max, el) {
    /* compute the highest z-index */
    return Math.max(max, parseInt(el.style.zIndex || 0, 10));
  }, 0);
} // Returns the next z-index to be used by a modal to ensure proper stacking
// regardless of document order. Increments by 2000


function getModalNextZIndex() {
  return getModalMaxZIndex() + ZINDEX_OFFSET;
} // @vue/component


var _default = {
  name: 'BModal',
  components: {
    BButton: _button.default,
    BButtonClose: _buttonClose.default
  },
  mixins: [_id.default, _listenOnRoot.default],
  model: {
    prop: 'visible',
    event: 'change'
  },
  props: {
    title: {
      type: String,
      default: ''
    },
    titleHTML: {
      type: String
    },
    titleTag: {
      type: String,
      default: 'h5'
    },
    size: {
      type: String,
      default: 'md'
    },
    centered: {
      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
    },
    headerBgVariant: {
      type: String,
      default: null
    },
    headerBorderVariant: {
      type: String,
      default: null
    },
    headerTextVariant: {
      type: String,
      default: null
    },
    headerClass: {
      type: [String, Array],
      default: null
    },
    bodyBgVariant: {
      type: String,
      default: null
    },
    bodyTextVariant: {
      type: String,
      default: null
    },
    modalClass: {
      type: [String, Array],
      default: null
    },
    dialogClass: {
      type: [String, Array],
      default: null
    },
    contentClass: {
      type: [String, Array],
      default: null
    },
    bodyClass: {
      type: [String, Array],
      default: null
    },
    footerBgVariant: {
      type: String,
      default: null
    },
    footerBorderVariant: {
      type: String,
      default: null
    },
    footerTextVariant: {
      type: String,
      default: null
    },
    footerClass: {
      type: [String, Array],
      default: null
    },
    hideHeader: {
      type: Boolean,
      default: false
    },
    hideFooter: {
      type: Boolean,
      default: false
    },
    hideHeaderClose: {
      type: Boolean,
      default: false
    },
    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: {
      // type: Object,
      default: null
    },
    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
    }
  },
  data: function data() {
    return {
      is_hidden: this.lazy || false,
      // for lazy modals
      is_visible: false,
      // controls modal visible state
      is_transitioning: false,
      // Used for style control
      is_show: false,
      // Used for style control
      is_block: false,
      // Used for style control
      is_opening: false,
      // Semaphore for previnting incorrect modal open counts
      is_closing: false,
      // Semapbore for preventing incorrect modal open counts
      scrollbarWidth: 0,
      zIndex: ZINDEX_OFFSET,
      // z-index for modal stacking
      isTop: true,
      // If the modal is the topmost opened modal
      isBodyOverflowing: false,
      return_focus: this.returnFocus || null
    };
  },
  computed: {
    contentClasses: function contentClasses() {
      return ['modal-content', this.contentClass];
    },
    modalClasses: function modalClasses() {
      return [{
        fade: !this.noFade,
        show: this.is_show,
        'd-block': this.is_block
      }, this.modalClass];
    },
    dialogClasses: function dialogClasses() {
      var _ref;

      return [(_ref = {}, (0, _defineProperty2.default)(_ref, "modal-".concat(this.size), Boolean(this.size)), (0, _defineProperty2.default)(_ref, 'modal-dialog-centered', this.centered), _ref), this.dialogClass];
    },
    backdropClasses: function backdropClasses() {
      return {
        fade: !this.noFade,
        show: this.is_show || this.noFade
      };
    },
    headerClasses: function headerClasses() {
      var _ref2;

      return [(_ref2 = {}, (0, _defineProperty2.default)(_ref2, "bg-".concat(this.headerBgVariant), Boolean(this.headerBgVariant)), (0, _defineProperty2.default)(_ref2, "text-".concat(this.headerTextVariant), Boolean(this.headerTextVariant)), (0, _defineProperty2.default)(_ref2, "border-".concat(this.headerBorderVariant), Boolean(this.headerBorderVariant)), _ref2), this.headerClass];
    },
    bodyClasses: function bodyClasses() {
      var _ref3;

      return [(_ref3 = {}, (0, _defineProperty2.default)(_ref3, "bg-".concat(this.bodyBgVariant), Boolean(this.bodyBgVariant)), (0, _defineProperty2.default)(_ref3, "text-".concat(this.bodyTextVariant), Boolean(this.bodyTextVariant)), _ref3), this.bodyClass];
    },
    footerClasses: function footerClasses() {
      var _ref4;

      return [(_ref4 = {}, (0, _defineProperty2.default)(_ref4, "bg-".concat(this.footerBgVariant), Boolean(this.footerBgVariant)), (0, _defineProperty2.default)(_ref4, "text-".concat(this.footerTextVariant), Boolean(this.footerTextVariant)), (0, _defineProperty2.default)(_ref4, "border-".concat(this.footerBorderVariant), Boolean(this.footerBorderVariant)), _ref4), this.footerClass];
    },
    modalOuterStyle: function modalOuterStyle() {
      return {
        // We only set these styles on the stacked modals (ones with next z-index > 0).
        position: 'relative',
        zIndex: this.zIndex
      };
    }
  },
  watch: {
    visible: function visible(newVal, oldVal) {
      if (newVal === oldVal) {
        return;
      }

      this[newVal ? 'show' : 'hide']();
    }
  },
  created: function created() {
    // create non-reactive property
    this._observer = null;
  },
  mounted: function mounted() {
    // 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::modal::shown', this.shownHandler);
    this.listenOnRoot('bv::hide::modal', this.hideHandler);
    this.listenOnRoot('bv::modal::hidden', this.hiddenHandler); // 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.show();
    }
  },
  beforeDestroy: function beforeDestroy()
  /* instanbul ignore next */
  {
    // Ensure everything is back to normal
    if (this._observer) {
      this._observer.disconnect();

      this._observer = null;
    } // Ensure our root "once" listener is gone


    this.$root.$off('bv::modal::hidden', this.doShow);
    this.setEnforceFocus(false);
    this.setResizeEvent(false);

    if (this.is_visible) {
      this.is_visible = false;
      this.is_show = false;
      this.is_transitioning = false;
      var count = decrementModalOpenCount();

      if (count === 0) {
        // Re-adjust body/navbar/fixed padding/margins (as we were the last modal open)
        this.setModalOpenClass(false);
        this.resetScrollbar();
        this.resetDialogAdjustments();
      }
    }
  },
  methods: {
    // Public Methods
    show: function show() {
      if (this.is_visible || this.is_opening) {
        // if already open, on in the process of opening, do nothing
        return;
      }

      if (this.is_closing) {
        // if we are in the process of closing, wait until hidden before re-opening
        this.$once('hidden', this.show);
        return;
      }

      this.is_opening = true;
      var showEvt = new _bvEvent.default('show', {
        cancelable: true,
        vueTarget: this,
        target: this.$refs.modal,
        modalId: this.safeId(),
        relatedTarget: null
      });
      this.emitEvent(showEvt); // Don't show if canceled

      if (showEvt.defaultPrevented || this.is_visible) {
        this.is_opening = false;
        return;
      }

      if (!this.noStacking) {
        // Find the z-index to use
        this.zIndex = getModalNextZIndex(); // Show the modal

        this.doShow();
        return;
      }

      if ((0, _dom.hasClass)(document.body, 'modal-open')) {
        // If another modal is already open, wait for it to close
        this.$root.$once('bv::modal::hidden', this.doShow);
        return;
      } // Show the modal


      this.doShow();
    },
    hide: function hide(trigger) {
      if (!this.is_visible || this.is_closing) {
        return;
      }

      this.is_closing = true;
      var hideEvt = new _bvEvent.default('hide', {
        cancelable: true,
        vueTarget: this,
        target: this.$refs.modal,
        modalId: this.safeId(),
        // this could be the trigger element/component reference
        relatedTarget: null,
        isOK: trigger || null,
        trigger: trigger || null,
        cancel: function cancel() {
          // Backwards compatibility
          (0, _warn.default)('b-modal: evt.cancel() is deprecated. Please use evt.preventDefault().');
          this.preventDefault();
        }
      });

      if (trigger === 'ok') {
        this.$emit('ok', hideEvt);
      } else if (trigger === 'cancel') {
        this.$emit('cancel', hideEvt);
      }

      this.emitEvent(hideEvt); // Hide if not canceled

      if (hideEvt.defaultPrevented || !this.is_visible) {
        this.is_closing = false;
        return;
      } // stop observing for content changes


      if (this._observer) {
        this._observer.disconnect();

        this._observer = null;
      }

      this.is_visible = false;
      this.$emit('change', false);
    },
    // Private method to finish showing modal
    doShow: function doShow() {
      var _this = this;

      // Place modal in DOM if lazy
      this.is_hidden = false;
      this.$nextTick(function () {
        // We do this in nextTick to ensure the modal is in DOM first before we show it
        _this.is_visible = true;
        _this.is_opening = false;

        _this.$emit('change', true); // Observe changes in modal content and adjust if necessary


        _this._observer = (0, _observeDom.default)(_this.$refs.content, _this.adjustDialog.bind(_this), OBSERVER_CONFIG);
      });
    },
    // Transition Handlers
    onBeforeEnter: function onBeforeEnter() {
      this.getScrollbarWidth();
      this.is_transitioning = true;
      this.checkScrollbar();
      var count = incrementModalOpenCount();

      if (count === 1) {
        this.setScrollbar();
      }

      this.adjustDialog();
      this.setModalOpenClass(true);
      this.setResizeEvent(true);
    },
    onEnter: function onEnter() {
      this.is_block = true;
    },
    onAfterEnter: function onAfterEnter() {
      var _this2 = this;

      this.is_show = true;
      this.is_transitioning = false;
      this.$nextTick(function () {
        var shownEvt = new _bvEvent.default('shown', {
          cancelable: false,
          vueTarget: _this2,
          target: _this2.$refs.modal,
          modalId: _this2.safeId(),
          relatedTarget: null
        });

        _this2.emitEvent(shownEvt);

        _this2.focusFirst();

        _this2.setEnforceFocus(true);
      });
    },
    onBeforeLeave: function onBeforeLeave() {
      this.is_transitioning = true;
      this.setResizeEvent(false);
    },
    onLeave: function onLeave() {
      // Remove the 'show' class
      this.is_show = false;
    },
    onAfterLeave: function onAfterLeave() {
      var _this3 = this;

      this.is_block = false;
      this.resetDialogAdjustments();
      this.is_transitioning = false;
      var count = decrementModalOpenCount();

      if (count === 0) {
        this.resetScrollbar();
        this.setModalOpenClass(false);
      }

      this.setEnforceFocus(false);
      this.$nextTick(function () {
        _this3.is_hidden = _this3.lazy || false;
        _this3.zIndex = ZINDEX_OFFSET;

        _this3.returnFocusTo();

        _this3.is_closing = false;
        var hiddenEvt = new _bvEvent.default('hidden', {
          cancelable: false,
          vueTarget: _this3,
          target: _this3.lazy ? null : _this3.$refs.modal,
          modalId: _this3.safeId(),
          relatedTarget: null
        });

        _this3.emitEvent(hiddenEvt);
      });
    },
    // Event emitter
    emitEvent: function emitEvent(bvEvt) {
      var type = bvEvt.type;
      this.$emit(type, bvEvt);
      this.$root.$emit("bv::modal::".concat(type), bvEvt, this.safeId());
    },
    // UI Event Handlers
    onClickOut: function onClickOut(evt) {
      // If backdrop clicked, hide modal
      if (this.is_visible && !this.noCloseOnBackdrop && !(0, _dom.contains)(this.$refs.content, evt.target)) {
        this.hide('backdrop');
      }
    },
    onEsc: function onEsc(evt) {
      // If ESC pressed, hide modal
      if (evt.keyCode === _keyCodes.default.ESC && this.is_visible && !this.noCloseOnEsc) {
        this.hide('esc');
      }
    },
    // Document focusin listener
    focusHandler: function focusHandler(evt) {
      // If focus leaves modal, bring it back
      var modal = this.$refs.modal;

      if (!this.noEnforceFocus && this.isTop && this.is_visible && modal && document !== evt.target && !(0, _dom.contains)(modal, evt.target)) {
        modal.focus({
          preventScroll: true
        });
      }
    },
    // Turn on/off focusin listener
    setEnforceFocus: function setEnforceFocus(on) {
      var options = {
        passive: true,
        capture: false
      };

      if (on) {
        (0, _dom.eventOn)(document, 'focusin', this.focusHandler, options);
      } else {
        (0, _dom.eventOff)(document, 'focusin', this.focusHandler, options);
      }
    },
    // Resize Listener
    setResizeEvent: function setResizeEvent(on)
    /* istanbul ignore next: can't easily test in JSDOM */
    {
      var _this4 = this;

      ;
      ['resize', 'orientationchange'].forEach(function (evtName) {
        var options = {
          passive: true,
          capture: false
        };

        if (on) {
          (0, _dom.eventOn)(window, evtName, _this4.adjustDialog, options);
        } else {
          (0, _dom.eventOff)(window, evtName, _this4.adjustDialog, options);
        }
      });
    },
    // Root Listener handlers
    showHandler: function showHandler(id, triggerEl) {
      if (id === this.id) {
        this.return_focus = triggerEl || null;
        this.show();
      }
    },
    hideHandler: function hideHandler(id) {
      if (id === this.id) {
        this.hide();
      }
    },
    shownHandler: function shownHandler() {
      this.setTop();
    },
    hiddenHandler: function hiddenHandler() {
      this.setTop();
    },
    setTop: function setTop() {
      // Determine if we are the topmost visible modal
      this.isTop = this.zIndex >= getModalMaxZIndex();
    },
    modalListener: function modalListener(bvEvt) {
      // If another modal opens, close this one
      if (this.noStacking && bvEvt.vueTarget !== this) {
        this.hide();
      }
    },
    // Focus control handlers
    focusFirst: function focusFirst() {
      // Don't try and focus if we are SSR
      if (typeof document === 'undefined') {
        return;
      }

      var modal = this.$refs.modal;
      var activeElement = document.activeElement;

      if (activeElement && (0, _dom.contains)(modal, activeElement)) {
        // If activeElement is child of modal or is modal, no need to change focus
        return;
      }

      if (modal) {
        // make sure top of modal is showing (if longer than the viewport) and
        // focus the modal content wrapper
        this.$nextTick(function () {
          modal.scrollTop = 0;
          modal.focus();
        });
      }
    },
    returnFocusTo: function returnFocusTo() {
      // Prefer returnFocus prop over event specified return_focus value
      var el = this.returnFocus || this.return_focus || null;

      if (typeof el === 'string') {
        // CSS Selector
        el = (0, _dom.select)(el);
      }

      if (el) {
        el = el.$el || el;

        if ((0, _dom.isVisible)(el)) {
          el.focus();
        }
      }
    },
    // Utility methods
    getScrollbarWidth: function getScrollbarWidth() {
      var scrollDiv = document.createElement('div');
      scrollDiv.className = 'modal-scrollbar-measure';
      document.body.appendChild(scrollDiv);
      this.scrollbarWidth = (0, _dom.getBCR)(scrollDiv).width - scrollDiv.clientWidth;
      document.body.removeChild(scrollDiv);
    },
    setModalOpenClass: function setModalOpenClass(open) {
      if (open) {
        (0, _dom.addClass)(document.body, 'modal-open');
      } else {
        (0, _dom.removeClass)(document.body, 'modal-open');
      }
    },
    adjustDialog: function adjustDialog() {
      if (!this.is_visible) {
        return;
      }

      var modal = this.$refs.modal;
      var isModalOverflowing = modal.scrollHeight > document.documentElement.clientHeight;

      if (!this.isBodyOverflowing && isModalOverflowing) {
        modal.style.paddingLeft = "".concat(this.scrollbarWidth, "px");
      } else {
        modal.style.paddingLeft = '';
      }

      if (this.isBodyOverflowing && !isModalOverflowing) {
        modal.style.paddingRight = "".concat(this.scrollbarWidth, "px");
      } else {
        modal.style.paddingRight = '';
      }
    },
    resetDialogAdjustments: function resetDialogAdjustments() {
      var modal = this.$refs.modal;

      if (modal) {
        modal.style.paddingLeft = '';
        modal.style.paddingRight = '';
      }
    },
    checkScrollbar: function checkScrollbar()
    /* istanbul ignore next: getBCR can't be tested in JSDOM */
    {
      var _getBCR = (0, _dom.getBCR)(document.body),
          left = _getBCR.left,
          right = _getBCR.right,
          height = _getBCR.height; // Extra check for body.height needed for stacked modals


      this.isBodyOverflowing = left + right < window.innerWidth || height > window.innerHeight;
    },
    setScrollbar: function setScrollbar() {
      /* istanbul ignore if: get Computed Style can't be tested in JSDOM */
      if (this.isBodyOverflowing) {
        // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
        //   while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
        var body = document.body;
        var scrollbarWidth = this.scrollbarWidth;
        body._paddingChangedForModal = [];
        body._marginChangedForModal = []; // Adjust fixed content padding

        (0, _dom.selectAll)(Selector.FIXED_CONTENT).forEach(function (el) {
          var actualPadding = el.style.paddingRight;
          var calculatedPadding = (0, _dom.getCS)(el).paddingRight || 0;
          (0, _dom.setAttr)(el, 'data-padding-right', actualPadding);
          el.style.paddingRight = "".concat(parseFloat(calculatedPadding) + scrollbarWidth, "px");

          body._paddingChangedForModal.push(el);
        }); // Adjust sticky content margin

        (0, _dom.selectAll)(Selector.STICKY_CONTENT).forEach(function (el) {
          var actualMargin = el.style.marginRight;
          var calculatedMargin = (0, _dom.getCS)(el).marginRight || 0;
          (0, _dom.setAttr)(el, 'data-margin-right', actualMargin);
          el.style.marginRight = "".concat(parseFloat(calculatedMargin) - scrollbarWidth, "px");

          body._marginChangedForModal.push(el);
        }); // Adjust navbar-toggler margin

        (0, _dom.selectAll)(Selector.NAVBAR_TOGGLER).forEach(function (el) {
          var actualMargin = el.style.marginRight;
          var calculatedMargin = (0, _dom.getCS)(el).marginRight || 0;
          (0, _dom.setAttr)(el, 'data-margin-right', actualMargin);
          el.style.marginRight = "".concat(parseFloat(calculatedMargin) + scrollbarWidth, "px");

          body._marginChangedForModal.push(el);
        }); // Adjust body padding

        var actualPadding = body.style.paddingRight;
        var calculatedPadding = (0, _dom.getCS)(body).paddingRight;
        (0, _dom.setAttr)(body, 'data-padding-right', actualPadding);
        body.style.paddingRight = "".concat(parseFloat(calculatedPadding) + scrollbarWidth, "px");
      }
    },
    resetScrollbar: function resetScrollbar() {
      var body = document.body;

      if (body._paddingChangedForModal) {
        // Restore fixed content padding
        body._paddingChangedForModal.forEach(function (el) {
          if ((0, _dom.hasAttr)(el, 'data-padding-right')) {
            el.style.paddingRight = (0, _dom.getAttr)(el, 'data-padding-right') || '';
            (0, _dom.removeAttr)(el, 'data-padding-right');
          }
        });
      }

      if (body._marginChangedForModal) {
        // Restore sticky content and navbar-toggler margin
        body._marginChangedForModal.forEach(function (el) {
          if ((0, _dom.hasAttr)(el, 'data-margin-right')) {
            el.style.marginRight = (0, _dom.getAttr)(el, 'data-margin-right') || '';
            (0, _dom.removeAttr)(el, 'data-margin-right');
          }
        });
      }

      body._paddingChangedForModal = null;
      body._marginChangedForModal = null; // Restore body padding

      if ((0, _dom.hasAttr)(body, 'data-padding-right')) {
        body.style.paddingRight = (0, _dom.getAttr)(body, 'data-padding-right') || '';
        (0, _dom.removeAttr)(body, 'data-padding-right');
      }
    }
  },
  render: function render(h) {
    var _this5 = this;

    var $slots = this.$slots; // Modal Header

    var header = h(false);

    if (!this.hideHeader) {
      var modalHeader = $slots['modal-header'];

      if (!modalHeader) {
        var closeButton = h(false);

        if (!this.hideHeaderClose) {
          closeButton = h('b-button-close', {
            props: {
              disabled: this.is_transitioning,
              ariaLabel: this.headerCloseLabel,
              textVariant: this.headerTextVariant
            },
            on: {
              click: function click(evt) {
                _this5.hide('headerclose');
              }
            }
          }, [$slots['modal-header-close']]);
        }

        modalHeader = [h(this.titleTag, {
          class: ['modal-title']
        }, [$slots['modal-title'] || this.titleHTML || (0, _html.stripTags)(this.title)]), closeButton];
      }

      header = h('header', {
        ref: 'header',
        staticClass: 'modal-header',
        class: this.headerClasses,
        attrs: {
          id: this.safeId('__BV_modal_header_')
        }
      }, [modalHeader]);
    } // Modal Body


    var body = h('div', {
      ref: 'body',
      staticClass: 'modal-body',
      class: this.bodyClasses,
      attrs: {
        id: this.safeId('__BV_modal_body_')
      }
    }, [$slots.default]); // Modal Footer

    var footer = h(false);

    if (!this.hideFooter) {
      var modalFooter = $slots['modal-footer'];

      if (!modalFooter) {
        var cancelButton = h(false);

        if (!this.okOnly) {
          cancelButton = h('b-button', {
            props: {
              variant: this.cancelVariant,
              size: this.buttonSize,
              disabled: this.cancelDisabled || this.busy || this.is_transitioning
            },
            on: {
              click: function click(evt) {
                _this5.hide('cancel');
              }
            }
          }, [$slots['modal-cancel'] || this.cancelTitleHTML || (0, _html.stripTags)(this.cancelTitle)]);
        }

        var okButton = h('b-button', {
          props: {
            variant: this.okVariant,
            size: this.buttonSize,
            disabled: this.okDisabled || this.busy || this.is_transitioning
          },
          on: {
            click: function click(evt) {
              _this5.hide('ok');
            }
          }
        }, [$slots['modal-ok'] || this.okTitleHTML || (0, _html.stripTags)(this.okTitle)]);
        modalFooter = [cancelButton, okButton];
      }

      footer = h('footer', {
        ref: 'footer',
        staticClass: 'modal-footer',
        class: this.footerClasses,
        attrs: {
          id: this.safeId('__BV_modal_footer_')
        }
      }, [modalFooter]);
    } // Assemble Modal Content


    var modalContent = h('div', {
      ref: 'content',
      class: this.contentClasses,
      attrs: {
        role: 'document',
        id: this.safeId('__BV_modal_content_'),
        'aria-labelledby': this.hideHeader ? null : this.safeId('__BV_modal_header_'),
        'aria-describedby': this.safeId('__BV_modal_body_')
      }
    }, [header, body, footer]); // Modal Dialog wrapper

    var modalDialog = h('div', {
      staticClass: 'modal-dialog',
      class: this.dialogClasses
    }, [modalContent]); // Modal

    var modal = h('div', {
      ref: 'modal',
      staticClass: 'modal',
      class: this.modalClasses,
      directives: [{
        name: 'show',
        rawName: 'v-show',
        value: this.is_visible,
        expression: 'is_visible'
      }],
      attrs: {
        id: this.safeId(),
        role: 'dialog',
        tabindex: '-1',
        'aria-hidden': this.is_visible ? null : 'true',
        'aria-modal': this.is_visible ? 'true' : null
      },
      on: {
        click: this.onClickOut,
        keydown: this.onEsc
      }
    }, [modalDialog]); // Wrap modal in transition

    modal = h('transition', {
      props: {
        enterClass: '',
        enterToClass: '',
        enterActiveClass: '',
        leaveClass: '',
        leaveActiveClass: '',
        leaveToClass: ''
      },
      on: {
        'before-enter': this.onBeforeEnter,
        enter: this.onEnter,
        'after-enter': this.onAfterEnter,
        'before-leave': this.onBeforeLeave,
        leave: this.onLeave,
        'after-leave': this.onAfterLeave
      }
    }, [modal]); // Modal Backdrop

    var backdrop = h(false);

    if (!this.hideBackdrop && (this.is_visible || this.is_transitioning)) {
      backdrop = h('div', {
        staticClass: 'modal-backdrop',
        class: this.backdropClasses,
        attrs: {
          id: this.safeId('__BV_modal_backdrop_')
        }
      });
    } // Tab trap to prevent page from scrolling to next element in tab index during enforce focus tab cycle


    var tabTrap = h(false);

    if (this.is_visible && this.isTop && !this.noEnforceFocus) {
      tabTrap = h('div', {
        attrs: {
          tabindex: '0'
        }
      });
    } // Assemble modal and backdrop in an outer div needed for lazy modals


    var outer = h(false);

    if (!this.is_hidden) {
      outer = h('div', {
        key: 'modal-outer',
        style: this.modalOuterStyle,
        attrs: {
          id: this.safeId('__BV_modal_outer_')
        }
      }, [modal, tabTrap, backdrop]);
    } // Wrap in DIV to maintain thi.$el reference for hide/show method aceess


    return h('div', {}, [outer]);
  }
};
exports.default = _default;