React Custom Tooltip
Tags: react, css, webdev, javascript
Published: 2021-03-14 16:52:53

En ocasiones necesitamos dar información al usuario de la acción que ejecutara algún elemento del nuestro sitio web, una de las mejores formas de hacerlo es con un Tooltip.

Primero debemos escribir el markup de nuestro nuevo tooltip:

Necesitaremos 4 elementos como props:
- content = sera el contenido a mostrar dentro del tooltip.
- direction = definirá la posición del contenido.
- delay = definirá el tiempo antes de mostrar el contenido.
- children = sera el elemento que accionara el tooltip.


```
import React, { FunctionComponent, useState } from 'react';

//Styled Components
import {
	TooltipContainer,
	TooltipContent
} from './styles/Tooltip_Styles';

const Tooltip: FunctionComponent<TooltipProps> = ({ 
content, 
direction = 'bottom', 
delay = 300,  
children 
}) => {

  let timeout;
  const [show, setShow] = useState<boolean>(true);

  const showTip = () => {
    timeout = setTimeout(() => {
      setShow(true);
    }, delay);
  };

  const hideTip = () => {
    clearInterval(timeout);
    setShow(true);
  };

  return (
   <TooltipContainer
   onMouseEnter={showTip}
   onMouseLeave={hideTip}
   >
    {children}
    { show && (
     <TooltipContent direction={direction}>
      {content}
     </TooltipContent>
     )
    }
   </TooltipContainer>
  )
}

export default Tooltip;

```

Dentro de nuestro tooltip tenemos dos funciones:

- showTip: esta función se activara cuando el cursor este sobre el contenedor del tooltip y encargara de mostrar el contenido del tooltip que recibimos en los props, cambiando el estado `show` a `true`.

```
const showTip = () => {
    timeout = setTimeout(() => {
      setShow(true);
    }, delay);
  };
```

- hideTip: esta función se activara cuando el cursor salga del contenedor del tooltip y encargara de ocultar el contenido del tooltip, cambiando el estado `show` a `false`.

```
const hideTip = () => {
    clearInterval(timeout);
    setShow(true);
  };
```

En este punto ya tenemos la lógica para mostrar y ocultar el contenido del Tooltip, pero aún tenemos que definir la posición del contenido, y eso lo haremos con styled-components.

Pueden agregar los styled-components en el mismo archivo, pero sugiero sea en uno nuevo para tener más ordene en nuestro código.

En este nuevo archivo definiremos dos componentes:

- TooltipContainer: sera el contenedor de todo el tooltip.

- TooltipContent: sera el contenedor de la información que mostrara nuestro tooltip y la posición en la que esta información sé mostrar: top, bottom, left, right.

```
import styled, { css } from 'styled-components';

export const TooltipContainer = styled.div`
  display: inline-block;
  position: relative;
`

export const TooltipContent = styled.div`
  position: absolute;
  border-radius: 4px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px;
  color: #fff;
  background: #000;
  font-size: 14px;
  font-family: sans-serif;
  line-height: 1;
  z-index: 100;
  white-space: nowrap;
  &:before{
    content: '';
    left: 50%;
    border: solid transparent;
    height: 0;
    width: 0;
    position: absolute;
    pointer-events: none;
    border-width: 6px;
    margin-left: calc(6px * -1);
  }
  ${props => {
    if(props.direction === 'top'){
      return css`
        top: calc(20px * -1);
        &:before {
          top: 100%;
          border-top-color: #000;
        }
      `
    }
    if(props.direction === 'bottom'){
      return css`
        bottom: calc(20px * -1);
        &:before {
          bottom: 100%;
          border-bottom-color: #000;
        }
      `
    }
    if(props.direction === 'left'){
      return css`
        left: auto;
        right: calc(100% + 20px);
        top: 50%;
        transform: translateX(0) translateY(-50%);
        &:before {
          left: auto;
          right: calc(6px * -2);
          top: 50%;
          transform: translateX(0) translateY(-50%);
          border-left-color: #000;
        }
      `
    }
    if(props.direction === 'right'){
      return css`
        left: calc(100% + 20px);
        top: 50%;
        transform: translateX(0) translateY(-50%);
        &:before {
          left: calc(6px * -1);
          top: 50%;
          transform: translateX(0) translateY(-50%);
          border-right-color: #000;
        }
      `
    }
  }}
`
```