{"version":3,"file":"modal.cjs","sources":["../../../components/modal/modal.vue"],"sourcesContent":["<template>\n  <dt-lazy-show\n    transition=\"d-zoom\"\n    :show=\"show\"\n    :class=\"[\n      'd-modal',\n      MODAL_KIND_MODIFIERS[kind],\n      MODAL_SIZE_MODIFIERS[size],\n      modalClass,\n    ]\"\n    data-qa=\"dt-modal\"\n    :aria-hidden=\"open\"\n    v-on=\"modalListeners\"\n  >\n    <div\n      v-if=\"show && (hasSlotContent($slots.banner) || bannerTitle)\"\n      data-qa=\"dt-modal-banner\"\n      :class=\"[\n        'd-modal__banner',\n        bannerClass,\n        bannerKindClass,\n      ]\"\n    >\n      <!-- @slot Slot for the banner, defaults to bannerTitle prop -->\n      <slot name=\"banner\">\n        {{ bannerTitle }}\n      </slot>\n    </div>\n    <transition\n      appear\n      name=\"d-modal__dialog\"\n    >\n      <div\n        v-show=\"show\"\n        :class=\"[\n          'd-modal__dialog',\n          { 'd-modal__dialog--scrollable': fixedHeaderFooter },\n          dialogClass,\n        ]\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        :aria-describedby=\"describedById\"\n        :aria-labelledby=\"labelledById\"\n      >\n        <div\n          v-if=\"hasSlotContent($slots.header)\"\n          :id=\"labelledById\"\n          class=\"d-modal__header\"\n          data-qa=\"dt-modal-title\"\n        >\n          <!-- @slot Slot for dialog header section, taking the place of any \"title\" text prop -->\n          <slot name=\"header\" />\n        </div>\n        <h2\n          v-else\n          :id=\"labelledById\"\n          class=\"d-modal__header\"\n          data-qa=\"dt-modal-title\"\n        >\n          {{ title }}\n        </h2>\n        <div\n          v-if=\"hasSlotContent($slots.default)\"\n          :class=\"[\n            'd-modal__content',\n            contentClass,\n          ]\"\n          data-qa=\"dt-modal-copy\"\n        >\n          <!-- @slot Default slot for dialog body section, taking the place of any \"copy\" text prop -->\n          <slot />\n        </div>\n        <p\n          v-else\n          :class=\"[\n            'd-modal__content',\n            contentClass,\n          ]\"\n          data-qa=\"dt-modal-copy\"\n        >\n          {{ copy }}\n        </p>\n        <footer\n          v-if=\"hasFooterSlot\"\n          class=\"d-modal__footer\"\n        >\n          <!-- @slot Slot for dialog footer content, often containing cancel and confirm buttons. -->\n          <slot name=\"footer\" />\n        </footer>\n        <sr-only-close-button\n          v-if=\"hideClose\"\n          @close=\"close\"\n        />\n        <dt-button\n          v-else\n          class=\"d-modal__close\"\n          data-qa=\"dt-modal-close-button\"\n          circle\n          size=\"lg\"\n          importance=\"clear\"\n          :aria-label=\"closeButtonTitle\"\n          :title=\"closeButtonTitle\"\n          @click=\"close\"\n        >\n          <template #icon>\n            <dt-icon-close\n              size=\"400\"\n            />\n          </template>\n        </dt-button>\n      </div>\n    </transition>\n  </dt-lazy-show>\n</template>\n\n<script>\n/* eslint-disable max-lines */\nimport { DtButton } from '@/components/button';\nimport { DtIconClose } from '@dialpad/dialtone-icons/vue3';\nimport Modal from '@/common/mixins/modal';\nimport {\n  MODAL_BANNER_KINDS,\n  MODAL_KIND_MODIFIERS,\n  MODAL_SIZE_MODIFIERS,\n} from './modal_constants';\nimport { returnFirstEl, getUniqueString, hasSlotContent, disableRootScrolling, enableRootScrolling } from '@/common/utils';\nimport { DtLazyShow } from '@/components/lazy_show';\nimport { EVENT_KEYNAMES } from '@/common/constants';\nimport SrOnlyCloseButton from '@/common/sr_only_close_button.vue';\nimport { NOTICE_KINDS } from '@/components/notice';\nimport { DialtoneLocalization } from '@/localization';\n\n/**\n * Modals focus the user’s attention exclusively on one task or piece of information\n * via a window that sits on top of the page content.\n * @see https://dialtone.dialpad.com/components/modal.html\n */\nexport default {\n  compatConfig: { MODE: 3 },\n  name: 'DtModal',\n\n  components: {\n    DtLazyShow,\n    DtButton,\n    DtIconClose,\n    SrOnlyCloseButton,\n  },\n\n  mixins: [Modal],\n\n  props: {\n    /**\n     * Body text to display as the modal's main content.\n     */\n    copy: {\n      type: String,\n      default: '',\n    },\n\n    /**\n     * Id to use for the dialog's aria-describedby.\n     * Recommended only if the dialog content itself isn't enough to give full context,\n     * as screen readers should recite the dialog contents by default before any aria-description.\n     */\n    describedById: {\n      type: String,\n      default: '',\n    },\n\n    /**\n     * Id to use for the dialog's aria-labelledby.\n     */\n    labelledById: {\n      type: String,\n      default: function () { return getUniqueString(); },\n    },\n\n    /**\n     * Whether the modal should be shown.\n     * Parent component can sync on this value to control the modal's visibility.\n     * @values true, false\n     */\n    show: {\n      type: Boolean,\n      default: false,\n    },\n\n    /**\n     * Title text to display in the modal header.\n     */\n    title: {\n      type: String,\n      default: '',\n    },\n\n    /**\n     * Title text to display in the modal banner.\n     */\n    bannerTitle: {\n      type: String,\n      default: '',\n    },\n\n    /**\n     * The theme of the modal. kind - default or danger,\n     * @values default, danger\n     */\n    kind: {\n      type: String,\n      default: 'default',\n      validator: (k) => Object.keys(MODAL_KIND_MODIFIERS).includes(k),\n    },\n\n    /**\n     * The size of the modal. size - default or full,\n     * @values default, full\n     */\n    size: {\n      type: String,\n      default: 'default',\n      validator: (s) => Object.keys(MODAL_SIZE_MODIFIERS).includes(s),\n    },\n\n    /**\n     * Additional class name for the root modal element.\n     * Can accept String, Object, and Array, i.e. has the\n     * same API as Vue's built-in handling of the class attribute.\n     */\n    modalClass: {\n      type: [String, Object, Array],\n      default: '',\n    },\n\n    /**\n     * Additional class name for the dialog element within the modal.\n     * Can accept String, Object, and Array, i.e. has the\n     * same API as Vue's built-in handling of the class attribute.\n     */\n    dialogClass: {\n      type: [String, Object, Array],\n      default: '',\n    },\n\n    /**\n     * Additional class name for the content element within the modal.\n     * Can accept String, Object, and Array, i.e. has the\n     * same API as Vue's built-in handling of the class attribute.\n     */\n    contentClass: {\n      type: [String, Object, Array],\n      default: '',\n    },\n\n    /**\n     * Sets the color of the banner.\n     * @values base, error, info, success, warning\n     */\n    bannerKind: {\n      type: String,\n      default: 'warning',\n      validate (kind) {\n        return NOTICE_KINDS.includes(kind);\n      },\n    },\n\n    /**\n     * Additional class name for the banner element within the modal.\n     * Can accept String, Object, and Array, i.e. has the\n     * same API as Vue's built-in handling of the class attribute.\n     */\n    bannerClass: {\n      type: [String, Object, Array],\n      default: '',\n    },\n\n    /**\n     * Hides the close button on the modal\n     * @values true, false\n     */\n    hideClose: {\n      type: Boolean,\n      default: false,\n    },\n\n    /**\n     * Whether the modal will close when you click outside of the dialog on the overlay.\n     * @values true, false\n     */\n    closeOnClick: {\n      type: Boolean,\n      default: true,\n    },\n\n    /**\n     * Scrollable modal that allows scroll the modal content keeping the header and footer fixed\n     * @values true, false\n     */\n    fixedHeaderFooter: {\n      type: Boolean,\n      default: true,\n    },\n\n    /**\n     * The element that is focused when the modal is opened. This can be an\n     * HTMLElement within the modal, a string starting with '#' which will\n     * find the element by ID. 'first' which will automatically focus\n     * the first element, or 'dialog' which will focus the dialog window itself.\n     * If the dialog is modal this prop cannot be 'none'.\n     */\n    initialFocusElement: {\n      type: [String, HTMLElement],\n      default: 'first',\n      validator: initialFocusElement => {\n        return initialFocusElement === 'first' ||\n          (initialFocusElement instanceof HTMLElement) ||\n          initialFocusElement.startsWith('#');\n      },\n    },\n  },\n\n  emits: [\n    /**\n     * Native button click event\n     *\n     * @event click\n     * @type {PointerEvent | KeyboardEvent}\n     */\n    'click',\n\n    /**\n     * Native keydown event\n     *\n     * @event keydown\n     * @type {KeyboardEvent}\n     */\n    'keydown',\n\n    /**\n     * The modal will emit a \"false\" boolean value for this event when the user performs a modal-closing action.\n     * Parent components can sync on this value to create a 2-way binding to control modal visibility.\n     *\n     * @event update:show\n     * @type {Boolean}\n     */\n    'update:show',\n  ],\n\n  data () {\n    return {\n      MODAL_KIND_MODIFIERS,\n      MODAL_SIZE_MODIFIERS,\n      MODAL_BANNER_KINDS,\n      EVENT_KEYNAMES,\n      hasSlotContent,\n      i18n: new DialtoneLocalization(),\n    };\n  },\n\n  computed: {\n    modalListeners () {\n      return {\n        click: event => {\n          // Handle backdrop clicks for closing modal\n          if (this.closeOnClick && event.target === event.currentTarget) {\n            this.close();\n          } else if (this.show && event.target !== event.currentTarget) {\n            // Ensure focus stays within modal when clicking inside it\n            this.handleModalClick(event);\n          }\n\n          this.$emit('click', event);\n        },\n\n        keydown: event => {\n          switch (event.code) {\n            case EVENT_KEYNAMES.esc:\n            case EVENT_KEYNAMES.escape:\n              this.close();\n              break;\n            case EVENT_KEYNAMES.tab:\n              this.trapFocus(event);\n              break;\n          }\n          this.$emit('keydown', event);\n        },\n\n        'after-enter': async () => {\n          this.$emit('update:show', true);\n          await this.setFocusAfterTransition();\n        },\n\n        focusin: event => {\n          // Ensure focus stays within modal\n          if (this.show && !this.$el.contains(event.target)) {\n            event.preventDefault();\n            this.focusFirstElement();\n          }\n        },\n      };\n    },\n\n    open () {\n      return `${!this.show}`;\n    },\n\n    hasFooterSlot () {\n      return !!this.$slots.footer;\n    },\n\n    bannerKindClass () {\n      return MODAL_BANNER_KINDS[this.bannerKind];\n    },\n\n    closeButtonTitle () {\n      return this.i18n.$t('DIALTONE_CLOSE_BUTTON');\n    },\n  },\n\n  watch: {\n    show: {\n      handler (isShowing) {\n        if (isShowing) {\n          // Set a reference to the previously-active element, to which we'll return focus on modal close.\n          this.previousActiveElement = document.activeElement;\n          disableRootScrolling(returnFirstEl(this.$el).getRootNode().host);\n        } else {\n          enableRootScrolling(returnFirstEl(this.$el).getRootNode().host);\n          // Modal is being hidden, so return focus to the previously active element before clearing the reference.\n          this.previousActiveElement?.focus();\n          this.previousActiveElement = null;\n        }\n      },\n    },\n  },\n\n  methods: {\n    close () {\n      this.$emit('update:show', false);\n    },\n\n    async setFocusAfterTransition () {\n      if (this.initialFocusElement === 'first') {\n        await this.focusFirstElement();\n      } else if (this.initialFocusElement.startsWith('#')) {\n        await this.focusElementById(this.initialFocusElement);\n      } else if (this.initialFocusElement instanceof HTMLElement) {\n        this.initialFocusElement.focus();\n      }\n    },\n\n    trapFocus (e) {\n      if (this.show) {\n        this.focusTrappedTabPress(e);\n      }\n    },\n\n    handleModalClick (event) {\n      // Ensure focus stays within modal when clicking inside it\n      const clickedElement = event.target;\n      const focusableElements = this._getFocusableElements();\n\n      // If the clicked element is not focusable, ensure focus stays in modal\n      if (focusableElements.length && !focusableElements.includes(clickedElement)) {\n        // Check if current active element is still within the modal\n        if (!focusableElements.includes(document.activeElement)) {\n          this.focusFirstElement();\n        }\n      }\n    },\n  },\n};\n</script>\n"],"names":["_sfc_main","DtLazyShow","DtButton","DtIconClose","SrOnlyCloseButton","Modal","getUniqueString","k","MODAL_KIND_MODIFIERS","s","MODAL_SIZE_MODIFIERS","kind","NOTICE_KINDS","initialFocusElement","MODAL_BANNER_KINDS","EVENT_KEYNAMES","hasSlotContent","DialtoneLocalization","event","isShowing","disableRootScrolling","returnFirstEl","enableRootScrolling","_a","e","clickedElement","focusableElements","_hoisted_1","_hoisted_2","_hoisted_3","_openBlock","_createBlock","_component_dt_lazy_show","_mergeProps","$props","$data","$options","_toHandlers","_withCtx","_ctx","_createElementBlock","_normalizeClass","_renderSlot","_createTextVNode","_toDisplayString","_createCommentVNode","_createVNode","_Transition","_createElementVNode","_hoisted_4","_component_sr_only_close_button","_component_dt_button","_component_dt_icon_close"],"mappings":"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"}