UNPKG

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