Skip to content

Commit

Permalink
Render on show (#33)
Browse files Browse the repository at this point in the history
* Defer rendering until the first time the tip is shown
* Add tipContentsClassName prop
* Add forceDirection prop
  • Loading branch information
bsidelinger912 authored Feb 2, 2019
1 parent 0cb8045 commit fc7ca82
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 140 deletions.
4 changes: 1 addition & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,10 @@
"no-unused-vars": [
1
],
"no-unused-expressions": [ 0 ],
"react/prefer-stateless-function": [
0
],
"react/prop-types": [
0
],
"react/jsx-indent": [
2,
2
Expand Down
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,29 @@ You can pass in props to define tip direction, styling, etc. Content is the onl
<td>string</td>
<td>the tip direction, defaults to up. Possible values are "up", "down", "left", "right" with optional modifer for alignment of "start" and "end". e.g. "left-start" will attempt tooltip on left and align it with the start of the target. If alignment modifier is not specified the default behavior is to align "middle".</td>
</tr>
<tr>
<td>forceDirection</td>
<td>boolean</td>
<td>Tells the tip to allow itself to render out of view if there's not room for the specified direction. If undefined or false, the tip will change direction as needed to render within the confines of the window.</td>
</tr>
<tr>
<td>className</td>
<td>string</td>
<td>css class added to the rendered wrapper</td>
<td>
css class added to the rendered wrapper (and the tooltip if tooltipClassName is undefined)
NOTE: in future versions className will only be applied to the wrapper element and not the tooltip
</td>
</tr>
<tr>
<td>tipContentClassName</td>
<td>string</td>
<td>css class added to the tooltip</td>
</tr>
<tr>
<td>tipContentHover</td>
<td>boolean</td>
<td>defines whether you should be able to hover over the tip contents for links and copying content,
defaults to false.</a>
</tr>
<tr>
<td>background</td>
Expand Down Expand Up @@ -130,12 +149,6 @@ You can pass in props to define tip direction, styling, etc. Content is the onl
<td>boolean</td>
<td>forces open/close state from a prop, overrides hover or click state</td>
</tr>
<tr>
<td>tipContentHover</td>
<td>boolean</td>
<td>defines whether you should be able to hover over the tip contents for links and copying content,
defaults to false.</a>
</tr>
<tr>
<td>hoverDelay</td>
<td>number</td>
Expand Down
4 changes: 0 additions & 4 deletions example/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ section {
margin-bottom: 50px;
}

section:last-child {
margin-bottom: 400px;
}

a {
display: inline-block;
}
Expand Down
63 changes: 34 additions & 29 deletions example/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class App extends React.Component {
}

bodyClick(e) {
if (this.tipContentRef.contains(e.target) || this.buttonRef.contains(e.target)) {
if ((this.tipContentRef && this.tipContentRef.contains(e.target)) || this.buttonRef.contains(e.target)) {
return;
}

Expand All @@ -46,15 +46,15 @@ class App extends React.Component {
<h3>Basic:</h3>

<div className="flex-spread">
<Tooltip content="By default the text is above the element" className="target">
<Tooltip content="By default the text is above the element" className="target" tipContentClassName="foo">
Target
</Tooltip>

<Tooltip content="It'll center if it has room" className="target">
<Tooltip content="It'll center if it has room" className="target" tipContentClassName="">
Target
</Tooltip>

<Tooltip content="you can specify 'direction' (up, down, left, right) too" direction="down" className="target">
<Tooltip content="you can specify 'direction' (up, down, left, right) too" direction="down" className="target" tipContentClassName="">
t
</Tooltip>
</div>
Expand All @@ -73,7 +73,8 @@ class App extends React.Component {
the <Tooltip content="Go to google" direction="right" tagName="span">
<a href="http://google.com" target="_blank" rel="noopener noreferrer"> edge</a>
</Tooltip>.
</p>
You can also force the direction of the tip and it will allow itself <Tooltip className="target" tipContentClassName="" content="this direction is forced" direction="right" tagName="span" forceDirection>to go off screen</Tooltip>.
</p>
</section>

<section>
Expand All @@ -96,6 +97,7 @@ class App extends React.Component {
direction="down"
tagName="span"
className="target"
tipContentClassName=""
>
Html content
</Tooltip>.
Expand All @@ -112,6 +114,7 @@ class App extends React.Component {
tagName="span"
direction="right"
className="target"
tipContentClassName=""
tipContentHover
>
example
Expand All @@ -122,7 +125,7 @@ class App extends React.Component {
<section>
<h3>Colors</h3>

You can pass <Tooltip tagName="span" className="target" color="blue" background="red" content="The color for this is defined by props">
You can pass <Tooltip tagName="span" className="target" tipContentClassName="" color="blue" background="red" content="The color for this is defined by props">
color options as props
</Tooltip> or use a&nbsp;
<Tooltip
Expand Down Expand Up @@ -167,7 +170,7 @@ class App extends React.Component {
<section>
<h3>Custom events</h3>
<p>
<Tooltip content="this uses hover but also closes on click" className="target" tagName="span" eventOff="onClick">
<Tooltip content="this uses hover but also closes on click" className="target" tipContentClassName="" tagName="span" eventOff="onClick">
Close on click
</Tooltip>
</p>
Expand All @@ -176,6 +179,7 @@ class App extends React.Component {
<Tooltip
content="opens on a click and closes on mouse out"
className="target"
tipContentClassName=""
tagName="span"
eventOn="onClick"
eventOff="onMouseOut"
Expand All @@ -186,7 +190,7 @@ class App extends React.Component {
</p>

<p>
<Tooltip content="this uses hover but also closes on click" className="target" tagName="span" eventToggle="onClick">
<Tooltip content="this uses hover but also closes on click" className="target" tipContentClassName="" tagName="span" eventToggle="onClick">
Toggle on click
</Tooltip>
</p>
Expand All @@ -197,7 +201,7 @@ class App extends React.Component {
pass the {'"defaultStyles"'} prop as true to get up and running quick and easy
</p>
<p>
<Tooltip content="styled with defaults" className="target" useDefaultStyles tagName="span">
<Tooltip content="styled with defaults" className="target" tipContentClassName="" useDefaultStyles tagName="span">
See default styles
</Tooltip>
</p>
Expand All @@ -220,6 +224,7 @@ class App extends React.Component {
isOpen={tipOpen}
tagName="span"
direction="down"
forceDirection
>
click the button
</Tooltip>
Expand All @@ -229,81 +234,81 @@ class App extends React.Component {
<h3>Distance and arrow size</h3>

<div className="flex-spread">
<Tooltip content="This has an arrowSize of 20, where the default is 10" className="target" arrowSize={20}>Larger arrowSize</Tooltip>
<Tooltip content="This has an arrowSize of 5, where the default is 10" className="target" arrowSize={5}>Smaller arrowSize</Tooltip>
<Tooltip content="This has a distance prop of 20, where the default is arrowSize" className="target" distance={20}>Increase distance</Tooltip>
<Tooltip content="This has a distance prop of 0, where the default is the arrowSize" className="target" distance={0}>Decrease distance</Tooltip>
<Tooltip content="This has an arrowSize of 20, where the default is 10" className="target" tipContentClassName="" arrowSize={20}>Larger arrowSize</Tooltip>
<Tooltip content="This has an arrowSize of 5, where the default is 10" className="target" tipContentClassName="" arrowSize={5}>Smaller arrowSize</Tooltip>
<Tooltip content="This has a distance prop of 20, where the default is arrowSize" className="target" tipContentClassName="" distance={20}>Increase distance</Tooltip>
<Tooltip content="This has a distance prop of 0, where the default is the arrowSize" className="target" tipContentClassName="" distance={0}>Decrease distance</Tooltip>
</div>
</section>

<section>
<h3>Compound Alignment</h3>

<div className="flex-spread">
<Tooltip content="you can have compound alignments" direction="right-start" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="right-start" className="target" tipContentClassName="" arrow={false}>
right-start
</Tooltip>

<Tooltip content="you can have compound alignments" direction="right-end" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="right-end" className="target" tipContentClassName="" arrow={false}>
right-end
</Tooltip>

<Tooltip content="you can have compound alignments" direction="left-start" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="left-start" className="target" tipContentClassName="" arrow={false}>
left-start
</Tooltip>

<Tooltip content="you can have compound alignments" direction="left-end" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="left-end" className="target" tipContentClassName="" arrow={false}>
left-end
</Tooltip>

<Tooltip content="you can have compound alignments" direction="up-start" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="up-start" className="target" tipContentClassName="" arrow={false}>
top-start
</Tooltip>

<Tooltip content="you can have compound alignments" direction="up-end" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="up-end" className="target" tipContentClassName="" arrow={false}>
top-end
</Tooltip>

<Tooltip content="you can have compound alignments" direction="down-start" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="down-start" className="target" tipContentClassName="" arrow={false}>
down-start
</Tooltip>

<Tooltip content="you can have compound alignments" direction="down-end" className="target" arrow={false}>
<Tooltip content="you can have compound alignments" direction="down-end" className="target" tipContentClassName="" arrow={false}>
down-end
</Tooltip>
</div>
<br />
<br />
<div className="flex-spread">
<Tooltip content="you can have compound alignments" direction="right-start" className="target">
<Tooltip content="you can have compound alignments" direction="right-start" className="target" tipContentClassName="">
right-start with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="right-end" className="target">
<Tooltip content="you can have compound alignments" direction="right-end" className="target" tipContentClassName="">
right-end with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="left-start" className="target">
<Tooltip content="you can have compound alignments" direction="left-start" className="target" tipContentClassName="">
left-start with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="left-end" className="target">
<Tooltip content="you can have compound alignments" direction="left-end" className="target" tipContentClassName="">
left-end with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="down-start" className="target">
<Tooltip content="you can have compound alignments" direction="down-start" className="target" tipContentClassName="">
down-start with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="down-end" className="target">
<Tooltip content="you can have compound alignments" direction="down-end" className="target" tipContentClassName="">
down-end with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="up-start" className="target">
<Tooltip content="you can have compound alignments" direction="up-start" className="target" tipContentClassName="">
up-start with arrow
</Tooltip>

<Tooltip content="you can have compound alignments" direction="up-end" className="target">
<Tooltip content="you can have compound alignments" direction="up-end" className="target" tipContentClassName="">
up-end with arrow
</Tooltip>
</div>
Expand Down
37 changes: 29 additions & 8 deletions src/getDirection.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Checks the intended tip direction and falls back if not enough space
*/
import { getScrollLeft, getArrowSpacing } from './position';
import { getScrollLeft, getArrowSpacing, minArrowPadding } from './position';

function checkLeftRightWidthSufficient(tip, target, distance, bodyPadding) {
const targetRect = target.getBoundingClientRect();
Expand All @@ -10,10 +10,31 @@ function checkLeftRightWidthSufficient(tip, target, distance, bodyPadding) {
return (tip.offsetWidth + target.offsetWidth + distance + bodyPadding + deadSpace < document.documentElement.clientWidth);
}

function checkTargetFullyVisible(target) {
const bottomOverhang = target.getBoundingClientRect().bottom > window.innerHeight;
const topOverhang = target.getBoundingClientRect().top < 0;
function checkTargetSufficientlyVisible(target, tip, props) {
const targetRect = target.getBoundingClientRect();
const bottomOverhang = targetRect.bottom > window.innerHeight;
const topOverhang = targetRect.top < 0;

// if the target is taller than the viewport (and we know there's sufficient left/right width before this is called),
// then go with the left/right direction as top/bottom will both be off screen
if (topOverhang && bottomOverhang) {
return true;
}

// if the target is bigger than the tip, we need to check if enough of the target is visible
if (target.offsetHeight > tip.offsetHeight) {
const halfTargetHeight = target.offsetHeight / 2;
const arrowClearance = props.arrowSize + minArrowPadding;
const bottomOverhangAmount = targetRect.bottom - window.innerHeight;
const topOverhangAmount = -targetRect.top;

const targetCenterToBottomOfWindow = halfTargetHeight - bottomOverhangAmount;
const targetCenterToTopOfWindow = halfTargetHeight - topOverhangAmount;

return (targetCenterToBottomOfWindow >= arrowClearance && targetCenterToTopOfWindow >= arrowClearance);
}

// otherwise just check that the whole target is visible
return (!bottomOverhang && !topOverhang);
}

Expand Down Expand Up @@ -46,7 +67,7 @@ export default function getDirection(currentDirection, tip, target, props, bodyP
switch (currentDirection) {
case 'right':
// if the window is not wide enough try top (which falls back to down)
if (!checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding) || !checkTargetFullyVisible(target)) {
if (!checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding) || !checkTargetSufficientlyVisible(target, tip, props)) {
return getDirection('up', tip, target, arrowSpacing, bodyPadding, arrowStyles, true);
}

Expand All @@ -58,7 +79,7 @@ export default function getDirection(currentDirection, tip, target, props, bodyP

case 'left':
// if the window is not wide enough try top (which falls back to down)
if (!checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding) || !checkTargetFullyVisible(target)) {
if (!checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding) || !checkTargetSufficientlyVisible(target, tip, props)) {
return getDirection('up', tip, target, arrowSpacing, bodyPadding, arrowStyles, true);
}

Expand All @@ -76,7 +97,7 @@ export default function getDirection(currentDirection, tip, target, props, bodyP
if (!hasSpaceAbove) {
if (hasSpaceBelow) {
return 'down';
} else if (checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding)) {
} else if (!recursive && checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding)) {
return getDirection('right', tip, target, arrowSpacing, bodyPadding, arrowStyles, true);
}
}
Expand All @@ -95,7 +116,7 @@ export default function getDirection(currentDirection, tip, target, props, bodyP
return 'up';

// if there's not space above or below, check if there would be space left or right
} else if (checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding)) {
} else if (!recursive && checkLeftRightWidthSufficient(tip, target, arrowSpacing, bodyPadding)) {
return getDirection('right', tip, target, arrowSpacing, bodyPadding, arrowStyles, true);
}

Expand Down
24 changes: 13 additions & 11 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,27 @@ declare module 'react-tooltip-lite' {
import * as React from 'react';

export interface TooltipProps {
tagName?: string;
direction?: string;
className?: string;
content: React.ReactNode;
arrow?: boolean;
arrowSize?: number;
background?: string;
className?: string;
color?: string;
padding?: string;
content: React.ReactNode;
direction?: string;
distance?: number;
styles?: object;
eventOff?: string;
eventOn?: string;
eventToggle?: string;
useHover?: boolean;
useDefaultStyles?: boolean;
isOpen?: boolean;
forceDirection?: boolean;
hoverDelay?: number;
isOpen?: boolean;
padding?: string;
styles?: object;
tagName?: string;
tipContentHover?: boolean;
arrow?: boolean;
arrowSize?: number;
tipContentClassName?: string;
useHover?: boolean;
useDefaultStyles?: boolean;
}

export default class Tooltip extends React.Component<TooltipProps> {
Expand Down
Loading

0 comments on commit fc7ca82

Please sign in to comment.