Permalink
Browse files

feat(Toolbar): Swapper over Toolbar to use Placement

    BREAKING CHANGE: Toolbar now doesn't require a targetRef but does require to be wrapped in Placement components. See example.
  • Loading branch information...
HHogg committed Oct 2, 2018
1 parent 91400c5 commit 417ed2aeaec5772214dd60fb105da8f0a3e07be3
@@ -1,5 +1,14 @@
import React, { Component } from 'react';
import { Icon, Flex, Toolbar, ToolbarAction, ToolbarActionGroup } from 'preshape';
import {
Icon,
Flex,
Placement,
PlacementManager,
PlacementReference,
Toolbar,
ToolbarAction,
ToolbarActionGroup,
} from 'preshape';
import onEdit from '../Component/onEdit';
import DocumentationPage from '../Documentation/DocumentationPage';
import ComponentExample from '../Component/ComponentExample';
@@ -23,33 +32,41 @@ export default class ComponentToggle extends Component {
alignChildren="middle"
direction="horizontal">
<Flex>
<Toolbar { ...this.state.Toolbar } target={ (ref) => (
<Icon name="Star" ref={ ref } size="2rem" />
) }>
<ToolbarActionGroup>
<ToolbarAction>
<Icon name="Pencil" size="1rem" />
</ToolbarAction>
<PlacementManager>
<PlacementReference>
{ ({ ref }) => (
<Icon fRef={ ref } name="Star" size="2rem" />
) }
</PlacementReference>

<ToolbarAction>
<Icon name="Copy" size="1rem" />
</ToolbarAction>
<Placement placement="top">
<Toolbar { ...this.state.Toolbar }>
<ToolbarActionGroup>
<ToolbarAction>
<Icon name="Pencil" size="1rem" />
</ToolbarAction>

<ToolbarAction>
<Icon name="Water" size="1rem" />
</ToolbarAction>
</ToolbarActionGroup>
<ToolbarAction>
<Icon name="Copy" size="1rem" />
</ToolbarAction>

<ToolbarActionGroup>
<ToolbarAction>
<Icon name="Delete" size="1rem" />
</ToolbarAction>
<ToolbarAction>
<Icon name="Water" size="1rem" />
</ToolbarAction>
</ToolbarActionGroup>

<ToolbarAction>
<Icon name="Eye" size="1rem" />
</ToolbarAction>
</ToolbarActionGroup>
</Toolbar>
<ToolbarActionGroup>
<ToolbarAction>
<Icon name="Delete" size="1rem" />
</ToolbarAction>

<ToolbarAction>
<Icon name="Eye" size="1rem" />
</ToolbarAction>
</ToolbarActionGroup>
</Toolbar>
</Placement>
</PlacementManager>
</Flex>
</Flex>
</ComponentExample>
@@ -2,24 +2,8 @@
--Toolbar-tip-size: var(--size--x4);
}

.Toolbar__popper {
position: absolute;
margin: var(--Toolbar-tip-size);
}

.Toolbar__content {
position: relative;
border-radius: var(--border-size--x2);
overflow: hidden;
}

.Toolbar__tip {
position: absolute;
top: 100%;
width: var(--Toolbar-tip-size);
height: var(--Toolbar-tip-size);
margin: var(--Toolbar-tip-size);
transform: translateY(calc(-50% + var(--Toolbar-tip-size) * -1)) rotate(45deg);
background-color: var(--color-text--shade-2);
pointer-events: none;
}
@@ -1,172 +1,38 @@
import PropTypes from 'prop-types';
import React, { Component, Fragment } from 'react';
import ReactDOM from 'react-dom';
import popperJS from 'popper.js';
import omit from 'lodash.omit';
import React, { Component } from 'react';
import Appear from '../Appear/Appear';
import Base from '../Base/Base';
import Flex from '../Flex/Flex';
import ToolbarTargetReference from './ToolbarTargetReference';
import PlacementArrow from '../Placement/PlacementArrow';
import './Toolbar.css';

export default class Toolbar extends Component {
static propTypes = {
/** Toolbar action components. */
children: PropTypes.node.isRequired,
/**
* An HTML element to insert the toolbar into. Defaults
* to the document.body.
*/
container: PropTypes.object,
/**
* Target is a function that is provided with a ref. Used for
* situations where the Toolbar is to be positioned around a
* DOM node.
*/
target: PropTypes.func,
/**
* An object that specifies `width`, `height`, `x` and `y` values.
* Used for situations where the Toolbar is to be pragmatically
* positioned, for example on an HTML canvas.
*/
targetBox: PropTypes.shape({
height: PropTypes.number.isRequired,
width: PropTypes.number.isRequired,
x: PropTypes.number.isRequired,
y: PropTypes.number.isRequired,
}),
/**
* Visibility toggle for the Toolbar.
*/
visible: PropTypes.bool.isRequired,
};

constructor(props) {
super(props);
this.state = {
render: props.visible,
visible: false,
};
}

componentDidMount() {
if (this.state.render) {
this.createPopper();
}
}

componentDidUpdate(prevProps) {
const {
visible,
targetBox,
} = this.props;

if (visible !== prevProps.visible) {
if (visible) {
this.setState({ render: true }, () => {
this.createPopper();
});
} else {
this.setState({ visible: false });
}
} else if (this.popper && visible) {
if (this.targetReference) {
this.targetReference.update(targetBox);
}

this.popper.update();
}
}

componentWillUnmount() {
this.destroyPopper();
}

createPopper() {
this.popper = new popperJS(this.target(), this.elContent, {
onCreate: () => this.setState({ visible: true }),
placement: 'top',
modifiers: {
arrow: {
element: this.elArrow,
},
flip: {
behavior: ['top'],
},
inner: {
enabled: false,
},
offset: {
enabled: false,
},
},
});
}

destroyPopper() {
if (this.propper) {
this.popper.destroy();
}
}

target() {
const {
target,
targetBox,
} = this.props;

if (target) {
return this.elTarget;
}

this.targetReference = new ToolbarTargetReference();
this.targetReference.update(targetBox);
return this.targetReference;
}

handleExited() {
this.destroyPopper();
this.setState({ render: false });
}

render() {
const { children, container = document.body, target, ...rest } = this.props;
const { render, visible } = this.state;
const props = omit(rest, [
'targetBox',
'visible',
]);
const { children, visible, ...rest } = this.props;

return (
<Fragment>
{ target && target((el) => this.elTarget = ReactDOM.findDOMNode(el)) }

{ render && ReactDOM.createPortal(
<div
className="Toolbar__popper"
ref={ (el) => this.elContent = ReactDOM.findDOMNode(el) }>
<Appear { ...props }
animation="Pop"
className="Toolbar"
onExited={ () => this.handleExited() }
visible={ visible }>
<div
className="Toolbar__tip"
ref={ (el) => this.elArrow = ReactDOM.findDOMNode(el) } />
<Base
Component={ Flex }
backgroundColor="text-shade-2"
className="Toolbar__content"
direction="horizontal"
gutter="x1"
padding="x1">
{ children }
</Base>
</Appear>
</div>,
container,
) }
</Fragment>
<Appear { ...rest }
animation="Pop"
className="Toolbar"
visible={ visible }>
<PlacementArrow backgroundColor="text-shade-2" />
<Flex
backgroundColor="text-shade-2"
className="Toolbar__content"
direction="horizontal"
gutter="x1"
padding="x1">
{ children }
</Flex>
</Appear>
);
}
}
@@ -11,7 +11,7 @@ export default class ToolbarActionGroup extends Component {
const { children, ...rest } = this.props;

return (
<Buttons { ...rest }>
<Buttons { ...rest } joined>
{ children }
</Buttons>
);

This file was deleted.

Oops, something went wrong.

0 comments on commit 417ed2a

Please sign in to comment.