Skip to content

Commit

Permalink
Allow adding your own rendering of arrows (#82)
Browse files Browse the repository at this point in the history
* Allow providing custom arrow content
  • Loading branch information
osdiab authored May 13, 2020
1 parent 07f1fba commit 158ba77
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 28 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/getDirection.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ function checkTargetSufficientlyVisible(target, tip, props) {

function checkForArrowOverhang(props, arrowStyles, bodyPadding) {
const scrollLeft = getScrollLeft();
const hasLeftClearance = arrowStyles.left - scrollLeft > bodyPadding;
const hasRightClearance = arrowStyles.left + (props.arrowSize * 2) < (scrollLeft + document.documentElement.clientWidth) - bodyPadding;
const hasLeftClearance = arrowStyles.positionStyles.left - scrollLeft > bodyPadding;
const hasRightClearance = arrowStyles.positionStyles.left + (props.arrowSize * 2) < (scrollLeft + document.documentElement.clientWidth) - bodyPadding;

return (!hasLeftClearance || !hasRightClearance);
}
Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare module 'react-tooltip-lite' {
useDefaultStyles?: boolean;
zIndex?: number;
onToggle?: (showTip: boolean) => void;
arrowContent?: React.ReactNode;
}

export default class Tooltip extends React.Component<TooltipProps> {
Expand Down
8 changes: 6 additions & 2 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class Tooltip extends React.Component {
useHover: PropTypes.bool,
zIndex: PropTypes.number,
onToggle: PropTypes.func,
arrowContent: PropTypes.node,
}

static defaultProps = {
Expand All @@ -74,6 +75,7 @@ class Tooltip extends React.Component {
useHover: true,
zIndex: 1000,
onToggle: undefined,
arrowContent: null,
}

static getDerivedStateFromProps(nextProps) {
Expand Down Expand Up @@ -260,6 +262,7 @@ class Tooltip extends React.Component {
tipContentClassName,
useDefaultStyles,
useHover,
arrowContent,
} = this.props;

const isControlledByProps = typeof isOpen !== 'undefined' && isOpen !== null;
Expand Down Expand Up @@ -330,7 +333,8 @@ class Tooltip extends React.Component {
};

const arrowStyles = {
...currentPositions.arrow,
...currentPositions.arrow.positionStyles,
...(arrowContent ? {} : currentPositions.arrow.borderStyles),
position: 'absolute',
width: '0px',
height: '0px',
Expand All @@ -343,7 +347,7 @@ class Tooltip extends React.Component {
<span className="react-tooltip-lite" style={tipStyles} ref={(tip) => { this.tip = tip; }}>
{content}
</span>
<span className={`react-tooltip-lite-arrow react-tooltip-lite-${currentPositions.realDirection}-arrow`} style={arrowStyles} />
<span className={`react-tooltip-lite-arrow react-tooltip-lite-${currentPositions.realDirection}-arrow`} style={arrowStyles}>{arrowContent}</span>
</div>
</Portal>
);
Expand Down
45 changes: 21 additions & 24 deletions src/position.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ function getLeftRightPosition(tip, target, state, direction, alignMode, props) {
function getArrowStyles(target, tip, direction, state, props) {
if (!target || !props.arrow) {
return {
top: '0',
left: '-10000000px',
positionStyles: {
top: '0',
left: '-10000000px',
},
};
}

Expand All @@ -159,6 +161,7 @@ function getArrowStyles(target, tip, direction, state, props) {
const scrollLeft = getScrollLeft();
const arrowSpacing = getArrowSpacing(props);
const borderStyles = {};
const positionStyles = {};

switch (direction) {
case 'right':
Expand All @@ -172,12 +175,9 @@ function getArrowStyles(target, tip, direction, state, props) {
borderStyles.borderRightStyle = 'solid';
}

return {
...borderStyles,
top: (state.showTip && tip) ? (targetRect.top + scrollTop + halfTargetHeight) - props.arrowSize : '-10000000px',
left: (targetRect.right + scrollLeft + arrowSpacing) - props.arrowSize,
};

positionStyles.top = (state.showTip && tip) ? (targetRect.top + scrollTop + halfTargetHeight) - props.arrowSize : '-10000000px';
positionStyles.left = (targetRect.right + scrollLeft + arrowSpacing) - props.arrowSize;
break;
case 'left':
borderStyles.borderTop = `${props.arrowSize}px solid transparent`;
borderStyles.borderBottom = `${props.arrowSize}px solid transparent`;
Expand All @@ -189,12 +189,9 @@ function getArrowStyles(target, tip, direction, state, props) {
borderStyles.borderLeftStyle = 'solid';
}

return {
...borderStyles,
top: (state.showTip && tip) ? (targetRect.top + scrollTop + halfTargetHeight) - props.arrowSize : '-10000000px',
left: (targetRect.left + scrollLeft) - arrowSpacing - 1,
};

positionStyles.top = (state.showTip && tip) ? (targetRect.top + scrollTop + halfTargetHeight) - props.arrowSize : '-10000000px';
positionStyles.left = (targetRect.left + scrollLeft) - arrowSpacing - 1;
break;
case 'up':
borderStyles.borderLeft = `${props.arrowSize}px solid transparent`;
borderStyles.borderRight = `${props.arrowSize}px solid transparent`;
Expand All @@ -207,12 +204,10 @@ function getArrowStyles(target, tip, direction, state, props) {
borderStyles.borderTopStyle = 'solid';
}

return {
...borderStyles,
left: (state.showTip && tip) ? (targetRect.left + scrollLeft + halfTargetWidth) - props.arrowSize : '-10000000px',
top: (targetRect.top + scrollTop) - arrowSpacing,
};

positionStyles.left = (state.showTip && tip) ? (targetRect.left + scrollLeft + halfTargetWidth) - props.arrowSize : '-10000000px';
positionStyles.top = (targetRect.top + scrollTop) - arrowSpacing;
break;
case 'down':
default:
borderStyles.borderLeft = `${props.arrowSize}px solid transparent`;
Expand All @@ -225,12 +220,14 @@ function getArrowStyles(target, tip, direction, state, props) {
borderStyles.borderBottomStyle = 'solid';
}

return {
...borderStyles,
left: (state.showTip && tip) ? (targetRect.left + scrollLeft + halfTargetWidth) - props.arrowSize : '-10000000px',
top: (targetRect.bottom + scrollTop + arrowSpacing) - props.arrowSize,
};
positionStyles.left = (state.showTip && tip) ? (targetRect.left + scrollLeft + halfTargetWidth) - props.arrowSize : '-10000000px';
positionStyles.top = (targetRect.bottom + scrollTop + arrowSpacing) - props.arrowSize;
break;
}
return {
borderStyles,
positionStyles,
};
}

/**
Expand Down
20 changes: 20 additions & 0 deletions stories/arrowContent.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.border-tooltip .react-tooltip-lite {
box-sizing: border-box;
border: 1px solid gray;
border-radius: 8px;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.2);
}

.border-tooltip .react-tooltip-lite-down-arrow svg {
transform: translateY(1px);
}

.border-tooltip .react-tooltip-lite-right-arrow svg {
transform: rotate(270deg) translateY(-4px) translateX(-4px);
}
.border-tooltip .react-tooltip-lite-up-arrow svg {
transform: rotate(180deg) translateY(1px);
}
.border-tooltip .react-tooltip-lite-left-arrow svg {
transform: rotate(90deg) translateY(5px) translateX(4px);
}
93 changes: 93 additions & 0 deletions stories/arrowContent.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { select } from '@storybook/addon-knobs';

import Tooltip from '../src/index';
import { isOpenOptions } from './shared';
import Wrapper from './Wrapper';

import './arrowContent.css';

storiesOf('Tooltip', module)
.add('Custom arrow content', () => {
const isOpen = select('isOpen', isOpenOptions, true);
const svgArrow = (
<svg style={{ display: 'block' }} viewBox="0 0 21 11" width="20px" height="10px">
<path
d="M0,11 L9.43630703,1.0733987 L9.43630703,1.0733987 C10.1266203,0.3284971 11.2459708,0 11.936284,1.0733987 L20,11"
style={{ stroke: 'gray', fill: 'white' }}
/>
</svg>
);


return (
<React.StrictMode>
<Wrapper flex>
<Tooltip
content="By default the text is above the element"
isOpen={isOpen}
className="target"
background="white"
color="black"
tipContentClassName="border-tooltip"
spacing={0}
arrowContent={svgArrow}
>
Hover
</Tooltip>
<Tooltip
content="It'll center if it has room"
isOpen={isOpen}
className="target"
background="white"
color="black"
tipContentClassName="border-tooltip"
spacing={0}
arrowContent={svgArrow}
>
Centered
</Tooltip>
<Tooltip
content="down"
direction="down"
isOpen={isOpen}
className="target"
background="white"
color="black"
tipContentClassName="border-tooltip"
spacing={0}
arrowContent={svgArrow}
>
Target is here
</Tooltip>
<Tooltip
content="right"
direction="right"
isOpen={isOpen}
className="target"
background="white"
color="black"
tipContentClassName="border-tooltip"
spacing={0}
arrowContent={svgArrow}
>
Target is here
</Tooltip>
<Tooltip
content="left"
direction="left"
isOpen={isOpen}
className="target"
background="white"
color="black"
tipContentClassName="border-tooltip"
spacing={0}
arrowContent={svgArrow}
>
Target is here
</Tooltip>
</Wrapper>
</React.StrictMode>
);
});

0 comments on commit 158ba77

Please sign in to comment.