Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Commit

Permalink
fix: Fix default positioning for Arrow component
Browse files Browse the repository at this point in the history
  • Loading branch information
diondiondion committed Aug 6, 2019
1 parent 114cfe3 commit b664597
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 34 deletions.
20 changes: 13 additions & 7 deletions src/PopOver/Arrow.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import getPrimaryPlacement from './getPrimaryPlacement';
import getArrowPosition, {getPlacements} from './getArrowPosition';

const transformMap = {
top: 'translateY(-50%) rotate(135deg)',
Expand All @@ -9,12 +9,9 @@ const transformMap = {
left: 'translateX(-50%) rotate(45deg)',
};

const getArrowStyles = (placement, arrowSize) => {
const primaryPlacement = getPrimaryPlacement(placement);

const getArrowStyles = (primaryPlacement, arrowSize) => {
return {
position: 'absolute',
[primaryPlacement]: '100%',

display: 'inline-block',
width: arrowSize + 'px',
Expand All @@ -34,10 +31,19 @@ const getArrowStyles = (placement, arrowSize) => {
const Arrow = React.forwardRef((props, ref) => {
const {placement, size, style} = props;

const magicStyle = getArrowStyles(placement, size);
const [primaryPlacement] = getPlacements(placement);
const baseArrowStyles = getArrowStyles(primaryPlacement, size);
const defaultArrowPosition = getArrowPosition(placement, {
centerOffset: `-${size / 2}px`,
});
// Don't let an empty primary position attribute reset the default
if (style && style[primaryPlacement] === '') {
delete style[primaryPlacement];
}
const arrowStyle = {
...baseArrowStyles,
...defaultArrowPosition,
...style,
...magicStyle,
};

return <span ref={ref} style={arrowStyle} />;
Expand Down
38 changes: 30 additions & 8 deletions src/PopOver/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
name: PopOver
menu: Components
---
import {useEffect} from 'react'
import {Playground, Props} from 'docz'

import PopOver from './'
import {useEffect} from 'react';
import {Playground, Props} from 'docz';

import PopOver from './';
import Arrow from './Arrow';

# PopOver

Expand All @@ -19,14 +21,34 @@ It's a wrapper around `react-popper`.
content="I'm a popover. I'll try to always open in a direction that keeps me visible."
placement="top-start"
>
{popover =>
<span ref={popover.ref}>
I'm a reference element
</span>
}
{popover => <span ref={popover.ref}>I'm a reference element</span>}
</PopOver>
</Playground>

## Props

<Props of={PopOver} />

# Using the Arrow Components

The Arrow component is a tooltip "arrow" that can be dropped into any container element and will inherit its styling.

It accepts a placement string formatted in the style of popper.js (i.e. `left`, `top-start`, `right-end`) and will position itself on the opposing side of its parent element. For this to work, the parent element needs to have a position other than 'static', e.g. 'relative', 'absolute', or 'fixed'.

Use the `size` prop to adjust the arrow's size (in pixels).

<Playground>
<div
style={{
position: 'relative',
padding: 8,
backgroundColor: 'grey',
color: 'white',
border: '1px solid black',
borderRadius: 4,
}}
>
Hello, I'm a box with a pointy arrow.
<Arrow placement="bottom-start" size={12} />
</div>
</Playground>
54 changes: 54 additions & 0 deletions src/PopOver/getArrowPosition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* Given a popper.js placement string, this function
* returns a CSS style object containing sensible default
* styles for the placement string.
*
* The values can be customised, but default to:
* - 100% for the primary direction
* - 8 pixels for the secondary direction
*
* When no secondary direction is defined, styles for centering
* are returned which can be customised with an offset:
* `calc(50% + ${centerOffset})`
*
* Examples:
* getArrowPosition('bottom-start') // returns {bottom: '100%', left: 8}
* getArrowPosition('bottom-start', {defaultSecondaryValue: 4}) // returns {bottom: '100%', left: 4}
* getArrowPosition('left-end') // returns {left: '100%', bottom: 8}
* getArrowPosition('right') // returns {right: '100%', top: 50%}
* getArrowPosition('right', {centerOffset: '-4px'}) // returns {right: '100%', top: calc(50% - 4px)}
*/

function getSecondaryDirection(primary, secondary) {
if (primary === 'left' || primary === 'right') {
return secondary === 'end' ? 'bottom' : 'top';
} else {
return secondary === 'end' ? 'right' : 'left';
}
}

export function getPlacements(placement) {
let primaryPlacement = placement;
let secondaryPlacement;
if (placement.indexOf('-') > -1) {
[primaryPlacement, secondaryPlacement] = placement.split('-');
}
return [primaryPlacement, secondaryPlacement];
}

function getArrowPosition(
placement,
{defaultPrimaryValue = '100%', defaultSecondaryValue = 8, centerOffset}
) {
const [primaryPlacement, secondaryPlacement] = getPlacements(placement);

return {
[primaryPlacement]: defaultPrimaryValue,
[getSecondaryDirection(secondaryPlacement)]: secondaryPlacement
? defaultSecondaryValue
: centerOffset
? `calc(50% + ${centerOffset})`
: '50%',
};
}

export default getArrowPosition;
19 changes: 0 additions & 19 deletions src/PopOver/getPrimaryPlacement.js

This file was deleted.

0 comments on commit b664597

Please sign in to comment.