Skip to content

Instantly share code, notes, and snippets.

@viclafouch
Created December 18, 2020 10:52
Show Gist options
  • Select an option

  • Save viclafouch/acbcd48ad089faebc6c5d0cc92c1efcb to your computer and use it in GitHub Desktop.

Select an option

Save viclafouch/acbcd48ad089faebc6c5d0cc92c1efcb to your computer and use it in GitHub Desktop.

Revisions

  1. viclafouch created this gist Dec 18, 2020.
    107 changes: 107 additions & 0 deletions tooltip.jsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,107 @@
    import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'
    import PropTypes from 'prop-types'
    import usePortal from '@viclafouch/usePortal'
    import { Styled } from './tooltip.styled'
    import styled from 'styled-components'

    const Styled = styled.div`
    color: #ffffff;
    padding: 4px 8px;
    font-size: 1rem;
    max-width: 300px;
    word-wrap: break-word;
    border-radius: 4px;
    background-color: rgba(0, 0, 0, 0.5);
    `

    const Tooltip = ({
    children, text, space, id
    }) => {
    const [style, setStyle] = useState(null)
    const elementAttached = useRef()
    const {
    openPortal, closePortal, isOpen, Portal
    } = usePortal()

    const showTooltip = () => {
    const css = {}

    const dimensions = elementAttached.current.getBoundingClientRect()

    let left = dimensions.left + dimensions.width / 2
    left = Math.max(space, left)
    css.left = Math.min(left, document.body.clientWidth - space)

    css.bottom = window.innerHeight - dimensions.top + space

    setStyle(css)
    }

    const hideTooltip = () => {
    setStyle(null)
    }

    const handleEscap = useCallback((event) => {
    if (event.keyCode === 27) {
    setStyle(null)
    }
    }, [])

    useLayoutEffect(() => {
    if (style) {
    openPortal((portal) => {
    portal.current.setAttribute('role', 'tootlip')
    portal.current.id = id
    portal.current.style.cssText = `
    position: absolute;
    left: ${style.left}px;
    bottom: ${style.bottom}px;
    transform: translateX(-50%);
    z-index: 1500;
    `
    })
    document.addEventListener('keydown', handleEscap, false)
    } else {
    closePortal()
    document.removeEventListener('keydown', handleEscap, false)
    }
    }, [style, openPortal, closePortal, id, handleEscap])

    return (
    <span
    onMouseOver={showTooltip}
    onMouseOut={hideTooltip}
    onBlur={hideTooltip}
    onFocus={showTooltip}
    ref={elementAttached}
    aria-describedby={id}
    >
    {children}

    {isOpen && Portal(
    <Styled>
    {text}
    </Styled>
    )}
    </span>
    )
    }

    Tooltip.propTypes = {
    children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node
    ]),
    space: PropTypes.number,
    text: PropTypes.string,
    id: PropTypes.string
    }

    Tooltip.defaultProps = {
    children: null,
    space: 8,
    text: '',
    id: ''
    }

    export default Tooltip