UNPKG

@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
/** * @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 };