import React, { useEffect, useState } from "react"; import ReactDOM from "react-dom"; import { Popper, Manager as PopperManager, Modifier, PopperProps, Reference as PopperReference, } from "react-popper"; import { buildAriaProps, buildCss, buildDataProps, noop, } from "../utilities/props"; import classnames from "classnames"; import { globalProps, GlobalProps } from "../utilities/globalProps"; import _uniqueId from 'lodash/uniqueId'; type PbPopoverProps = { aria?: { [key: string]: string }; className?: string; closeOnClick?: "outside" | "inside" | "any"; data?: { [key: string]: string }, id?: string; offset?: boolean; reference: PopperReference & any; show?: boolean; shouldClosePopover?: (arg0: boolean) => void; } & GlobalProps & Omit, 'children'> & { children?: React.ReactChild[] | React.ReactChild } // Prop enabled default modifiers here // https://popper.js.org/docs/v2/modifiers const POPOVER_MODIFIERS = { offset: { //https://popper.js.org/docs/v2/modifiers/offset/ enabled: true, name: "offset", options: { offset: [0, 20], }, phase: "main", }, }; const popoverModifiers = ({ modifiers, offset, }: { modifiers: Modifier & any; offset: {}; }) => { return offset ? modifiers.concat([POPOVER_MODIFIERS.offset]) : modifiers; }; const Popover = (props: PbPopoverProps) => { const { aria = {}, className, children, data = {}, id, modifiers, offset, placement, referenceElement, zIndex, maxHeight, maxWidth, minHeight, minWidth, targetId, } = props; const popoverSpacing = globalProps(props).includes("dark") || !globalProps(props) ? "p_sm" : globalProps(props); const overflowHandling = maxHeight || maxWidth ? "overflow_handling" : ""; const zIndexStyle = zIndex ? { zIndex: zIndex } : {}; const widthHeightStyles = () => { return Object.assign( {}, maxHeight ? { maxHeight: maxHeight } : {}, maxWidth ? { maxWidth: maxWidth } : {}, minHeight ? { minHeight: minHeight } : {}, minWidth ? { minWidth: minWidth } : {} ); }; const ariaProps = buildAriaProps(aria); const dataProps = buildDataProps(data); const classes = classnames( buildCss("pb_popover_kit"), globalProps(props), className ); return ( {({ placement, ref, style }) => { return (
{children}
); }}
); }; const PbReactPopover = (props: PbPopoverProps) => { const [targetId] = useState(_uniqueId('id-')) const { className, children, modifiers = [], offset = false, placement = "left", portal = "body", reference, referenceElement, show = false, usePortal = true, zIndex, maxHeight, maxWidth, minHeight, minWidth, } = props; useEffect(() => { const { closeOnClick, shouldClosePopover = noop } = props; if (!closeOnClick) return; document.body.addEventListener( "click", ({ target }) => { const targetIsPopover = (target as HTMLElement).closest("#" + targetId) !== null; const targetIsReference = (target as HTMLElement).closest("#reference-" + targetId) !== null; switch (closeOnClick) { case "outside": if (!targetIsPopover && !targetIsReference) { shouldClosePopover(true); } break; case "inside": if (targetIsPopover) { shouldClosePopover(true); } break; case "any": if (targetIsPopover || !targetIsPopover && !targetIsReference) { shouldClosePopover(true); } break; } }, { capture: true } ); }, []); const popoverComponent = ( {children} ); return ( <> {reference && !referenceElement && ( {({ ref }) => ( )} )} {show && (usePortal ? ( <> {ReactDOM.createPortal( popoverComponent, document.querySelector(portal) )} ) : ( { popoverComponent } ))} ); }; export default PbReactPopover;