@dialpad/dialtone-vue
Version:
Vue component library for Dialpad's design system Dialtone
582 lines (581 loc) • 17.9 kB
JavaScript
import { HTML_ELEMENT_TYPE as e } from "../../common/constants/index.js";
import { disableRootScrolling as t, enableRootScrolling as n, getUniqueString as r, hasSlotContent as i, isOutOfViewPort as a, returnFirstEl as o, warnIfUnmounted as s } from "../../common/utils/index.js";
import { t as c } from "../../_plugin-vue_export-helper-BTgDAbhb.js";
import l from "../../common/mixins/modal.js";
import u from "../../shared/sr_only_close_button.js";
import d from "../lazy-show/lazy-show.js";
import { createTippyPopover as f, getPopperOptions as p } from "./tippy-utils.js";
import { POPOVER_APPEND_TO_VALUES as m, POPOVER_BOUNDARY_VALUES as h, POPOVER_CONTENT_WIDTHS as g, POPOVER_HEADER_FOOTER_PADDING_CLASSES as _, POPOVER_INITIAL_FOCUS_STRINGS as v, POPOVER_PADDING_CLASSES as y, POPOVER_ROLES as b, POPOVER_STICKY_VALUES as x } from "./popover-constants.js";
import S from "./popover-header-footer.js";
import { Teleport as C, createBlock as w, createCommentVNode as T, createElementBlock as E, createElementVNode as D, createVNode as O, mergeProps as k, normalizeClass as A, openBlock as j, renderSlot as M, resolveComponent as N, resolveDynamicComponent as P, toHandlers as F, withCtx as I, withKeys as L, withModifiers as R } from "vue";
//#region components/popover/popover.vue
var z = {
compatConfig: { MODE: 3 },
name: "DtPopover",
components: {
SrOnlyCloseButton: u,
DtLazyShow: d,
PopoverHeaderFooter: S
},
mixins: [l],
props: {
open: {
type: Boolean,
default: null
},
openOnContext: {
type: Boolean,
default: !1
},
elementType: {
type: String,
default: "div"
},
transition: {
type: String,
default: "fade"
},
role: {
type: String,
default: "dialog",
validator: (e) => b.includes(e)
},
ariaLabelledby: {
type: String,
default: null
},
ariaLabel: {
type: String,
default: null
},
padding: {
type: String,
default: "large",
validator: (e) => Object.keys(y).some((t) => t === e)
},
contentClass: {
type: [
String,
Array,
Object
],
default: ""
},
contentWidth: {
type: String,
default: "",
validator: (e) => g.includes(e)
},
contentAppear: {
type: Boolean,
default: null
},
contentTabindex: {
type: Number || null,
default: -1
},
externalAnchor: {
type: String,
default: ""
},
externalAnchorElement: {
type: e,
default: null
},
id: {
type: String,
default() {
return r();
}
},
offset: {
type: Array,
default: () => [0, 4]
},
hideOnClick: {
type: Boolean,
default: !0
},
modal: {
type: Boolean,
default: !0
},
fallbackPlacements: {
type: Array,
default: () => ["auto"]
},
boundary: {
type: [String, e],
default: "clippingParents",
validator: (e) => h.includes(e) || e instanceof HTMLElement
},
placement: {
type: String,
default: "bottom-end"
},
tether: {
type: Boolean,
default: !0
},
sticky: {
type: [Boolean, String],
default: !1,
validator: (e) => x.includes(e)
},
maxHeight: {
type: String,
default: ""
},
maxWidth: {
type: String,
default: ""
},
showCloseButton: {
type: Boolean,
default: !1
},
headerClass: {
type: [
String,
Array,
Object
],
default: ""
},
footerClass: {
type: [
String,
Array,
Object
],
default: ""
},
dialogClass: {
type: [
String,
Array,
Object
],
default: ""
},
initialFocusElement: {
type: [String, e],
default: "first",
validator: (e) => v.includes(e) || e instanceof HTMLElement || e.startsWith("#")
},
openWithArrowKeys: {
type: Boolean,
default: !1
},
appendTo: {
type: [e, String],
default: "body",
validator: (e) => m.includes(e) || e instanceof HTMLElement
}
},
emits: [
"keydown",
"update:open",
"opened",
"mouseenter-popover",
"mouseleave-popover",
"mouseenter-popover-anchor",
"mouseleave-popover-anchor"
],
data() {
return {
POPOVER_PADDING_CLASSES: y,
POPOVER_HEADER_FOOTER_PADDING_CLASSES: _,
intersectionObserver: null,
mutationObserver: null,
isOutsideViewport: !1,
isOpen: !1,
toAppear: !1,
anchorEl: null,
popoverContentEl: null,
hasSlotContent: i
};
},
computed: {
popoverListeners() {
return {
keydown: (e) => {
this.onKeydown(e);
},
"after-leave": () => {
this.onLeaveTransitionComplete();
},
"after-enter": () => {
this.onEnterTransitionComplete();
}
};
},
calculatedMaxHeight() {
return this.isOutsideViewport && this.modal ? "calc(100vh - var(--dt-space-300))" : this.maxHeight;
},
labelledBy() {
return this.ariaLabelledby || !this.ariaLabel && r("DtPopover__anchor");
}
},
watch: {
$props: {
immediate: !0,
deep: !0,
handler() {
this.validateProps();
}
},
modal(e) {
this.tip?.setProps({ zIndex: e ? 650 : this.calculateAnchorZindex() });
},
offset(e) {
this.tip?.setProps({ offset: e });
},
sticky(e) {
this.tip?.setProps({ sticky: e });
},
fallbackPlacements() {
this.tip?.setProps({ popperOptions: this.popperOptions() });
},
tether() {
this.tip?.setProps({ popperOptions: this.popperOptions() });
},
boundary() {
this.tip?.setProps({ popperOptions: this.popperOptions() });
},
externalAnchorElement() {
this.updateAnchorEl();
},
placement(e) {
this.tip?.setProps({ placement: e });
},
open: {
handler: function(e) {
e !== null && (this.isOpen = e), e === !0 && (this.toAppear = !0);
},
immediate: !0
},
contentAppear: { handler: function(e) {
e !== null && (this.toAppear = e);
} },
isOpen(e, t) {
e ? (this.initTippyInstance(), this.tip?.show()) : !e && t !== e && (this.removeEventListeners(), this.tip?.hide());
}
},
mounted() {
s(o(this.$el), this.$options.name), this.popoverContentEl = o(this.$refs.content?.$el), this.updateAnchorEl(), this.mutationObserver = new MutationObserver(this.updateAnchorEl), this.mutationObserver.observe(this.$refs.anchor, { childList: !0 }), this.intersectionObserver = new IntersectionObserver(this.hasIntersectedViewport), this.intersectionObserver.observe(this.popoverContentEl);
},
beforeUnmount() {
this._isUnmounting = !0, this.popoverContentEl && (this.popoverContentEl.style.transition = "none"), this.tip?.destroy(), this.intersectionObserver?.disconnect(), this.mutationObserver?.disconnect(), this.removeReferences(), this.removeEventListeners();
},
methods: {
hasIntersectedViewport(e) {
let t = e?.[0]?.target;
if (!t) return;
let n = a(t);
this.isOutsideViewport = n.bottom || n.top;
},
updateAnchorEl() {
let e = (this.externalAnchorElement || (this.externalAnchor ? this.$refs.anchor.getRootNode().querySelector(`#${this.externalAnchor}`) : null)) ?? this.$refs.anchor.children[0];
if (e !== this.anchorEl) {
if (this.anchorEl = e, this.tip?.destroy(), delete this.tip, !this.anchorEl) {
console.warn("No anchor found for popover");
return;
}
this.isOpen && (this.initTippyInstance(), this.tip?.show());
}
},
popperOptions() {
return p({
fallbackPlacements: this.fallbackPlacements,
tether: this.tether,
hasHideModifierEnabled: !0,
boundary: this.boundary
});
},
validateProps() {
this.modal && this.initialFocusElement === "none" && console.error("If the popover is modal you must set the initialFocusElement prop. Possible values: \"dialog\", \"first\", HTMLElement");
},
calculateAnchorZindex() {
return o(this.$el).getRootNode().querySelector(".d-modal[aria-hidden=\"false\"], .d-modal--transparent[aria-hidden=\"false\"]") || this.anchorEl?.closest(".d-zi-drawer") ? 650 : 300;
},
defaultToggleOpen(e) {
if (!this.openOnContext && (this.open === null || this.open === void 0)) {
if (!this.anchorEl?.contains(e.target) && !this.anchorEl?.isEqualNode(e.target) || this.anchorEl?.disabled) return;
this.toggleOpen();
}
},
async onContext(e) {
this.openOnContext && (e.preventDefault(), this.isOpen = !0, await this.$nextTick(), this.tip?.setProps({
placement: "right-start",
getReferenceClientRect: () => ({
width: 0,
height: 0,
top: e.clientY,
bottom: e.clientY,
left: e.clientX,
right: e.clientX
})
}));
},
toggleOpen() {
this.isOpen = !this.isOpen;
},
onArrowKeyPress(e) {
this.open === null && (this.openWithArrowKeys && this.anchorEl?.contains(e.target) && (this.isOpen || (this.isOpen = !0)), this.$emit("keydown", e));
},
addEventListeners() {
window.addEventListener("dt-popover-close", this.closePopover), this.contentWidth === "anchor" && window.addEventListener("resize", this.onResize);
},
removeEventListeners() {
window.removeEventListener("dt-popover-close", this.closePopover), this.contentWidth === "anchor" && window.removeEventListener("resize", this.onResize);
},
closePopover() {
this.isOpen = !1;
},
preventScrolling() {
if (this.modal) {
let e = this.anchorEl?.closest("body, .tippy-box");
if (!e) return;
e.tagName?.toLowerCase() === "body" ? (t(this.anchorEl.getRootNode().host), this.tip?.setProps({ offset: this.offset })) : e.classList.add("d-zi-popover");
}
},
enableScrolling() {
let e = this.anchorEl?.closest("body, .tippy-box");
e && (e.tagName?.toLowerCase() === "body" ? (n(this.anchorEl.getRootNode().host), this.tip?.setProps({ offset: this.offset })) : e.classList.remove("d-zi-popover"));
},
removeReferences() {
this.anchorEl = null, this.popoverContentEl = null, this.tip = null;
},
async onShow() {
this.contentWidth === "anchor" && (await this.setPopoverContentAnchorWidth(), this._isUnmounting) || (this.contentWidth === null && (this.popoverContentEl.style.width = "auto"), this.addEventListeners());
},
async onLeaveTransitionComplete() {
if (!this._isUnmounting) {
if (this.modal) {
if (await this.focusFirstElement(this.$refs.anchor), this._isUnmounting || (await this.$nextTick(), this._isUnmounting)) return;
this.enableScrolling();
}
this._isUnmounting || (this.tip?.unmount(), this.$emit("opened", !1), this.open !== null && this.$emit("update:open", !1));
}
},
async onEnterTransitionComplete() {
this._isUnmounting || (this.focusInitialElement(), await this.$nextTick(), !this._isUnmounting && (this.preventScrolling(), this.$emit("opened", !0, this.$refs.popover__content), this.open !== null && this.$emit("update:open", !0)));
},
focusInitialElement() {
this.initialFocusElement === "dialog" && o(this.$refs.content?.$el)?.focus(), this.initialFocusElement.startsWith("#") && this.focusInitialElementById(), this.initialFocusElement === "first" && this.focusFirstElementIfNeeded(this.$refs.popover__content), this.initialFocusElement instanceof HTMLElement && this.initialFocusElement.focus();
},
focusInitialElementById() {
let e = o(this.$refs.content?.$el)?.querySelector(this.initialFocusElement);
e ? e.focus() : (console.warn("Could not find the element specified in dt-popover prop \"initialFocusElement\". Defaulting to focusing the dialog."), o(this.$refs.content?.$el)?.focus());
},
onResize() {
this.closePopover();
},
onClickOutside() {
this.hideOnClick && (this.popoverContentEl?.querySelector(".d-popover__anchor--opened") || this.closePopover());
},
onKeydown(e) {
e.key === "Tab" && this.modal && this.focusTrappedTabPress(e, this.popoverContentEl), e.key === "Escape" && this.closePopover(), this.$emit("keydown", e);
},
async setPopoverContentAnchorWidth() {
await this.$nextTick(), this.popoverContentEl.style.width = `${this.anchorEl?.clientWidth}px`;
},
focusFirstElementIfNeeded(e) {
this._getFocusableElements(e, !0).length === 0 ? this.showCloseButton ? this.$refs.popover__header?.focusCloseButton() : o(this.$refs.content?.$el).focus() : this.focusFirstElement(e);
},
getReferenceClientRect(e) {
let t = this.anchorEl?.getBoundingClientRect();
if (this.appendTo !== "root" || e) return t;
let n = this.anchorEl?.ownerDocument, r = (n?.defaultView || n?.parentWindow)?.frameElement;
if (!r) return t;
let i = r.getBoundingClientRect();
return {
width: t?.width,
height: t?.height,
top: i?.top + t?.top,
left: i?.left + t?.left,
right: i?.right + t?.right,
bottom: i?.bottom + t?.bottom
};
},
initTippyInstance() {
let e = null, t = !1;
switch (this.appendTo) {
case "body":
e = this.anchorEl?.getRootNode()?.querySelector("body");
break;
case "root":
try {
e = window.parent.document.body;
} catch (n) {
console.error("Could not attach the popover to iframe parent window: ", n), e = "parent", t = !0;
}
break;
default:
e = this.appendTo;
break;
}
this.tip?.destroy(), this.tip = f(this.anchorEl, {
popperOptions: this.popperOptions(),
contentElement: this.popoverContentEl,
placement: this.placement,
offset: this.offset,
sticky: this.sticky,
appendTo: e,
interactive: !0,
trigger: "manual",
getReferenceClientRect: () => this.getReferenceClientRect(t),
hideOnClick: !1,
zIndex: this.modal ? 650 : this.calculateAnchorZindex(),
onClickOutside: this.onClickOutside,
onShow: this.onShow
});
},
onMouseEnter() {
this.$emit("mouseenter-popover");
},
onMouseLeave() {
this.$emit("mouseleave-popover");
},
onMouseEnterAnchor() {
this.$emit("mouseenter-popover-anchor");
},
onMouseLeaveAnchor() {
this.$emit("mouseleave-popover-anchor");
}
}
}, B = [
"id",
"data-qa",
"tabindex"
], V = ["data-qa"];
function H(e, t, n, r, i, a) {
let o = N("popover-header-footer"), s = N("sr-only-close-button"), c = N("dt-lazy-show");
return j(), E("div", null, [n.modal && i.isOpen ? (j(), w(C, {
key: 0,
to: "body"
}, [D("div", {
class: "d-modal--transparent",
"aria-hidden": "false",
onClick: t[0] || (t[0] = R(() => {}, ["prevent", "stop"]))
})])) : T("", !0), (j(), w(P(n.elementType), {
ref: "popover",
class: A(["d-popover", { "d-popover__anchor--opened": i.isOpen }]),
"data-qa": "dt-popover-container"
}, {
default: I(() => [D("div", {
id: !n.ariaLabelledby && a.labelledBy,
ref: "anchor",
"data-qa": e.$attrs["data-qa"] ? `${e.$attrs["data-qa"]}-anchor` : "dt-popover-anchor",
tabindex: n.openOnContext ? 0 : void 0,
onClickCapture: t[1] || (t[1] = (...e) => a.defaultToggleOpen && a.defaultToggleOpen(...e)),
onContextmenu: t[2] || (t[2] = (...e) => a.onContext && a.onContext(...e)),
onKeydown: [
t[3] || (t[3] = L(R((...e) => a.onArrowKeyPress && a.onArrowKeyPress(...e), ["prevent"]), ["up"])),
t[4] || (t[4] = L(R((...e) => a.onArrowKeyPress && a.onArrowKeyPress(...e), ["prevent"]), ["down"])),
t[6] || (t[6] = L((t) => e.$emit("keydown", t), ["enter"])),
t[7] || (t[7] = L((t) => e.$emit("keydown", t), ["space"]))
],
onKeydownCapture: t[5] || (t[5] = L((...e) => a.closePopover && a.closePopover(...e), ["escape"])),
onMouseenter: t[8] || (t[8] = (...e) => a.onMouseEnter && a.onMouseEnter(...e)),
onMouseleave: t[9] || (t[9] = (...e) => a.onMouseLeave && a.onMouseLeave(...e))
}, [M(e.$slots, "anchor", { attrs: {
"aria-expanded": i.isOpen.toString(),
"aria-controls": n.id,
"aria-haspopup": n.role
} })], 40, B), O(c, k({
id: n.id,
ref: "content",
role: n.role,
"data-qa": e.$attrs["data-qa"] ? `${e.$attrs["data-qa"]}__dialog` : "dt-popover",
"aria-hidden": `${!i.isOpen}`,
"aria-labelledby": a.labelledBy,
"aria-label": n.ariaLabel,
"aria-modal": `${!n.modal}`,
transition: n.transition,
show: i.isOpen,
appear: i.toAppear,
class: [
"d-popover__dialog",
{ "d-popover__dialog--modal": n.modal },
n.dialogClass
],
style: {
"max-height": a.calculatedMaxHeight,
"max-width": n.maxWidth
},
css: e.$attrs.css,
tabindex: n.contentTabindex
}, F(a.popoverListeners), {
onMouseenter: a.onMouseEnterAnchor,
onMouseleave: a.onMouseLeaveAnchor
}), {
default: I(() => [
i.hasSlotContent(e.$slots.headerContent) || n.showCloseButton ? (j(), w(o, {
key: 0,
ref: "popover__header",
class: A(i.POPOVER_HEADER_FOOTER_PADDING_CLASSES[n.padding]),
"content-class": n.headerClass,
type: "header",
"show-close-button": n.showCloseButton,
onClose: a.closePopover
}, {
content: I(() => [M(e.$slots, "headerContent", { close: a.closePopover })]),
_: 3
}, 8, [
"class",
"content-class",
"show-close-button",
"onClose"
])) : T("", !0),
D("div", {
ref: "popover__content",
"data-qa": e.$attrs["data-qa"] ? `${e.$attrs["data-qa"]}-content` : "dt-popover-content",
class: A([
"d-popover__content",
i.POPOVER_PADDING_CLASSES[n.padding],
n.contentClass
])
}, [M(e.$slots, "content", { close: a.closePopover })], 10, V),
i.hasSlotContent(e.$slots.footerContent) ? (j(), w(o, {
key: 1,
ref: "popover__footer",
type: "footer",
class: A(i.POPOVER_HEADER_FOOTER_PADDING_CLASSES[n.padding]),
"content-class": n.footerClass
}, {
content: I(() => [M(e.$slots, "footerContent", { close: a.closePopover })]),
_: 3
}, 8, ["class", "content-class"])) : T("", !0),
n.showCloseButton ? T("", !0) : (j(), w(s, {
key: 2,
onClose: a.closePopover
}, null, 8, ["onClose"]))
]),
_: 3
}, 16, [
"id",
"role",
"data-qa",
"aria-hidden",
"aria-labelledby",
"aria-label",
"aria-modal",
"transition",
"show",
"appear",
"class",
"style",
"css",
"tabindex",
"onMouseenter",
"onMouseleave"
])]),
_: 3
}, 8, ["class"]))]);
}
var U = /* @__PURE__ */ c(z, [["render", H]]);
//#endregion
export { U as default };
//# sourceMappingURL=popover.js.map