UNPKG

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