@navinc/base-react-components
Version:
Nav's Pattern Library
215 lines (203 loc) • 6.36 kB
JavaScript
import React, { useState } from 'react'
import MaterialTooltip from '@material-ui/core/Tooltip/index.js'
import { makeStyles } from '@material-ui/core/styles/index.js'
import styled from 'styled-components'
import Icon from './icon.js'
import { theme } from './theme.js'
import PropTypes from 'prop-types'
import isRebrand from './is-rebrand.js'
const arrowGenerator = () => {
return {
'&[x-placement*="top"] $arrow': {
bottom: 0,
left: 0,
marginBottom: '12px',
marginTop: '12px',
overflow: 'hidden',
height: '12px',
transition: 'none',
transform: 'translateY(4px)',
'&::before': {
width: '12px',
height: '12px',
boxShadow: `1px 2px 4px 2px ${({ theme }) => (isRebrand(theme) ? theme.navNeutral300 : theme.neutral300)}`,
transform: 'translate(15px, -6px) rotate(45deg)',
},
},
'&[x-placement*="right"] $arrow': {
left: 0,
marginLeft: '12px',
marginRight: '12px',
width: '12px',
overflow: 'hidden',
transform: 'translateX(-10px)',
transition: 'none',
'&::before': {
width: '12px',
height: '12px',
boxShadow: `1px 2px 4px 2px ${({ theme }) => (isRebrand(theme) ? theme.navNeutral300 : theme.neutral300)}`,
transform: 'translate(6px, 12px) rotate(45deg)',
},
},
}
}
const useStylesArrow = makeStyles(() => ({
arrow: {
position: 'absolute',
width: '42px',
height: '42px',
transition: 'none',
'&::before': {
content: '""',
width: '12px',
height: '12px',
boxShadow: `1px 2px 4px 2px ${({ theme }) => (isRebrand(theme) ? theme.navNeutral300 : theme.neutral300)}`,
backgroundColor: 'white',
position: 'absolute',
},
},
popper: arrowGenerator(),
tooltip: {
color: '#70727B',
fontSize: '12px',
lineHeight: '18px',
backgroundColor: 'white',
boxShadow: `1px 2px 4px 2px ${({ theme }) => (isRebrand(theme) ? theme.navNeutral300 : theme.neutral300)}`,
width: '198px',
marginBottom: '20px',
},
}))
const ArrowTooltip = ({ title, ...props }) => {
const { arrow, ...classes } = useStylesArrow()
const [arrowRef, setArrowRef] = useState(null)
return (
<MaterialTooltip
TransitionProps={{ timeout: 0 }}
classes={classes}
PopperProps={{
popperOptions: {
modifiers: {
arrow: {
enabled: Boolean(arrowRef),
element: arrowRef,
},
},
},
}}
{...props}
title={
<>
{title}
<span className={arrow} ref={setArrowRef} />
</>
}
/>
)
}
const RightTooltip = styled(ArrowTooltip)`
display: none;
@media (${theme.forLargerThanPhone}) {
display: inline-block;
}
`
const TopTooltip = styled(ArrowTooltip)`
display: inline-block;
@media (${theme.forLargerThanPhone}) {
display: none;
}
`
const Wrapper = styled.span``
export const Tooltip = ({
children,
className,
iconName = 'actions/circle-info',
disableFocusListener = false,
disableHoverListener = false,
disableTouchListener = false,
enterDelay = 100,
enterTouchDelay = 700,
enterNextDelay = 0,
interactive = false,
leaveDelay = 0,
leaveTouchDelay = 1500,
onClose = () => {},
onOpen = () => {},
open,
...props
}) => (
<Wrapper className={className}>
<RightTooltip
title={children}
placement="right"
disableFocusListener={disableFocusListener}
disableHoverListener={disableHoverListener}
disableTouchListener={disableTouchListener}
enterDelay={enterDelay}
enterTouchDelay={enterTouchDelay}
enterNextDelay={enterNextDelay}
interactive={interactive}
leaveDelay={leaveDelay}
leaveTouchDelay={leaveTouchDelay}
onClose={onClose}
onOpen={onOpen}
open={open}
>
<div>
<Icon name={iconName} {...props} />
</div>
</RightTooltip>
<TopTooltip
title={children}
placement="top"
disableFocusListener={disableFocusListener}
disableHoverListener={disableHoverListener}
disableTouchListener={disableTouchListener}
enterDelay={enterDelay}
enterTouchDelay={enterTouchDelay}
enterNextDelay={enterNextDelay}
interactive={interactive}
leaveDelay={leaveDelay}
leaveTouchDelay={leaveTouchDelay}
onClose={onClose}
onOpen={onOpen}
open={open}
>
<div>
<Icon name={iconName} {...props} />
</div>
</TopTooltip>
</Wrapper>
)
Tooltip.propTypes = {
/** Add child components. */
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
/** Set the class names provided to Tooltip. */
className: PropTypes.string,
/** Set the name of the icon to show (same as Icon component uses). */
iconName: PropTypes.string,
/** Do not respond to focus events */
disableFocusListener: PropTypes.bool,
/** Do not respond to hover events */
disableHoverListener: PropTypes.bool,
/** Do not respond to long press touch events */
disableTouchListener: PropTypes.bool,
/** The number of milliseconds to wait before showing the tooltip. This prop won't impact the enter touch delay (`enterTouchDelay`). */
enterDelay: PropTypes.number,
/** The number of milliseconds a user must touch the element before showing the tooltip. */
enterTouchDelay: PropTypes.number,
/** The number of milliseconds to wait before showing the tooltip when one was already recently opened. */
enterNextDelay: PropTypes.number,
/** Makes a tooltip interactive, i.e. will not close when the user hovers over the tooltip before the `leaveDelay` is expired. */
interactive: PropTypes.bool,
/** The number of milliseconds to wait before hiding the tooltip. This prop won't impact the leave touch delay (`leaveTouchDelay`). */
leaveDelay: PropTypes.number,
/** The number of milliseconds after the user stops touching an element before hiding the tooltip. */
leaveTouchDelay: PropTypes.number,
/** Callback fired when the component requests to be closed. */
onClose: PropTypes.func,
/** Callback fired when the component requests to be open. */
onOpen: PropTypes.func,
/** If `true`, the tooltip is shown. */
open: PropTypes.bool,
}
export default styled(Tooltip)``