{"version":3,"file":"modal.vue.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 && ($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=\"$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=\"$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        <dt-button\n          v-if=\"!hideClose\"\n          class=\"d-modal__close\"\n          circle\n          size=\"lg\"\n          importance=\"clear\"\n          :aria-label=\"closeButtonProps.ariaLabel\"\n          v-bind=\"closeButtonProps\"\n          @click=\"close\"\n        >\n          <template #icon>\n            <dt-icon-close\n              size=\"400\"\n            />\n          </template>\n        </dt-button>\n        <sr-only-close-button\n          v-if=\"showVisuallyHiddenClose\"\n          :visually-hidden-close-label=\"visuallyHiddenCloseLabel\"\n          @close=\"close\"\n        />\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/vue2';\nimport Modal from '@/common/mixins/modal';\nimport {\n  MODAL_BANNER_KINDS,\n  MODAL_KIND_MODIFIERS,\n  MODAL_SIZE_MODIFIERS,\n} from './modal_constants';\nimport { getUniqueString, disableRootScrolling, enableRootScrolling } from '@/common/utils';\nimport { DtLazyShow } from '@/components/lazy_show';\nimport { EVENT_KEYNAMES } from '@/common/constants';\nimport SrOnlyCloseButtonMixin from '@/common/mixins/sr_only_close_button';\nimport SrOnlyCloseButton from '@/common/sr_only_close_button.vue';\nimport { NOTICE_KINDS } from '@/components/notice';\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  name: 'DtModal',\n\n  components: {\n    DtLazyShow,\n    DtButton,\n    DtIconClose,\n    SrOnlyCloseButton,\n  },\n\n  mixins: [Modal, SrOnlyCloseButtonMixin],\n\n  props: {\n    /**\n     * A set of props to be passed into the modal's close button.\n     * Requires an 'ariaLabel' property.\n     */\n    closeButtonProps: {\n      type: Object,\n      required: true,\n      validator: (props) => {\n        return !!props.ariaLabel;\n      },\n    },\n\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     * 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    };\n  },\n\n  computed: {\n    modalListeners () {\n      return {\n        ...this.$listeners,\n\n        click: event => {\n          if (!this.closeOnClick) return;\n          (event.target === event.currentTarget) && this.close();\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': event => {\n          this.$emit('update:show', true);\n          (event.target === event.currentTarget) && this.setFocusAfterTransition();\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\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(this.$el.getRootNode().host);\n        } else {\n          enableRootScrolling(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    $props: {\n      immediate: true,\n      deep: true,\n      handler () {\n        this.validateProps();\n      },\n    },\n  },\n\n  methods: {\n    close () {\n      this.$emit('update:show', false);\n    },\n\n    setFocusAfterTransition () {\n      if (this.initialFocusElement === 'first') {\n        this.focusFirstElement();\n      } else if (this.initialFocusElement.startsWith('#')) {\n        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    validateProps () {\n      if (this.hideClose && !this.visuallyHiddenClose) {\n        console.error(`If hideClose prop is true, visuallyHiddenClose and visuallyHiddenCloseLabel props\n        need to be set so the component always includes a close button`);\n      }\n    },\n  },\n};\n</script>\n"],"names":["DtLazyShow","DtButton","DtIconClose","SrOnlyCloseButton","Modal","SrOnlyCloseButtonMixin","getUniqueString","MODAL_KIND_MODIFIERS","MODAL_SIZE_MODIFIERS","NOTICE_KINDS","MODAL_BANNER_KINDS","EVENT_KEYNAMES","disableRootScrolling","enableRootScrolling"],"mappings":"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"}