Skip to content

Instantly share code, notes, and snippets.

@rngala
Created December 7, 2018 18:28
Show Gist options
  • Save rngala/ca8daf98c3bec4c77ff44863fbc34ba9 to your computer and use it in GitHub Desktop.
Save rngala/ca8daf98c3bec4c77ff44863fbc34ba9 to your computer and use it in GitHub Desktop.
PaperTooltip
const Content = () => (
<div className="content">
<img src="https://vignette.wikia.nocookie.net/epicrapbattlesofhistory/images/c/c2/Peanut-butter-jelly-time.gif/revision/latest?cb=20141129150614" />
</div>
)
const TooltipButton = ({
position,
relation
}) => (
<PaperTooltip position={position} relation={relation} content={<Content />}>
<div className="butao">Position: {position} <br /> Relation: {relation}</div>
</PaperTooltip>
)
const Tooltipped = () => (
<div className="wrapper">
<TooltipButton position={1} relation="top"/>
<TooltipButton position={2} relation="top"/>
<TooltipButton position={3} relation="top"/>
<TooltipButton position={1} relation="left"/>
<TooltipButton position={2} relation="left"/>
<TooltipButton position={3} relation="left"/>
<TooltipButton position={1} relation="right"/>
<TooltipButton position={2} relation="right"/>
<TooltipButton position={3} relation="right"/>
<TooltipButton position={1} relation="bottom"/>
<TooltipButton position={2} relation="bottom"/>
<TooltipButton position={3} relation="bottom"/>
</div>
)
class PaperTooltip extends React.Component {
constructor(props) {
super(props);
this.state = {
show: false,
};
this.clickOut = this.clickOut.bind(this);
}
componentDidMount() {
window.addEventListener('click', this.clickOut);
}
componentWillUnmount() {
window.removeEventListener('click', this.clickOut);
}
componentDidUpdate(prevProps, prevState) {
if (prevState.show === this.props.show) return;
if (prevProps.onOpen && this.state.show) {
return this.props.onOpen();
}
}
clickOut({ target }) {
if (this.me && !this.me.contains(target)) {
this.setState({
show: false,
});
}
}
show() {
this.setState({
show: !this.state.show,
});
}
hide() {
this.setState({
show: false,
});
}
tooltipBody() {
return (
<div
data-relation={this.props.relation}
data-position={this.props.position}
className={'paper-tooltip-body' + (this.state.show ? ' triggered' : '')}
>
{this.props.content}
</div>
);
}
clonedChildren() {
return React.Children.map(this.props.children, child =>
React.cloneElement(child, {
onClick: () => {
this.show();
},
})
);
}
render() {
const className = 'paper-tooltip ' + (this.props.className ? this.props.className : '') + (this.state.show ? ' triggered-cont' : '');
return (
<div className={className} title={this.props.title} ref={me => (this.me = me)}>
{this.clonedChildren()}
{this.tooltipBody()}
</div>
);
}
}
ReactDOM.render(<Tooltipped />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
@use postcss-nested-ancestors;
@use postcss-nested;
@use postcss-preset-env;
body{
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
font-family: sans-serif;
background-image: radial-gradient(white, #efefef);
}
.wrapper{
display: flex;
max-width: 550px;
flex-wrap: wrap;
}
.paper-tooltip{
margin: 10px;
}
.butao {
cursor: pointer;
padding: 1em;
background-color: #efefef;
padding: 10px;
width: 130px;
font-size: 0.8em;
text-align: center;
line-height: 1.3;
border-radius: 14px;
user-select: none;
}
.content{
padding: 10px;
}
.content img{
width: 8em;
}
/* Paper tooltip */
.paper-tooltip{
position: relative;
& .paper-tooltip-body{
display: block;
position: absolute;
background-color: #fff;
box-shadow: 0 8px 42px rgba(41, 71, 124, 0.34);
border-radius: 15px;
transition: all 0.1s ease-out;
opacity: 0;
pointer-events: none;
z-index: 10;
&.triggered{
opacity: 1;
pointer-events: all;
}
&:after{
content: '';
background-color: transparent;
display: block;
position: absolute;
pointer-events: none;
border-color: transparent;
border-style: solid;
width: 0;
height: 0;
border-width: 8px;
}
&[data-relation="top"]{
bottom: calc(100% - 20px);
&.triggered{
bottom: calc(100% + 10px);
}
&:after{
top: 100%;
border-top-color: #fff;
}
}
&[data-relation="bottom"]{
top: calc(100% - 20px);
&.triggered{
top: calc(100% + 10px);
}
&:after{
bottom: 100%;
border-bottom-color: #fff;
}
}
&[data-relation="bottom"], &[data-relation="top"]{
left: 50%;
&[data-position="1"]{
transform: translateX(calc(-100% + 30px)) scale(0.8);
&.triggered{
transform: translateX(calc(-100% + 20px)) scale(1);
}
&:after{
right: 15px;
}
}
&[data-position="2"]{
transform: translateX(-50%) scale(0.8);
&.triggered{
transform: translateX(-50%) scale(1);
}
&:after{
left: 50%;
transform: translateX(-50%);
}
}
&[data-position="3"]{
transform: translateX(calc(0% - 40px)) scale(0.8);
&.triggered{
transform: translateX(calc(0% - 20px)) scale(1);
}
&:after{
left: 15px;
}
}
}
&[data-relation="left"]{
right: calc(100% - 20px);
&.triggered{
right: calc(100% + 20px);
}
&:after{
left: 100%;
border-left-color: #fff;
}
}
&[data-relation="right"]{
left: calc(100% - 20px);
&.triggered{
left: calc(100% + 20px);
}
&:after{
right: 100%;
border-right-color: #fff;
}
}
&[data-relation="right"], &[data-relation="left"]{
top: 50%;
&[data-position="1"]{
transform: translateY(calc(-100% + 40px)) scale(0.8);
&.triggered{
transform: translateY(calc(-100% + 20px));
}
&:after{
bottom: 15px;
}
}
&[data-position="2"]{
transform: translateY(-50%) scale(0.8);
&.triggered{
transform: translateY(-50%);
}
&:after{
top: 50%;
transform: translateY(-50%);
}
}
&[data-position="3"]{
transform: translateY(calc(0% - 40px)) scale(0.8);
&.triggered{
transform: translateY(calc(0% - 20px));
}
&:after{
top: 15px;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment