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

How to handle onEnter / onExit animations? #138

Closed
Ganasist opened this issue Mar 15, 2017 · 5 comments
Closed

How to handle onEnter / onExit animations? #138

Ganasist opened this issue Mar 15, 2017 · 5 comments
Labels

Comments

@Ganasist
Copy link

Ganasist commented Mar 15, 2017

I'm trying to attach intro / exit transitions to my modals but have some difficulties. I'm only able to use onOpen to determine whether a Modal is active (and connecting that info to a redux store app-wide).

Is there a way to trigger transitions into and out of a modal appearance while preserving everything else?

Here is an example of how I'm calling a modal from a top container:

<Portal
  closeOnEsc
  isOpened={currentModal == 'shareOpen'}
  onClose={() => resetSidebarsAndToggle()}
>
  <ShareProduct />
</Portal>
@gucheen
Copy link
Contributor

gucheen commented Mar 23, 2017

I prefer to use ReactCSSTransitionGroup from React addons.
Refer to https://facebook.github.io/react/docs/animation.html.

@jcrben
Copy link

jcrben commented Apr 12, 2017

@gucheen how did you use it exactly? I found that ReactCSSTransitionGroup doesn't play real well with portal, but I was probably doing something wrong. I was wrapping <Portal> around CSSTransitionGroup. Should probably try the reverse. It was pretty easy to run a CSS keyframes animation in the beforeClose() callback and listen to animationend but I'm not sure that's the cleanest or most idiomatic React way...

UPDATE: I tried reversing it and the lower-level TransitionGroup and couldn't get it to work. Basically it works on the appearance, but on close it doesn't. Could use an established working example.

@gucheen
Copy link
Contributor

gucheen commented Apr 13, 2017

@jcrben Exactly. react-portal remove the container div instead of PseudoModal, so the leaving animation will not be triggered.
Manually add leave class to PseudoModal in the beforeClose may be the only way to trigger the css animation.
Currently I have no better solutions.

@4poc
Copy link

4poc commented Apr 14, 2017

In theory changing state in the beforeClose callback should allow us to render the closing animation in the child, adding a listener for the animationEnd event that will then call the removeFromDOM() method. However when the beforeClose callback is called the child is never updated thereafter, sounds like a bug to me.
I did wrote a wrapper that works around this problem by handling the open state independently and ignoring Portals own capability of doing so, I just paste it here just if anyone is interested:
This also adds the capability of the portal link to know about the portals open state.

import React, { PureComponent, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import Portal from 'react-portal';
import $ from 'jquery';

const ANIMATION_END_EVENTS = 'webkitAnimationEnd oanimationend oAnimationEnd msAnimationEnd animationend';

export default class PortalWrapper extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired,
    button: PropTypes.node.isRequired
  };

  constructor() {
    super();
    this.state = {
      open: false,
      openPortal: false
    };
    this._onToggle = ::this._onToggle;
    this._onOpen = ::this._onOpen;
    this._onClose = ::this._onClose;
  }

  _onToggle() {
    const { open } = this.state;
    if (open) {
      this._onClose();
    }
    else {
      this._onOpen();
    }
  }

  _onOpen() {
    this.setState({ open: true, openPortal: true });
  }

  _onClose() {
    const node = ReactDOM.findDOMNode(this.refs.child);
    $(node).on(ANIMATION_END_EVENTS, () => {
      this.setState({ openPortal: false }); // TODO: ensure the event is discarded on umount
    });
    this.setState({ open: false });
  }

  render() {
    const { children, button } = this.props;
    const { open, openPortal } = this.state;
    const props = {
      open, onOpen: this._onOpen, onClose: this._onClose, onToggle: this._onToggle
    };

    return (
        <div>
            {React.cloneElement(button, props)}
            <Portal isOpened={openPortal}>
                {React.cloneElement(children, { ...props, ref: 'child' })}
            </Portal>
        </div>
    );
  }
}

Not thoroughly tested yet, would probably want to add the removal of the animation listener on umount.

Usage: (Imagine PortalChild.scss having a .close class that plays a (forward) css animation.)

/*
 
    class PortalButton extends PureComponent {
      render() {
        const { open, onToggle } = this.props;
        return (
          <div onClick={onToggle}>
            { open ? 'close portal' : 'open portal' }
          </div>
        );
      }
    }

    class PortalChild extends PureComponent {
      render() {
        const styles = require('./PortalChild.scss');
        const { open, onClose } = this.props;
        console.log('PortalChild#render', open);
        return (
          <div className={styles.child + (!open ? (' ' + styles.close) : '')}>
            Hello World <div onClick={onClose}>(close)</div>
          </div>
        );
      }
    }

    class PortalTest extends PureComponent {
      render() {
        console.log('PortalTest#render');
        return (
          <PortalWrapper button={<PortalButton />}>
            <PortalChild />
          </PortalWrapper>
        );
      }
    }

*/

@tajo tajo added the question label Jul 29, 2017
@tajo
Copy link
Owner

tajo commented Oct 1, 2017

Hey, please check the new major version of react-portal: #157

It's React v16 only since its uses the new official Portal API. There is the first beta released and I would like to get your feedback. I don't have bandwidth to maintain v3 which is very different and full of hacks.

I am happy to discuss this with v4 in mind. If this is still a problem, please re-open/update the issue. Thanks.

@tajo tajo closed this as completed Oct 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants