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