import React, {cloneElement, useState} from 'react';

interface ClickOutsideProps {
  children: React.FunctionComponentElement<any>;
  onClick: (e: Event) => void;
}

const ClickOutside = ({children, onClick}) => {
  const [hasGlobalEvent, setHasGlobalEvent] = useState(false);

  const handleClick = (reactEvent: React.MouseEvent) => {
    if (hasGlobalEvent) return;

    const childrenTarget = reactEvent.currentTarget;

    const clickOutsideEventHandler = (e: MouseEvent) => {
      const outsideClick = !childrenTarget.contains(e.target as Node);

      if (onClick && outsideClick) {
        onClick(e);
        document.removeEventListener('click', clickOutsideEventHandler);
        setHasGlobalEvent(false);
      }
    };

    setHasGlobalEvent(true);
    document.addEventListener('click', clickOutsideEventHandler);
  };

  return cloneElement(children, {onClick: handleClick});
};

export default ClickOutside;
import React, {useEffect, useRef, cloneElement} from 'react';

interface ClickOutsideProps {
  children: React.FunctionComponentElement<any>;
  onClick: (e: Event) => void;
}

const ClickOutside = ({children, onClick}: ClickOutsideProps) => {
  const ref = useRef<HTMLElement>();

  useEffect(() => {
    if (!ref?.current) {
      return;
    }
    const handleClickOutside = e => {
      if (onClick && ref.current && !ref.current.contains(e.target)) {
        onClick(e);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, []);

  return <>{cloneElement(children, {ref})}</>;
};

export default ClickOutside;