Skip to content

Instantly share code, notes, and snippets.

@kaw2k
Last active October 8, 2015 14:49
Show Gist options
  • Save kaw2k/c7a204d8ddad5182829a to your computer and use it in GitHub Desktop.
Save kaw2k/c7a204d8ddad5182829a to your computer and use it in GitHub Desktop.
A hypothetical tooltip API
import React from 'react';
import mouseTracker from 'decorators/mouseTracker';
// options: {
// followMouse: false,
// positionOrder: ['top', 'bottom', 'left', 'right'],
//
// }
@mouseTracker
class PopoverComponent extends React.Component {
followMouse() {
// TODO: Change to new FINDDomNode
const domPopover = this.refs.popover.getDOMNode();
const domToWrap = this.refs.toWrap.getDOMNode();
const width = window.screen.width;
const height = window.screen.height;
const { clientX, clientY } = this.props.mouse;
const { left, right, top, bottom } = domToWrap.getBoundingClientRect();
const { offsetHeight: popoverHeight, offsetWidth: popoverWidth} = domPopover;
domPopover.style.left = `${clientX - left}px`;
domPopover.style.top = `${clientY - top + 20}px`;
}
componentDidUpdate() {
// If we are not hovering (no mouse prop),
// no need to position the popover
if (!this.props.mouse) { return; }
if (this.props.followMouse) {
this.followMouse();
}
}
render() {
const popover = <div ref="popover"
className="popover-wrapper"
style={{ position: 'absolute' }}>
{this.props.popover}
</div>;
return <div style={{ position: 'relative' }} ref="toWrap">
{this.props.toWrap}
{this.props.mouse ? popover : null}
</div>;
}
}
export default function Popover(options, popover, toWrap) {
return <PopoverComponent {...options} popover={popover} toWrap={toWrap} />
};
/*
* V1. BAD
*
* This API is confusing in multiple was:
* 1. title is a terrible name, it overloads the DOM's title tag
* 2. Nesting a <Tooltip /> component feels clunky and requires
* thought. Do you nest just one? What happens with two...
*/
// We have the ability to make simple one off tooltips by just passing
// a title attribute
function() {
return <TooltipWrapper title="Cool">
Anything within this tag gets a tooltip
</TooltipWrapper>;
}
// If we want more complex functionallity, we can nest components. We
// Omit the title (or keep it) and nest a <TooltipBody /> component
function() {
return <TooltipWrapper>
<Tooltip>
You can put any arbitrary html in this tag
</Tooltip>
Anything within this tag gets a tooltip
</TooltipWrapper>;
}
/*
* V2. Meh?
*
* The thrust here is fix the bad from V1 by ONLY limiting it
* to using props to render. The tooltip prop would always
* take a function whose result gets rendered in a tooltip
*
* GOOD:
* - Consistent API
* - Easy to configure how tooltips work (via props)
*
* BAD:
* - Still confusing in the sense that you are wrapping non
* tooltip text in a tag and rendering the actual tooltip
* via props
*/
function() {
return <TooltipWrapper tooltip={ () => <span> Hello world!</span> }>
Anything within this tag gets a tooltip
</TooltipWrapper>;
}
/*
* V3. Meh?
*
* What we actually are building is a popover component. This
* logic could be shared between popover menus, tooltips, etc.
* To eliminate the bad from V2 we could change Tooltip, popover
* or whatever, to be a function.
*
* Popover.wrap({options}, <ThingToWrap />, <ThingToPopover />)
*
* GOOD:
* - More explicit with who is doing what
*
* BAD:
* - Still need to know order of operations for method signature
* - Hard (or clunky) with how we would configure popovers
*/
function() {
return <div>
{Popver.wrap(
{ stayOpen: true, followMouse: true},
<span>Anything within this tag gets a tooltip</span>,
<span>Hello world!</span>
)}
</div>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment