UNPKG

@dialpad/dialtone-vue

Version:

Vue component library for Dialpad's design system Dialtone

288 lines (287 loc) 9.65 kB
import { DtIconClose as r } from "@dialpad/dialtone-icons/vue2"; import n from "../../common/mixins/modal.js"; import { MODAL_BANNER_KINDS as o, MODAL_SIZE_MODIFIERS as l, MODAL_KIND_MODIFIERS as i } from "./modal-constants.js"; import { disableRootScrolling as d, enableRootScrolling as c, getUniqueString as u } from "../../common/utils/index.js"; import { EVENT_KEYNAMES as s } from "../../common/constants/index.js"; import m from "../../shared/sr_only_close_button.js"; import { DialtoneLocalization as f } from "../../localization/index.js"; import { n as h } from "../../_plugin-vue2_normalizer-DSLOjnn3.js"; import _ from "../button/button.js"; import p from "../lazy-show/lazy-show.js"; import { NOTICE_KINDS as y } from "../notice/notice-constants.js"; const b = { name: "DtModal", components: { DtLazyShow: p, DtButton: _, DtIconClose: r, SrOnlyCloseButton: m }, mixins: [n], props: { /** * Body text to display as the modal's main content. */ copy: { type: String, default: "" }, /** * Id to use for the dialog's aria-describedby. * Recommended only if the dialog content itself isn't enough to give full context, * as screen readers should recite the dialog contents by default before any aria-description. */ describedById: { type: String, default: "" }, /** * Id to use for the dialog's aria-labelledby. */ labelledById: { type: String, default: function() { return u(); } }, /** * Whether the modal should be shown. * Parent component can sync on this value to control the modal's visibility. * @values true, false */ show: { type: Boolean, default: !1 }, /** * Title text to display in the modal header. */ title: { type: String, default: "" }, /** * Title text to display in the modal banner. */ bannerTitle: { type: String, default: "" }, /** * The theme of the modal. kind - default or danger, * @values default, danger */ kind: { type: String, default: "default", validator: (e) => Object.keys(i).includes(e) }, /** * The size of the modal. size - default or full, * @values default, full */ size: { type: String, default: "default", validator: (e) => Object.keys(l).includes(e) }, /** * Additional class name for the root modal element. * Can accept String, Object, and Array, i.e. has the * same API as Vue's built-in handling of the class attribute. */ modalClass: { type: [String, Object, Array], default: "" }, /** * Additional class name for the dialog element within the modal. * Can accept String, Object, and Array, i.e. has the * same API as Vue's built-in handling of the class attribute. */ dialogClass: { type: [String, Object, Array], default: "" }, /** * Additional class name for the content element within the modal. * Can accept String, Object, and Array, i.e. has the * same API as Vue's built-in handling of the class attribute. */ contentClass: { type: [String, Object, Array], default: "" }, /** * Sets the color of the banner. * @values base, error, info, success, warning */ bannerKind: { type: String, default: "warning", validate(e) { return y.includes(e); } }, /** * Additional class name for the banner element within the modal. * Can accept String, Object, and Array, i.e. has the * same API as Vue's built-in handling of the class attribute. */ bannerClass: { type: [String, Object, Array], default: "" }, /** * Hides the close button on the modal * @values true, false */ hideClose: { type: Boolean, default: !1 }, /** * Whether the modal will close when you click outside of the dialog on the overlay. * @values true, false */ closeOnClick: { type: Boolean, default: !0 }, /** * Scrollable modal that allows scroll the modal content keeping the header and footer fixed * @values true, false */ fixedHeaderFooter: { type: Boolean, default: !0 }, /** * The element that is focused when the modal is opened. This can be an * HTMLElement within the modal, a string starting with '#' which will * find the element by ID. 'first' which will automatically focus * the first element, or 'dialog' which will focus the dialog window itself. * If the dialog is modal this prop cannot be 'none'. */ initialFocusElement: { type: [String, HTMLElement], default: "first", validator: (e) => e === "first" || e instanceof HTMLElement || e.startsWith("#") } }, emits: [ /** * The modal will emit a "false" boolean value for this event when the user performs a modal-closing action. * Parent components can sync on this value to create a 2-way binding to control modal visibility. * * @event update:show * @type {Boolean} */ "update:show" ], data() { return { MODAL_KIND_MODIFIERS: i, MODAL_SIZE_MODIFIERS: l, MODAL_BANNER_KINDS: o, EVENT_KEYNAMES: s, i18n: new f() }; }, computed: { modalListeners() { return { ...this.$listeners, click: (e) => { this.closeOnClick && e.target === e.currentTarget ? this.close() : this.show && e.target !== e.currentTarget && this.handleModalClick(e), this.$emit("click", e); }, keydown: (e) => { switch (e.code) { case s.esc: case s.escape: this.close(); break; case s.tab: this.trapFocus(e); break; } this.$emit("keydown", e); }, "after-enter": async () => { this.$emit("update:show", !0), await this.setFocusAfterTransition(); } }; }, open() { return `${!this.show}`; }, hasFooterSlot() { return !!this.$slots.footer; }, bannerKindClass() { return o[this.bannerKind]; }, closeButtonTitle() { return this.i18n.$t("DIALTONE_CLOSE_BUTTON"); } }, watch: { show: { handler(e) { var t; e ? (this.previousActiveElement = document.activeElement, d(this.$el.getRootNode().host)) : (c(this.$el.getRootNode().host), (t = this.previousActiveElement) == null || t.focus(), this.previousActiveElement = null); } } }, methods: { close() { this.$emit("update:show", !1); }, async setFocusAfterTransition() { this.initialFocusElement === "first" ? await this.focusFirstElement() : this.initialFocusElement.startsWith("#") ? await this.focusElementById(this.initialFocusElement) : this.initialFocusElement instanceof HTMLElement && this.initialFocusElement.focus(); }, trapFocus(e) { this.show && this.focusTrappedTabPress(e); }, handleModalClick(e) { const t = e.target, a = this._getFocusableElements(); a.length && !a.includes(t) && (a.includes(document.activeElement) || this.focusFirstElement()); } } }; var g = function() { var t = this, a = t._self._c; return a("dt-lazy-show", t._g({ class: [ "d-modal", t.MODAL_KIND_MODIFIERS[t.kind], t.MODAL_SIZE_MODIFIERS[t.size], t.modalClass ], attrs: { transition: "d-zoom", show: t.show, "data-qa": "dt-modal", "aria-hidden": t.open } }, t.modalListeners), [t.show && (t.$slots.banner || t.bannerTitle) ? a("div", { class: [ "d-modal__banner", t.bannerClass, t.bannerKindClass ], attrs: { "data-qa": "dt-modal-banner" } }, [t._t("banner", function() { return [t._v(" " + t._s(t.bannerTitle) + " ")]; })], 2) : t._e(), a("transition", { attrs: { appear: "", name: "d-modal__dialog" } }, [a("div", { directives: [{ name: "show", rawName: "v-show", value: t.show, expression: "show" }], class: [ "d-modal__dialog", { "d-modal__dialog--scrollable": t.fixedHeaderFooter }, t.dialogClass ], attrs: { role: "dialog", "aria-modal": "true", "aria-describedby": t.describedById, "aria-labelledby": t.labelledById } }, [t.$slots.header ? a("div", { staticClass: "d-modal__header", attrs: { id: t.labelledById, "data-qa": "dt-modal-title" } }, [t._t("header")], 2) : a("h2", { staticClass: "d-modal__header", attrs: { id: t.labelledById, "data-qa": "dt-modal-title" } }, [t._v(" " + t._s(t.title) + " ")]), t.$slots.default ? a("div", { class: [ "d-modal__content", t.contentClass ], attrs: { "data-qa": "dt-modal-copy" } }, [t._t("default")], 2) : a("p", { class: [ "d-modal__content", t.contentClass ], attrs: { "data-qa": "dt-modal-copy" } }, [t._v(" " + t._s(t.copy) + " ")]), t.hasFooterSlot ? a("footer", { staticClass: "d-modal__footer" }, [t._t("footer")], 2) : t._e(), t.hideClose ? a("sr-only-close-button", { on: { close: t.close } }) : a("dt-button", { staticClass: "d-modal__close", attrs: { "data-qa": "dt-modal-close-button", circle: "", size: "lg", importance: "clear", "aria-label": t.closeButtonTitle, title: t.closeButtonTitle }, on: { click: t.close }, scopedSlots: t._u([{ key: "icon", fn: function() { return [a("dt-icon-close", { attrs: { size: "400" } })]; }, proxy: !0 }]) })], 1)])], 1); }, E = [], S = /* @__PURE__ */ h( b, g, E ); const M = S.exports; export { M as default }; //# sourceMappingURL=modal.js.map