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