Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect Inserter bounds collision by generic Popover component #1193

Merged
merged 3 commits into from
Jun 15, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Components
export { default as Button } from './button';
export { default as ClipboardButton } from './clipboard-button';
export { default as Dashicon } from './dashicon';
Expand All @@ -12,6 +13,8 @@ export { default as Placeholder } from './placeholder';
export { default as ResponsiveWrapper } from './responsive-wrapper';
export { default as Spinner } from './spinner';
export { default as Toolbar } from './toolbar';
export { default as Popover } from './popover';

// Higher-Order Components
export { default as withFocusReturn } from './higher-order/with-focus-return';
export { default as withInstanceId } from './higher-order/with-instance-id';
110 changes: 110 additions & 0 deletions components/popover/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { isEqual, includes } from 'lodash';

/**
* WordPress dependencies
*/
import { Component } from 'element';

/**
* Internal dependencies
*/
import './style.scss';

class Popover extends Component {
constructor() {
super( ...arguments );

this.bindNode = this.bindNode.bind( this );

this.state = {
forcedPositions: {},
};
}

componentDidMount() {
this.setForcedPositions();
}

componentWillReceiveProps( nextProps ) {
if ( this.props.position !== nextProps.position ) {
this.setState( { forcedPositions: {} } );
}
}

componentDidUpdate( prevProps ) {
if ( this.props.position !== prevProps.position ) {
this.setForcedPositions();
}
}

setForcedPositions() {
const rect = this.node.getBoundingClientRect();
const { forcedPositions } = this.state;

const nextForcedPositions = {};

// Check exceeding top or bottom of viewport
if ( rect.top < 0 ) {
nextForcedPositions.bottom = true;
} else if ( rect.bottom > window.innerHeight ) {
nextForcedPositions.top = true;
}

// Check exceeding left or right of viewport
if ( rect.left < 0 ) {
nextForcedPositions.right = true;
} else if ( rect.right > window.innerWidth ) {
nextForcedPositions.left = true;
}

if ( ! isEqual( nextForcedPositions, forcedPositions ) ) {
this.setState( {
forcedPositions: nextForcedPositions,
} );
}
}

bindNode( node ) {
this.node = node;
}

render() {
const { position, children, className } = this.props;
const positions = position.split( ' ' );
const { forcedPositions } = this.state;

const classes = classnames(
'components-popover',
className,
...[ [ 'top', 'bottom' ], [ 'center', 'left', 'right' ] ].map( ( directions ) => {
// Consider first of directions set as the default
const defaultDirection = directions[ 0 ];

// Prefer the forced direction, but allow direction from props
// otherwise. Use default if neither forced nor prop value.
const direction = directions.reduce( ( result, dir ) => (
forcedPositions[ dir ] || ( ! result && includes( positions, dir ) )
? dir
: result
), null ) || defaultDirection;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we store forced positions like this { forceYAxis: 'top', forceXAxis: 'left' } we could simplify the logic here and avoid some loops.


return 'is-' + direction;
} )
);

return (
<div
ref={ this.bindNode }
className={ classes }
tabIndex="0">
{ children }
</div>
);
}
}

export default Popover;
102 changes: 102 additions & 0 deletions components/popover/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
.components-popover {
box-shadow: $shadow-popover;
border: 1px solid $light-gray-500;
position: absolute;
background: $white;
z-index: z-index( ".components-popover" );
left: 0;
right: 0;

@include break-medium {
width: 280px;
left: -122px;
right: auto;
height: auto;
}

&:before {
border: 10px dashed $light-gray-500;
display: none;

@include break-medium {
display: block;
}
}

&:after {
border: 10px solid $white;
}

&:before,
&:after {
content: "";
position: absolute;
left: 50%;
margin-left: -10px;
height: 0;
width: 0;
border-left-color: transparent;
border-right-color: transparent;
line-height: 0;
}

&.is-top {
bottom: 42px;

&:before {
bottom: -10px;
}

&:after {
bottom: -8px;
}

&:before,
&:after {
border-top-style: solid;
border-bottom: none;
}
}

&.is-bottom {
top: $admin-bar-height-big + $item-spacing -1px;

@include break-medium {
top: $admin-bar-height + $item-spacing;
}

&:before {
top: -10px;
}

&:after {
top: -8px;
}

&:before,
&:after {
border-bottom-style: solid;
border-top: none;
}
}

&.is-right {
left: -32px;

&:before,
&:after {
left: 49px;
}
}

&.is-left {
left: auto;
right: -42px;

&:before,
&:after {
left: auto;
right: 49px;
}
}
}
3 changes: 1 addition & 2 deletions editor/assets/stylesheets/_z-index.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
$z-layers: (
'.editor-mode-switcher .dashicon': -1,
'.editor-block-switcher__arrow': 1,
'.editor-inserter__arrow': 1,
'.editor-inserter__menu': 1,
'.components-popover': 1,
'.editor-visual-editor__block:before': -1,
'.editor-visual-editor__block {core/image aligned left or right}': 10,
'.editor-visual-editor__block-controls': 1,
Expand Down
12 changes: 4 additions & 8 deletions editor/inserter/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
* External dependencies
*/
import { flow, groupBy, sortBy, findIndex, filter } from 'lodash';
import classnames from 'classnames';
import { connect } from 'react-redux';

/**
* WordPress dependencies
*/
import { Dashicon, withFocusReturn, withInstanceId } from 'components';
import { Dashicon, Popover, withFocusReturn, withInstanceId } from 'components';
import { TAB, ESCAPE, LEFT, UP, RIGHT, DOWN } from 'utils/keycodes';

/**
Expand Down Expand Up @@ -223,14 +222,11 @@ class InserterMenu extends wp.element.Component {
}

render() {
const { position = 'top', instanceId } = this.props;
const { position, instanceId } = this.props;
const visibleBlocksByCategory = this.getVisibleBlocksByCategory( wp.blocks.getBlockTypes() );
const positionClasses = position.split( ' ' ).map( ( pos ) => `is-${ pos }` );
const className = classnames( 'editor-inserter__menu', positionClasses );

return (
<div className={ className } tabIndex="0">
<div className="editor-inserter__arrow" />
<Popover position={ position } className="editor-inserter__menu">
<div role="menu" className="editor-inserter__content">
{ wp.blocks.getCategories()
.map( ( category ) => !! visibleBlocksByCategory[ category.slug ] && (
Expand Down Expand Up @@ -281,7 +277,7 @@ class InserterMenu extends wp.element.Component {
ref={ this.bindReferenceNode( 'search' ) }
tabIndex="-1"
/>
</div>
</Popover>
);
}
}
Expand Down
106 changes: 4 additions & 102 deletions editor/inserter/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,108 +32,6 @@
}
}

.editor-inserter__menu {
box-shadow: $shadow-popover;
border: 1px solid $light-gray-500;
position: absolute;
background: $white;
z-index: z-index( '.editor-inserter__menu' );

left: 0;
right: 0;

// inserter becomes popup
@include break-medium {
width: 280px;
left: -122px;
right: auto;
height: auto;
}

&.is-top {
bottom: 42px;

.editor-inserter__arrow {
bottom: -10px;
left: 50%;
margin-left: -10px;
border-top-style: solid;
border-bottom: none;
border-left-color: transparent;
border-right-color: transparent;

&:before {
bottom: 2px;
border: 10px solid $white;
content: " ";
position: absolute;
left: 50%;
margin-left: -10px;
border-top-style: solid;
border-bottom: none;
border-left-color: transparent;
border-right-color: transparent;
}
}
}

&.is-bottom {
top: $admin-bar-height-big + $item-spacing -1px;

@include break-medium {
top: $admin-bar-height + $item-spacing;
}

.editor-inserter__arrow {
top: -10px;
left: 50%;
margin-left: -10px;
border-bottom-style: solid;
border-top: none;
border-left-color: transparent;
border-right-color: transparent;

&:before {
top: 2px;
border: 10px solid $light-gray-100;
content: " ";
position: absolute;
left: 50%;
margin-left: -10px;
border-bottom-style: solid;
border-top: none;
border-left-color: transparent;
border-right-color: transparent;
}
}
}

&.is-right {
left: -32px;
.editor-inserter__arrow {
left: 49px;
&:before {
left: 0;
}
}
}
}

.editor-inserter__arrow {
border: 10px dashed $light-gray-500;
height: 0;
line-height: 0;
position: absolute;
width: 0;
z-index: z-index( '.editor-inserter__arrow' );

display: none;

@include break-medium {
display: block;
}
}

.editor-inserter__content {
height: calc( 100vh - #{ $admin-bar-height-big + $header-height + $icon-button-size } );

Expand Down Expand Up @@ -171,6 +69,10 @@ input[type=search].editor-inserter__search {
padding: 3px;
}

.editor-inserter__menu.is-bottom:after {
border-bottom-color: $light-gray-100;
}

.editor-inserter__block {
display: flex;
width: 50%;
Expand Down