Skip to content

Instantly share code, notes, and snippets.

@none23
Created July 20, 2020 21:40
Show Gist options
  • Save none23/eeec643c7a137d9b9d95e840685d14e7 to your computer and use it in GitHub Desktop.
Save none23/eeec643c7a137d9b9d95e840685d14e7 to your computer and use it in GitHub Desktop.
Tooltip
import * as React from 'react';
import styled from 'styled-components';
interface Params {
showDelay: number;
hideDelay: number;
}
type Actions = {
show: () => void;
hide: () => void;
};
type VisibilityProps = [boolean, Actions];
function useVisibility({ showDelay, hideDelay }: Params): VisibilityProps {
const [visible, setVisible] = React.useState<boolean>(() => false);
const targetStateRef = React.useRef<boolean>(false);
const show = React.useCallback(() => {
targetStateRef.current = true;
setTimeout(() => {
if (targetStateRef.current) {
setVisible(true);
}
}, showDelay);
}, [showDelay]);
const hide = React.useCallback(() => {
targetStateRef.current = false;
setTimeout(() => {
if (!targetStateRef.current) {
setVisible(false);
}
}, hideDelay);
}, [hideDelay]);
return [visible, { show, hide }];
}
const Container = styled.span`
position: relative;
@media (max-width: 768px) {
&::before {
content: '';
display: block;
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 1;
pointer-events: none;
background: hsla(0, 0%, 0%, 0.6);
transition: opacity 0.1s ease-io-out;
pointer-events: none;
}
&.hidden::before {
visibility: hidden;
opacity: 0;
}
&.visible::before {
visibility: visible;
opacity: 1;
}
}
`;
const Target = styled.span`
padding: 20px 0;
text-decoration: underline;
text-decoration-skip-ink: none;
`;
const Popover = styled.span`
--popover-background-color: hsl(0, 0%, 100%);
position: absolute;
width: 252px;
bottom: 20px;
left: 50%;
z-index: 2;
padding: 8px 13px 8px 11px;
font-size: 11px;
box-shadow: 0 5px 15px hsla(207, 21%, 27%, 0.2);
background-color: var(--popover-background-color);
color: hsl(0, 0%, 20%);
border: none;
border-radius: 4px;
transform: translateX(-50%);
&::before {
content: '';
display: block;
position: absolute;
width: 0;
height: 0;
left: 50%;
bottom: -10px;
margin: 0;
padding: 0;
border: 0px solid transparent;
border-width: 16px 16px 0px;
border-top-color: var(--popover-background-color);
pointer-events: none;
}
&::after {
content: '';
display: block;
position: absolute;
top: -40px;
right: -40px;
bottom: -40px;
left: -40px;
pointer-events: none;
opacity: 0;
}
.hidden & {
opacity: 0;
visibility: hidden;
}
.visible & {
visibility: visible;
opacity: 1;
}
`;
interface Props {
id: string;
children: React.ReactNode;
text: string;
showDelay?: number;
hideDelay?: number;
}
function Tooltip({ id, children, text, showDelay = 40, hideDelay = 400 }: Props) {
const [visible, { show, hide }] = useVisibility({ showDelay, hideDelay });
return (
<Container className={visible ? 'visible' : 'hidden'}>
<Target
aria-describedby={id}
onMouseEnter={show}
onFocus={show}
onMouseLeave={hide}
onBlur={hide}
onClick={hide}
>
{children}
</Target>
<Popover role="tooltip" id={id}>
{text}
</Popover>
</Container>
);
}
export default Tooltip;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment