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

useTransition with multiple targets within a controller #160

Open
sevab opened this issue Jun 24, 2021 · 15 comments
Open

useTransition with multiple targets within a controller #160

sevab opened this issue Jun 24, 2021 · 15 comments

Comments

@sevab
Copy link

sevab commented Jun 24, 2021

It seems the current architecture of userTransition doesn't support multiple animated elements within a controller.

Was trying to implement Tailwind UI's modal component, which animates the backdrop separately from the modal dialog. However calling this.leave() only animates the last element passed to useTransition. In my case the dialog:

// "controllers/modal_controller.js"
import { Controller } from "stimulus"
import { useTransition } from 'stimulus-use';

export default class extends Controller {
  static targets = ['backdrop', 'dialog']
  connect() {
    useTransition(this, {
      element: this.backdropTarget,
      enterActive: 'ease-out duration-300',
      enterFrom: 'opacity-0',
      enterTo: 'opacity-100',
      leaveActive: 'ease-in duration-200',
      leaveFrom: 'opacity-100',
      leaveTo: 'opacity-0',
      hiddenClass: 'hidden'
    });

   // Only this animation is ran:
    useTransition(this, {
      element: this.dialogTarget,
      enterActive: 'ease-out duration-300',
      enterFrom: 'opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95',
      enterTo: 'opacity-100 translate-y-0 sm:scale-100',
      leaveActive: 'ease-in duration-200',
      leaveFrom: 'opacity-100 translate-y-0 sm:scale-100',
      leaveTo: 'opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95',
      hiddenClass: 'hidden'
    });

    this.open();
  }

  close() {
    this.leave()
  }

  open() {
    this.enter();
  }
}

Was wondering whether it's a use case useTransition would want to support in the future or whether it's intended to be used with just one element per controller?

@adrienpoly
Copy link
Contributor

Hello I have an on going refactoring of useTransition that will transition on multiple targets.

The API looks like that currently

....
static targets = ['item']
....
connect(){
  useTransition(this, {
      elements: "item",
      hiddenClass: 'hidden'
    });
}

We can pass the target name of the items to be transitioned.

In your case each item has a different animation. This should be possible by adding the the transition information as data attributes to the html mark up.

I don't have a firm ETA yet hopefully within 2 weeks  🤷‍♂️ ....

@sevab
Copy link
Author

sevab commented Jun 25, 2021

Hi Adrien, great to hear it's something that userTransition plans to support, appreciate your work on this.
The API for multiple elements looks good, and animating multiple elements via data-attributes sounds like a good approach as well.

Just wanted to share that in the meantime I went with el-transition package, which has the following API (the animations are specified via data-attributes Alpine.js-style).

import { Controller } from "stimulus"
import {enter, leave } from 'el-transition'

export default class extends Controller {
  static targets = ['backdrop', 'dialog']

  close() {
    leave(this.backdropTarget);
    leave(this.dialogTarget);
  }

  open() {
    enter(this.backdropTarget);
    enter(this.dialogTarget);
  }
}

While in this case I'm animating both targets at the same time, I like the offered flexibility of being able to animate them separately via enter / leave functions.

@sevab
Copy link
Author

sevab commented Jun 25, 2021

Forgot to mention that el-transition returns a promise for both enter/leave, which allows me to sequence animation nicely as well.

In case of the closing modal, I can wait for backdrop and dialog animation to finish to then hide the whole modal as well:

close() {
  Promise.all([leave(this.backdropTarget), leave(this.dialogTarget)]).then(() => {
    this.element.classList.add('hidden');
  });
}

Just thought I'd share in case it's valuable for the next iteration of useTransition api.

@seb-jean
Copy link

seb-jean commented Oct 5, 2021

I am also doing a modal with Tailwind and I have the same problem.

@aarongerig
Copy link

I would be very interested in this feature as well. Anything we could help you with? :)

@janklan
Copy link

janklan commented Apr 27, 2022

Just wondering, any progress here?

@pySilver
Copy link

In a mean time backdrop can live a separate controller that responds to some events.

@atwoodjw
Copy link

Just got bit by this. Without support for multiple targets within a controller, you can't really leverage useTransition for Tailwind components more complicated than a dropdown. Modals, sidebar layouts, etc. all require animating multiple targets. Creating multiple controllers for a single component is messy. I echo @sevab's suggestion to use el-transition until this gets sorted.

@olivier-thatch
Copy link

Same here, was writing a Stimulus component for a modal and realized I needed to add transitions to more than one target.

@adrienpoly I know it's been a while, but are you still working on this? If you could open even a draft PR, I think that'd be very helpful.

@pySilver
Copy link

What are the benefits of having this behaviour at all actually? I'm using el-transition directly in controllers and it works great. I haven't had a situation when Transition was required as dedicated feature decoupled from something more concrete like Modal or Disclosure.

@aviflombaum
Copy link

I was able to get this working by also exporting the helper function within use-transition.ts #transition() to the mixed in controller. With that you can manually trigger the transitions in your controller in addition to the singular one built into the library.

aviflombaum@107f90e

@aviflombaum
Copy link

@pySilver Ya, I guess since I'm already importing use-stimulus for a bunch of other features in my app, duplicating functionality that is right there with another import "feels" wrong. But ya.

@pySilver
Copy link

pySilver commented Mar 8, 2023

@aviflombaum I'm just not sure about implementation. How would control enter and leave for multiple targets for controller? It can work as wrapper so you call enter/leave imported from stimulus-use instead of el-transition .. but then what's the point in doing that? You can copy-paste el-transition into your app if you don't want to keep additional dependency. Its tiny.

@aviflombaum
Copy link

I mean I got it working with that temp hack but I can do it properly. Basically I'm thinking that every transition target should be storing whether it is transitionsed and not the parent controller. So take what it's doing right now and bind on multiple targets instead of just one, rescope transitioned to the target and that should do it. No matter what it's sort of nice to expose the lower level transition function to the mixing. It'd also be nice to have a method added to the target that can return the various classes for the target, it's called getAttribute in the library but it's too low level. It took me a min to get comfortable with the tests and tbh, like 2nd time playing with TS and tailwind transitions but I can do this is now and sounds fun.

@henrikbjorn
Copy link

Would love to see an API like this, which would also allow for multiple element transitions.

export default Controller {
  connect() {
    this.menuTransition = new Transition(this.menuTarget, {
      // ... classes options
      enterActive: 'asdsadasd',
     }

    this.backdropTransition = new Transition(this.backdropTarget, {
      // ...
    })
  }

  open() {
    Promise.all([
      this.menuTransition.enter(),
      this.backdropTransition.enter(),
    ])
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants