All files / src/components/elements/Popup Popup.tsx

0% Statements 0/23
0% Branches 0/18
0% Functions 0/11
0% Lines 0/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                                                                                                                                                                                                                                       
import * as React from "react";
import styled from "styled-components";
import { Box } from "rimble-ui";
import { baseColors, colors } from "../../../themes";
 
const PopupBox = styled(Box)`
  display: none;
  position: absolute;
  right: ${(props) => props.popupRightPos}px;
  top: ${(props) => props.popupTopPos};
  background: ${baseColors.white};
  border: 1px solid ${colors.lightGray};
  border-radius: 4px;
  min-width: 180px;
  font-size: 14px;
 
  &:before {
    content: "";
    position: absolute;
    right: ${(props) => props.arrowOffset - 1}px;
    top: -10px;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
    border-bottom: 10px solid ${colors.lightGray};
  }
  &:after {
    content: "";
    position: absolute;
    right: ${(props) => props.arrowOffset}px;
    top: -9px;
    border-left: 9px solid transparent;
    border-right: 9px solid transparent;
    border-bottom: 9px solid ${baseColors.white};
  }
`;
const PopupWrap = styled(Box)`
  position: relative;
  z-index: 1;
  &:hover ${PopupBox}.hoverable, ${PopupBox}.open {
    display: block;
  }
`;
export const PopupGroup = styled.div`
  padding: 12px 16px;
  border-bottom: 1px solid ${colors.lightGray};
  &:last-child {
    border-bottom: 0;
  }
 
  & > a {
    display: block;
    margin-bottom: 12px;
    text-decoration: none;
    cursor: pointer;
    color: ${baseColors.black};
 
    &:hover {
      color: ${baseColors.blurple};
    }
    &.selected {
      cursor: default;
      font-weight: bold;
      color: ${baseColors.black};
    }
  }
  & > a:last-child {
    margin-bottom: 0;
  }
`;
 
export interface PopupProps {
  popupContents: JSX.Element;
  /** Default is trigger on hover. */
  triggerOnClick?: boolean;
  remainOpenOnClick?: boolean;
  rimbleProps?: { [prop: string]: any };
  popupRimbleProps?: { [prop: string]: any };
  /** CSS "top" property of popup, default "calc(100% + 10px)". **/
  popupTopPos?: string;
  /** CSS "right" property of popup in px, default 0. **/
  popupRightPos?: number;
  /** Offset of arrow from right edge of popup, in px. **/
  arrowOffset?: number;
}
 
export const Popup: React.FunctionComponent<PopupProps> = (props) => {
  const node = React.useRef() as React.MutableRefObject<HTMLDivElement>;
  const [isOpen, setIsOpen] = React.useState(false);
 
  const onClickOutside = React.useMemo(
    () => (e: any) => {
      if (!isOpen || node.current.contains(e.target)) {
        return;
      }
      setIsOpen(false);
    },
    [node, isOpen],
  );
  React.useEffect(() => {
    if (props.triggerOnClick) {
      document.addEventListener("mousedown", onClickOutside);
      return () => {
        document.removeEventListener("mousedown", onClickOutside);
      };
    }
  }, [onClickOutside, props.triggerOnClick]);
 
  return (
    <PopupWrap ref={node} {...props.rimbleProps} onClick={() => setIsOpen(!isOpen)}>
      {props.children}
      <PopupBox
        className={!props.triggerOnClick ? "hoverable" : isOpen ? "open" : undefined}
        arrowOffset={props.arrowOffset || 10}
        popupTopPos={props.popupTopPos || "calc(100% + 10px)"}
        popupRightPos={props.popupRightPos || 0}
        onClick={props.remainOpenOnClick && ((e: Event) => e.stopPropagation())}
        {...props.popupRimbleProps}
      >
        {props.popupContents}
      </PopupBox>
    </PopupWrap>
  );
};