Skip to content

Commit

Permalink
Merge pull request #2353 from oliviertassinari/popover-on-request-close
Browse files Browse the repository at this point in the history
[Popover] Fix onRequestClose called 3 times
  • Loading branch information
oliviertassinari committed Dec 3, 2015
2 parents c1a4d0a + 15dbab8 commit 03cdf02
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 96 deletions.
4 changes: 2 additions & 2 deletions docs/src/app/components/pages/components/popover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ let PopoverPage = React.createClass({
},
{
name: 'onRequestClose',
type: 'func',
type: 'function(reason)',
header: 'default: no-op',
desc: 'This is a callback that fires when the popover thinks it should close. (e.g. click-away or scroll off-screen)',
desc: 'This is a callback that fires when the popover thinks it should close. (e.g. clickAway or offScreen)',
},
{
name: 'zDepth',
Expand Down
6 changes: 0 additions & 6 deletions src/menu/menu-item.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,6 @@ const MenuItem = React.createClass({
};
},

componentWillUnmount() {
if (this.state.open) {
this.setState({open:false});
}
},

getTheme() {
return this.state.muiTheme.menuItem;
},
Expand Down
54 changes: 30 additions & 24 deletions src/menus/menu-item.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ import DefaultRawTheme from '../styles/raw-themes/light-raw-theme';
import ThemeManager from '../styles/theme-manager';
import Menu from './menu';

const nestedMenuStyle = {position:'relative'};
const nestedMenuStyle = {
position: 'relative',
};

const MenuItem = React.createClass({

mixins: [PureRenderMixin, StylePropable],
mixins: [
PureRenderMixin,
StylePropable,
],

contextTypes: {
muiTheme: React.PropTypes.object,
Expand Down Expand Up @@ -52,7 +58,7 @@ const MenuItem = React.createClass({
getInitialState() {
return {
muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme),
open:false,
open: false,
};
},

Expand Down Expand Up @@ -83,7 +89,9 @@ const MenuItem = React.createClass({

componentWillUnmount() {
if (this.state.open) {
this.setState({open:false});
this.setState({
open: false,
});
}
},

Expand Down Expand Up @@ -176,7 +184,7 @@ const MenuItem = React.createClass({
if (menuItems) {
childMenuPopover = (
<Popover
anchorOrigin={{horizontal:'right', vertical:'top'}}
anchorOrigin={{horizontal: 'right', vertical: 'top'}}
anchorEl={this.state.anchorEl}
open={this.state.open}
onRequestClose={this._onRequestClose}>
Expand Down Expand Up @@ -210,40 +218,38 @@ const MenuItem = React.createClass({
},

_cloneMenuItem(item) {
let props = {
onTouchTap: (e) =>
return React.cloneElement(item, {
onTouchTap: (event) =>
{
this._onRequestClose();
if (item.props.onTouchTap) {
item.props.onTouchTap(e);
if (!item.props.menuItems) {
this._onRequestClose();
}
if (this.props.onTouchTap) {
this.props.onTouchTap(e);

if (item.props.onTouchTap) {
item.props.onTouchTap(event);
}
},
onRequestClose: this._onRequestClose,
};
return React.cloneElement(item, props);
});
},

_onTouchTap(e) {
e.preventDefault();
_onTouchTap(event) {
event.preventDefault();

this.setState({
open:true,
anchorEl:ReactDOM.findDOMNode(this),
open: true,
anchorEl: ReactDOM.findDOMNode(this),
});

if (this.props.onTouchTap) {
this.props.onTouchTap(e);
this.props.onTouchTap(event);
}
},

_onRequestClose() {
if (!this.isMounted()) {
return;
}
this.setState({
open:false,
anchorEl:null,
open: false,
anchorEl: null,
});
},
});
Expand Down
116 changes: 52 additions & 64 deletions src/popover/popover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ import ReactDOM from 'react-dom';
import WindowListenable from '../mixins/window-listenable';
import RenderToLayer from '../render-to-layer';
import StylePropable from '../mixins/style-propable';
import CssEvent from '../utils/css-event';
import PropTypes from '../utils/prop-types';
import Transitions from '../styles/transitions';
import Paper from '../paper';
import throttle from 'lodash.throttle';
import AutoPrefix from '../styles/auto-prefix';
import ContextPure from '../mixins/context-pure';

const Popover = React.createClass({
mixins: [
ContextPure,
StylePropable,
WindowListenable,
],
Expand All @@ -36,27 +33,28 @@ const Popover = React.createClass({
getDefaultProps() {
return {
anchorOrigin: {
vertical:'bottom',
horizontal:'left',
vertical: 'bottom',
horizontal: 'left',
},
animated:true,
autoCloseWhenOffScreen:true,
canAutoPosition:true,
animated: true,
autoCloseWhenOffScreen: true,
canAutoPosition: true,
onRequestClose: () => {},
open:false,
open: false,
style: {},
targetOrigin: {
vertical:'top',
horizontal:'left',
vertical: 'top',
horizontal: 'left',
},
zDepth: 1,
};
},

getInitialState() {
this.setPlacementThrottled = throttle(this.setPlacement, 100);

return {
open: false,
open: this.props.open,
};
},

Expand All @@ -71,10 +69,18 @@ const Popover = React.createClass({

componentWillReceiveProps(nextProps) {
if (nextProps.open !== this.state.open) {
if (nextProps.open)
this._showInternal(nextProps.anchorEl);
else
this._hideInternal();
if (nextProps.open) {
this.anchorEl = nextProps.anchorEl || this.props.anchorEl;
this.setState({
open: true,
});
} else {
this.setState({
open: false,
}, () => {
this._animateClose();
});
}
}
},

Expand All @@ -84,7 +90,7 @@ const Popover = React.createClass({

componentWillUnmount() {
if (this.state.open) {
this.props.onRequestClose();
this._animateClose();
}
},

Expand Down Expand Up @@ -143,56 +149,39 @@ const Popover = React.createClass({
<div>
<div style={horizontalAnimation}>
<div style={verticalAnimation}>
{this.props.children}
{this.props.children}
</div>
</div>
</div>
</Paper>
);
},

requestClose() {
if (this.props.onRequestClose)
this.props.onRequestClose();
requestClose(reason) {
if (this.props.onRequestClose) {
this.props.onRequestClose(reason);
}
},

componentClickAway(e) {
if (e.defaultPrevented) {
componentClickAway(event) {
if (event.defaultPrevented) {
return;
}
this._hideInternal();

this.requestClose('clickAway');
},

_resizeAutoPosition() {
this.setPlacement();
},

_showInternal(anchorEl) {
this.anchorEl = anchorEl || this.props.anchorEl;
this.setState({open: true});
},

_hideInternal() {
if (!this.state.open) {
return;
}
this.setState({
open: false,
}, () => {
this._animateClose();
});
},

_animateClose() {
if (!this.refs.layer || !this.refs.layer.getLayer()) {
return;
}
let el = this.refs.layer.getLayer().children[0];
this._animate(el, false);
},

_animateOpen(el) {
this._animate(el, true);
const el = this.refs.layer.getLayer().children[0];
this._animate(el, false);
},

_animate(el) {
Expand All @@ -206,12 +195,6 @@ const Popover = React.createClass({
if (this.state.open) {
value = '1';
}
else {
CssEvent.onTransitionEnd(inner, () => {
if (!this.state.open)
this.requestClose();
});
}

AutoPrefix.set(el.style, 'transform', `scale(${value},${value})`);
AutoPrefix.set(innerInner.style, 'transform', `scaleX(${value})`);
Expand All @@ -223,8 +206,9 @@ const Popover = React.createClass({
},

getAnchorPosition(el) {
if (!el)
if (!el) {
el = ReactDOM.findDOMNode(this);
}

const rect = el.getBoundingClientRect();
const a = {
Expand All @@ -238,28 +222,31 @@ const Popover = React.createClass({
a.bottom = a.top + a.height;
a.middle = a.left + a.width / 2;
a.center = a.top + a.height / 2;

return a;
},

getTargetPosition(targetEl) {
return {
top:0,
top: 0,
center: targetEl.offsetHeight / 2,
bottom: targetEl.offsetHeight,
left:0,
middle:targetEl.offsetWidth / 2,
right:targetEl.offsetWidth,
left: 0,
middle: targetEl.offsetWidth / 2,
right: targetEl.offsetWidth,
};
},

setPlacement() {
if (!this.state.open)
if (!this.state.open) {
return;
}

const anchorEl = this.props.anchorEl || this.anchorEl;

if (!this.refs.layer.getLayer())
if (!this.refs.layer.getLayer()) {
return;
}

const targetEl = this.refs.layer.getLayer().children[0];
if (!targetEl) {
Expand All @@ -276,8 +263,9 @@ const Popover = React.createClass({
left: anchor[anchorOrigin.horizontal] - target[targetOrigin.horizontal],
};

if (this.props.autoCloseWhenOffScreen)
if (this.props.autoCloseWhenOffScreen) {
this.autoCloseWhenOffScreen(anchor);
}

if (this.props.canAutoPosition) {
target = this.getTargetPosition(targetEl); // update as height may have changed
Expand All @@ -287,18 +275,18 @@ const Popover = React.createClass({

targetEl.style.top = targetPosition.top + 'px';
targetEl.style.left = targetPosition.left + 'px';
this._animateOpen(targetEl);

this._animate(targetEl, true);
},

autoCloseWhenOffScreen(anchorPosition) {
if (!this.props.autoCloseWhenOffScreen)
return;
if (anchorPosition.top < 0
|| anchorPosition.top > window.innerHeight
|| anchorPosition.left < 0
|| anchorPosition.left > window.innerWith
)
this._hideInternal();
) {
this.requestClose('offScreen');
}
},

applyAutoPositionIfNeeded(anchor, target, targetOrigin, anchorOrigin, targetPosition) {
Expand Down

0 comments on commit 03cdf02

Please sign in to comment.