diff --git a/src/MenuContext.js b/src/MenuContext.js index 2fe2585..ddaa6d0 100644 --- a/src/MenuContext.js +++ b/src/MenuContext.js @@ -11,6 +11,8 @@ const layoutsEqual = (a, b) => ( a === b || (a && b && a.width === b.width && a.height === b.height) ); +const isFunctional = Component => !Component.prototype.render; + export default class MenuContext extends Component { constructor(props) { @@ -47,14 +49,17 @@ export default class MenuContext extends Component { closeMenu() { debug('close menu'); - this._menuRegistry.getAll().forEach(menu => { - if (menu.instance._getOpened()) { - menu.instance._setOpened(false); - // invalidate trigger layout - this._menuRegistry.updateLayoutInfo(menu.name, { triggerLayout: undefined }); - } - }); - this._notify(); + const closeAnimation = (this.refs.menuOptions && this.refs.menuOptions.close) || Promise.resolve; + closeAnimation().then(() => { + this._menuRegistry.getAll().forEach(menu => { + if (menu.instance._getOpened()) { + menu.instance._setOpened(false); + // invalidate trigger layout + this._menuRegistry.updateLayoutInfo(menu.name, { triggerLayout: undefined }); + } + }); + this._notify(); + }) } toggleMenu(name) { @@ -166,7 +171,11 @@ export default class MenuContext extends Component { const style = [optionsContainerStyle, customStyles.optionsContainer]; const layouts = { windowLayout, triggerLayout, optionsLayout }; const props = { style, onLayout, layouts }; - return React.createElement(isOutside ? MenuOutside : renderer, props, optionsRenderer(options)); + const optionsType = isOutside ? MenuOutside : renderer; + if (!isFunctional(optionsType)) { + props.ref = 'menuOptions'; + } + return React.createElement(optionsType, props, optionsRenderer(options)); } _onLayout({ nativeEvent: { layout } }) { @@ -202,3 +211,4 @@ MenuContext.childContextTypes = { menuRegistry: React.PropTypes.object, menuActions: React.PropTypes.object, }; + diff --git a/src/renderers/ContextMenu.js b/src/renderers/ContextMenu.js index 10e016b..d0dc715 100644 --- a/src/renderers/ContextMenu.js +++ b/src/renderers/ContextMenu.js @@ -37,6 +37,7 @@ export const computePosition = ({ windowLayout, triggerLayout, optionsLayout }) return { top, left }; }; +const DURATION = 80; export default class ContextMenu extends React.Component { @@ -45,15 +46,25 @@ export default class ContextMenu extends React.Component { this.state = { scaleAnim: new Animated.Value(0.1), }; + this.close = this.close.bind(this); } componentDidMount() { Animated.timing(this.state.scaleAnim, { - duration: 80, + duration: DURATION, toValue: 1 }).start(); } + close() { + return new Promise(resolve => { + Animated.timing(this.state.scaleAnim, { + duration: DURATION, + toValue: 0 + }).start(resolve); + }); + } + render() { const { style, children, layouts, ...other } = this.props; const animation = { diff --git a/src/renderers/SlideInMenu.js b/src/renderers/SlideInMenu.js index 92f0f77..129f99a 100644 --- a/src/renderers/SlideInMenu.js +++ b/src/renderers/SlideInMenu.js @@ -9,6 +9,8 @@ export const computePosition = ({ windowLayout, optionsLayout }) => { return { top, left }; } +const DURATION = 100; + export default class SlideInMenu extends React.Component { constructor(props) { @@ -16,15 +18,25 @@ export default class SlideInMenu extends React.Component { this.state = { slide: new Animated.Value(0), }; + this.close = this.close.bind(this); } componentDidMount() { Animated.timing(this.state.slide, { - duration: 100, + duration: DURATION, toValue: 1 }).start(); } + close() { + return new Promise(resolve => { + Animated.timing(this.state.slide, { + duration: DURATION, + toValue: 0 + }).start(resolve); + }); + } + render() { const { style, children, layouts, ...other } = this.props; const { height: oHeight } = layouts.optionsLayout;