@progress/kendo-react-popup
Version:
React Popup positions a piece of content next to a specific anchor component. KendoReact Popup package
330 lines (329 loc) • 10.9 kB
JavaScript
/**
* @license
*-------------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the package root for more information
*-------------------------------------------------------------------------------------------
*/
import * as i from "react";
import * as Pe from "react-dom";
import t from "prop-types";
import { animations as re } from "./animation.mjs";
import { useUnstyled as Oe, useZIndexContext as Ee, canUseDOM as be, ZIndexContext as Ce, classNames as D, uPopup as T } from "@progress/kendo-react-common";
import { Collision as w, AlignPoint as c, alignElement as xe, positionElement as Ae, domUtils as De } from "@progress/kendo-popup-common";
import { throttle as Te, FRAME_DURATION as Re } from "./util.mjs";
const ze = i.createContext((l) => l), Ne = 100, Ie = 1;
function C(l, p) {
if (l === p)
return !0;
if (!!l != !!p)
return !1;
const R = Object.getOwnPropertyNames(l), z = Object.getOwnPropertyNames(p);
if (R.length !== z.length)
return !1;
for (let m = 0; m < R.length; m++) {
const h = R[m];
if (l[h] !== p[h])
return !1;
}
return !0;
}
const Le = {
left: -1e3,
top: 0
}, se = i.forwardRef((l, p) => {
const z = i.useContext(ze).call(void 0, l), {
collision: m = g.collision,
anchorAlign: h = g.anchorAlign,
popupAlign: v = g.popupAlign,
offset: N = g.offset,
animate: I = g.animate,
show: y = g.show,
margin: q = g.margin,
positionMode: V = g.positionMode,
appendTo: P,
contentKey: L,
anchor: f,
scale: ce,
role: le,
onKeyDown: ae,
onPosition: W,
onOpen: X,
onMouseDownOutside: B,
onClose: G,
className: M,
popupClass: S,
id: ue,
style: U,
children: pe
} = z, fe = Oe(), j = z.unstyled || fe, O = j && j.uPopup, [n, k] = i.useState({
current: "hidden",
previous: "hidden",
props: {}
}), x = I && typeof I != "object" ? ie : { ...ie, ...I }, J = (e) => {
window == null || window.addEventListener("mousedown", Y), H(e), Q(e.firstChild, "enter", de), k({ ...o, current: "shown", previous: o.current });
}, H = (e) => {
const { width: r, height: d } = e.style;
if (e.offsetWidth === 0 && e.offsetHeight === 0)
return;
e.style.width = e.offsetWidth + "px", e.style.height = e.offsetHeight + "px";
const a = xe({
anchor: f,
anchorAlign: h,
element: e,
elementAlign: v,
offset: N,
margin: q,
positionMode: V,
scale: ce
}), u = Ae({
anchor: f,
anchorAlign: h,
element: e,
elementAlign: v,
collisions: m,
currentLocation: a,
margin: q
});
if (e.style.top = u.offset.top + "px", e.style.left = u.offset.left + "px", e.style.width = r, e.style.height = d, _.current = {
fit: u.fit,
fitted: u.fitted,
flip: u.flip,
flipped: u.flipped
}, W) {
const A = {
target: b.current,
flipped: u.flipped,
fitted: u.fitted
};
W.call(void 0, A);
}
}, de = () => {
const e = s.current;
e && (y && e.classList.add(
...D(T.animationContainerShown({ c: O })).split(" ").filter((r) => r)
), he(e), X && X.call(void 0, { target: b.current }));
}, Q = (e, r, d) => {
if (v) {
if (x.type === "slide") {
let a;
const { horizontal: u, vertical: A } = v;
typeof l.animate == "object" && l.animate.direction ? a = x.direction : u === "left" && A === "center" ? a = "right" : u === "right" && A === "center" ? a = "left" : A === "top" ? a = "down" : a = "up";
const ye = {
down: "up",
up: "down",
left: "right",
right: "left"
};
_.current && _.current.flipped && (a = ye[a], re(
e,
ee()[r],
r,
d,
O,
x.type,
a
));
}
re(
e,
ee()[r],
r,
d,
O,
x.type,
x.direction
);
}
}, Y = (e) => {
var a;
const r = ((a = e == null ? void 0 : e.target) == null ? void 0 : a.closest(".k-animation-container")) === null, d = (f == null ? void 0 : f.contains(e == null ? void 0 : e.target)) || !1;
if (r) {
if (B) {
const u = {
target: b.current,
event: e,
state: n,
isAnchorClicked: d
};
B.call(void 0, u);
}
window == null || window.removeEventListener("mousedown", Y);
}
}, me = (e) => {
if (!y) {
const r = j && j.uPopup;
e.classList.remove(
...D(T.animationContainerShown({ c: r })).split(" ").filter((d) => d)
);
}
Z();
}, $ = () => {
o.current === "hiding" && (o.previous === "shown" || o.previous === "reposition") && k({ ...o, current: "hidden", previous: o.current }), G && G.call(void 0, { target: b.current });
}, ee = () => {
const e = I;
let r = 0, d = 0;
return e && (e === !0 ? r = d = 300 : (r = e.openDuration || 0, d = e.closeDuration || 0)), { enter: r, exit: d };
}, he = (e) => {
Z(), E.current = De.scrollableParents(f || e), E.current && E.current.map((r) => r.addEventListener("scroll", K)), window.addEventListener("resize", K);
}, Z = () => {
E.current && (E.current.map((e) => e.removeEventListener("scroll", K)), E.current = void 0), window.removeEventListener("resize", K);
}, K = Te(() => {
s.current && o.current !== "hidden" && o.current !== "hiding" && k({ ...n, current: "reposition", previous: o.current });
}, Re), ge = () => te ? te + Ie : Ne, te = Ee() || 0, _ = i.useRef(void 0), E = i.useRef(void 0), s = i.useRef(null), b = i.useRef(null), ne = i.useRef({}), F = i.useRef(!1);
i.useImperativeHandle(b, () => ({
element: s.current,
setPosition: H,
props: l
})), i.useImperativeHandle(p, () => b.current);
const o = i.useMemo(() => {
const e = {
...n,
props: {
show: y,
anchor: f,
anchorAlign: h,
appendTo: P,
collision: m,
popupAlign: v,
className: M,
popupClass: S,
style: U,
offset: N,
contentKey: L
}
};
return y ? n.current === "hidden" || n.current === "hiding" ? { ...e, current: "showing", previous: n.current } : n.current === "showing" ? { ...e, current: "shown", previous: n.current } : n.current === "shown" && (!C(N, n.props.offset) || !C(h, n.props.anchorAlign) || !C(P, n.props.appendTo) || !C(m, n.props.collision) || !C(v, n.props.popupAlign) || !C(U, n.props.style) || f !== n.props.anchor || S !== n.props.popupClass || M !== n.props.className) ? { ...e, current: "reposition", previous: n.current } : e : n.current === "hiding" || n.current === "hidden" ? { ...e, current: "hidden", previous: n.current } : { ...e, current: "hiding", previous: n.current };
}, [
y,
f,
h,
P,
m,
v,
M,
S,
U,
N,
L,
n
]);
i.useEffect(() => {
o.current === "hiding" && !F.current && s.current ? (F.current = !0, me(s.current), Q(s.current.firstChild, "exit", $)) : (F.current = !1, o.current === "showing" && s.current && !s.current.classList.contains("k-animation-container-shown") ? J(s.current) : o.current === "hiding" && s.current ? $() : o.current === "reposition" && o.previous === "shown" ? setTimeout(() => {
k({ ...o, current: "shown", previous: o.current });
}, 0) : o.current === "shown" && ne.current.contentKey !== L && s.current && H(s.current)), o.current === "reposition" && (o.previous === "shown" || o.previous === "showing") && s.current && H(s.current), ne.current = { contentKey: L };
}, [o]), i.useEffect(() => (o.current === "showing" && s.current && J(s.current), () => {
Z();
}), []);
const oe = P || (be ? f && f.ownerDocument ? f.ownerDocument.body : document.body : void 0), we = Object.assign(
{},
{ position: V, top: 0, left: -1e4 },
U || {}
), ve = o.current === "hiding";
if ((y || ve) && oe) {
const e = ge(), r = /* @__PURE__ */ i.createElement(Ce.Provider, { value: e }, /* @__PURE__ */ i.createElement(
"div",
{
onKeyDown: ae,
className: D(T.animationContainer({ c: O }), M),
id: ue,
ref: s,
style: {
zIndex: e,
...we
}
},
/* @__PURE__ */ i.createElement(
"div",
{
className: D(T.animationChild({ c: O })),
style: { transitionDelay: "0ms" }
},
/* @__PURE__ */ i.createElement("div", { role: le, className: D(T.popup({ c: O }), S) }, pe)
)
));
return P !== null ? Pe.createPortal(r, oe) : r;
}
return null;
}), g = {
collision: {
horizontal: w.fit,
vertical: w.flip
},
anchorAlign: {
horizontal: c.left,
vertical: c.bottom
},
popupAlign: {
horizontal: c.left,
vertical: c.top
},
offset: Le,
animate: !0,
show: !1,
margin: {
horizontal: 0,
vertical: 0
},
positionMode: "absolute"
}, ie = {
type: "slide",
direction: "down"
};
se.displayName = "Popup";
se.propTypes = {
anchor: function(l) {
const p = l.anchor;
return p && typeof p.nodeType != "number" ? new Error("Invalid prop `anchor` supplied to `Kendo React Popup`. Validation failed.") : null;
},
appendTo: function(l) {
const p = l.appendTo;
return p && typeof p.nodeType != "number" ? new Error("Invalid prop `appendTo` supplied to `Kendo React Popup`. Validation failed.") : null;
},
className: t.oneOfType([t.string, t.arrayOf(t.string.isRequired)]),
id: t.string,
popupClass: t.oneOfType([t.string, t.arrayOf(t.string), t.object]),
collision: t.shape({
horizontal: t.oneOf([w.fit, w.flip, w.none]),
vertical: t.oneOf([w.fit, w.flip, w.none])
}),
anchorAlign: t.shape({
horizontal: t.oneOf([c.left, c.center, c.right]),
vertical: t.oneOf([c.top, c.center, c.bottom])
}),
popupAlign: t.shape({
horizontal: t.oneOf([c.left, c.center, c.right]),
vertical: t.oneOf([c.top, c.center, c.bottom])
}),
offset: t.shape({
left: t.number,
top: t.number
}),
children: t.oneOfType([t.element, t.node]),
show: t.bool,
animate: t.oneOfType([
t.bool,
t.shape({
type: t.oneOf(["slide", "zoom", "push", "expand", "fade"]),
openDuration: t.number,
closeDuration: t.number
})
]),
margin: t.shape({
horizontal: t.number,
vertical: t.number
}),
positionMode: t.oneOf(["fixed", "absolute"]),
scale: t.number,
style: t.object,
onClose: t.func,
onPosition: t.func,
onOpen: t.func,
onKeyDown: t.func,
onMouseDownOutside: t.func
};
export {
se as Popup,
ze as PopupPropsContext,
g as popupDefaultProps
};