@dialpad/dialtone
Version:
Dialpad's Dialtone design system monorepo
572 lines (571 loc) • 17.6 kB
JavaScript
import { disableRootScrolling as e, enableRootScrolling as t, getUniqueString as n, hasSlotContent as r, isOutOfViewPort as i, returnFirstEl as a, warnIfUnmounted as o } from "../../common/utils/index.js";
import { t as s } from "../../_plugin-vue_export-helper-BTgDAbhb.js";
import c from "../../common/mixins/modal.js";
import l from "../../shared/sr_only_close_button.js";
import u from "../lazy-show/lazy-show.js";
import { createTippyPopover as d, getPopperOptions as f } from "./tippy-utils.js";
import { POPOVER_APPEND_TO_VALUES as p, POPOVER_CONTENT_WIDTHS as m, POPOVER_HEADER_FOOTER_PADDING_CLASSES as h, POPOVER_INITIAL_FOCUS_STRINGS as g, POPOVER_PADDING_CLASSES as _, POPOVER_ROLES as v, POPOVER_STICKY_VALUES as y } from "./popover-constants.js";
import b from "./popover-header-footer.js";
import { Teleport as x, createBlock as S, createCommentVNode as C, createElementBlock as w, createElementVNode as T, createVNode as E, mergeProps as D, normalizeClass as O, openBlock as k, renderSlot as A, resolveComponent as j, resolveDynamicComponent as M, toHandlers as N, withCtx as P, withKeys as F, withModifiers as I } from "vue";
//#region components/popover/popover.vue
var L = {
compatConfig: { MODE: 3 },
name: "DtPopover",
components: {
SrOnlyCloseButton: l,
DtLazyShow: u,
PopoverHeaderFooter: b
},
mixins: [c],
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) => v.includes(e)
},
ariaLabelledby: {
type: String,
default: null
},
ariaLabel: {
type: String,
default: null
},
padding: {
type: String,
default: "large",
validator: (e) => Object.keys(_).some((t) => t === e)
},
contentClass: {
type: [
String,
Array,
Object
],
default: ""
},
contentWidth: {
type: String,
default: "",
validator: (e) => m.includes(e)
},
contentAppear: {
type: Boolean,
default: null
},
contentTabindex: {
type: Number || null,
default: -1
},
externalAnchor: {
type: String,
default: ""
},
externalAnchorElement: {
type: HTMLElement,
default: null
},
id: {
type: String,
default() {
return n();
}
},
offset: {
type: Array,
default: () => [0, 4]
},
hideOnClick: {
type: Boolean,
default: !0
},
modal: {
type: Boolean,
default: !0
},
fallbackPlacements: {
type: Array,
default: () => ["auto"]
},
placement: {
type: String,
default: "bottom-end"
},
tether: {
type: Boolean,
default: !0
},
sticky: {
type: [Boolean, String],
default: !1,
validator: (e) => y.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, HTMLElement],
default: "first",
validator: (e) => g.includes(e) || e instanceof HTMLElement || e.startsWith("#")
},
openWithArrowKeys: {
type: Boolean,
default: !1
},
appendTo: {
type: [HTMLElement, String],
default: "body",
validator: (e) => p.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: _,
POPOVER_HEADER_FOOTER_PADDING_CLASSES: h,
intersectionObserver: null,
mutationObserver: null,
isOutsideViewport: !1,
isOpen: !1,
toAppear: !1,
anchorEl: null,
popoverContentEl: null,
hasSlotContent: r
};
},
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 && n("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() });
},
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() {
o(a(this.$el), this.$options.name), this.popoverContentEl = a(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 = i(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 f({
fallbackPlacements: this.fallbackPlacements,
tether: this.tether,
hasHideModifierEnabled: !0
});
},
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 a(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 t = this.anchorEl?.closest("body, .tippy-box");
if (!t) return;
t.tagName?.toLowerCase() === "body" ? (e(this.anchorEl.getRootNode().host), this.tip?.setProps({ offset: this.offset })) : t.classList.add("d-zi-popover");
}
},
enableScrolling() {
let e = this.anchorEl?.closest("body, .tippy-box");
e && (e.tagName?.toLowerCase() === "body" ? (t(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.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" && a(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 = a(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."), a(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() : a(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 = d(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");
}
}
}, R = [
"id",
"data-qa",
"tabindex"
], z = ["data-qa"];
function B(e, t, n, r, i, a) {
let o = j("popover-header-footer"), s = j("sr-only-close-button"), c = j("dt-lazy-show");
return k(), w("div", null, [n.modal && i.isOpen ? (k(), S(x, {
key: 0,
to: "body"
}, [T("div", {
class: "d-modal--transparent",
"aria-hidden": "false",
onClick: t[0] || (t[0] = I(() => {}, ["prevent", "stop"]))
})])) : C("", !0), (k(), S(M(n.elementType), {
ref: "popover",
class: O(["d-popover", { "d-popover__anchor--opened": i.isOpen }]),
"data-qa": "dt-popover-container"
}, {
default: P(() => [T("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] = F(I((...e) => a.onArrowKeyPress && a.onArrowKeyPress(...e), ["prevent"]), ["up"])),
t[4] || (t[4] = F(I((...e) => a.onArrowKeyPress && a.onArrowKeyPress(...e), ["prevent"]), ["down"])),
t[6] || (t[6] = F((t) => e.$emit("keydown", t), ["enter"])),
t[7] || (t[7] = F((t) => e.$emit("keydown", t), ["space"]))
],
onKeydownCapture: t[5] || (t[5] = F((...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))
}, [A(e.$slots, "anchor", { attrs: {
"aria-expanded": i.isOpen.toString(),
"aria-controls": n.id,
"aria-haspopup": n.role
} })], 40, R), E(c, D({
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
}, N(a.popoverListeners), {
onMouseenter: a.onMouseEnterAnchor,
onMouseleave: a.onMouseLeaveAnchor
}), {
default: P(() => [
i.hasSlotContent(e.$slots.headerContent) || n.showCloseButton ? (k(), S(o, {
key: 0,
ref: "popover__header",
class: O(i.POPOVER_HEADER_FOOTER_PADDING_CLASSES[n.padding]),
"content-class": n.headerClass,
type: "header",
"show-close-button": n.showCloseButton,
onClose: a.closePopover
}, {
content: P(() => [A(e.$slots, "headerContent", { close: a.closePopover })]),
_: 3
}, 8, [
"class",
"content-class",
"show-close-button",
"onClose"
])) : C("", !0),
T("div", {
ref: "popover__content",
"data-qa": e.$attrs["data-qa"] ? `${e.$attrs["data-qa"]}-content` : "dt-popover-content",
class: O([
"d-popover__content",
i.POPOVER_PADDING_CLASSES[n.padding],
n.contentClass
])
}, [A(e.$slots, "content", { close: a.closePopover })], 10, z),
i.hasSlotContent(e.$slots.footerContent) ? (k(), S(o, {
key: 1,
ref: "popover__footer",
type: "footer",
class: O(i.POPOVER_HEADER_FOOTER_PADDING_CLASSES[n.padding]),
"content-class": n.footerClass
}, {
content: P(() => [A(e.$slots, "footerContent", { close: a.closePopover })]),
_: 3
}, 8, ["class", "content-class"])) : C("", !0),
n.showCloseButton ? C("", !0) : (k(), S(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 V = /* @__PURE__ */ s(L, [["render", B]]);
//#endregion
export { V as default };
//# sourceMappingURL=popover.js.map