import _ from "lodash" import React, { ReactNode } from "react" import { createRoot } from "react-dom/client" import { useEffect, useRef, useState } from "react" /** Props for PopoverHelpComponent. */ export interface PopoverHelpComponentProps { placement?: "top" | "right" | "bottom" | "left" /** hover is default */ trigger?: "hover" | "click" | "focus" /** Override content. Defaults to gray question circle */ content?: ReactNode /** Help content rendered inside the popover */ children?: ReactNode } /** Shows a popover when help icon is clicked. Needs bootstrap */ function PopoverHelpComponent(props: PopoverHelpComponentProps) { const spanRef = useRef(null) const [Popover, setPopover] = useState(null) const [isTouchDevice, setIsTouchDevice] = useState(false) // Detect touch device useEffect(() => { const checkTouchDevice = () => { setIsTouchDevice('ontouchstart' in window || navigator.maxTouchPoints > 0) } checkTouchDevice() window.addEventListener('resize', checkTouchDevice) return () => window.removeEventListener('resize', checkTouchDevice) }, []) // Load popover module (bootstrap which interferes with Node.js) useEffect(() => { import("bootstrap/js/dist/popover").then(PopoverModule => { setPopover(() => PopoverModule.default) }) }, []) useEffect(() => { const el = spanRef.current let popoverInstance: any = null let contentRoot: ReturnType | null = null if (el && Popover) { // Create div with content const contentDiv = document.createElement("div") contentRoot = createRoot(contentDiv) contentRoot.render(props.children as any) popoverInstance = new Popover(el, { content: contentDiv, trigger: props.trigger || (isTouchDevice ? "click" : "hover"), placement: props.placement || "top", html: true }) } return () => { if (popoverInstance) { popoverInstance.dispose() } if (contentRoot) { contentRoot.unmount() } } }, [Popover, props.children, props.trigger, props.placement, isTouchDevice]) return ( { props.content ? props.content : } ) } export default PopoverHelpComponent