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

Add new data-attribute-based transition API #3273

Merged
merged 10 commits into from
Jun 11, 2024

Conversation

RobinMalfait
Copy link
Member

@RobinMalfait RobinMalfait commented Jun 6, 2024

We renamed the properties, see: #3285


This PR adds a new way of dealing with transitions. Instead of using the <Transition /> component and using the enter, enterFrom, enterTo, leave, leaveFrom and leaveTo props, we can now define all of that in CSS.

Most components that are rendered conditionally based on the state of the component:

  • ComboboxOptions
  • DisclosurePanel
  • ListboxOptions
  • MenuItems
  • PopoverPanel

Will now accept a transition prop. The moment that is enabled then we will expose data-from, data-enter, data-exit and data-transition data attributes at the correct time. You can use these data attributes in your CSS or Tailwind Classes to style these components.

The lifecycle of the data attributes look like this:

┌─────┐              │       ┌────────────┐
│From │              │       │From        │
└─────┘              │       └────────────┘
┌─────┐┌─────┐┌─────┐│┌─────┐┌─────┐┌─────┐
│Frame││Frame││Frame│││Frame││Frame││Frame│
└─────┘└─────┘└─────┘│└─────┘└─────┘└─────┘
┌───────────────────┐│┌───────────────────┐
│Enter              │││Exit               │
└───────────────────┘│└───────────────────┘
┌───────────────────┐│┌───────────────────┐
│Transition         │││Transition         │
├───────────────────┘│└───────────────────┘
│
└─ Applied when `Enter` or `Exit` is applied.
  • The data-transition attribute is added if an enter or exit transition is happening.
  • The data-enter attribute is added during the full enter transition from start to finish.
  • The data-exit attribute is added during the full exit transition from start to finish.

The data-from attribute is applied at different times depending on the enter or exit transition. The styles applied using data-from are for describing the from state or the initial state.

  • When there is an enter transition we will add the data-from attribute briefly to make sure that state is present in the DOM. Then, we take it away and let the browser transition the DOM node to it's default state (e.g.: opacity-100).
  • When there is an exit transition we will add the data-from attribute (and keep it there) after the transition starts because then we can go from the current state to the data-from state until finished. Once finished, we will unmount the DOM node.

For example, let's say you want to fade in the MenuItems, previously you would use the <Transition /> component for that:

<Transition
  enter="transition duration-200 ease-out"
  enterFrom="opacity-0"
  enterTo="opacity-100"
  leave="transition duration-200 ease-out"
  leaveFrom="opacity-100"
  leaveTo="opacity-0"
>
  <MenuItems />
</Transition>

There are a few things you might notice:

  1. The enterFrom and leaveTo are the same
  2. The enterTo and leaveFrom are the same
  3. The enterTo and leaveFrom are the default state of an element
  4. The enter and leave are the same

To use the data attribute approach, you can represent this like:

<MenuItems transition className="transition duration-200 ease-out data-[from]:opacity-0" />

The transition duration-200 ease-out were the same, so we can always define them.

The data-from data attribute represents the initial state, so we can define it once and it will be applied at the correct time.

If the duration or easing function is different depending on whether we are
entering or exiting, then we can do that explicitly as well:

Before:

<Transition
  enter="transition duration-200 ease-out"
  enterFrom="opacity-0"
  enterTo="opacity-100"
  leave="transition duration-75 ease-in"
  leaveFrom="opacity-100"
  leaveTo="opacity-0"
>
  <MenuItems />
</Transition>

After:

<MenuItems
  transition
  className="transition data-[from]:opacity-0 data-[enter]:duration-200 data-[exit]:duration-75 data-[enter]:ease-out data-[exit]:ease-in"
/>

If the enter and leave states are different, e.g.: when entering from the left, and exiting to the right. Then we can combine the data attributes:

<Transition
  enter="transition duration-200 ease-out"
  enterFrom="-translate-x-full"
  enterTo="translate-x-0"
  leave="transition duration-200 ease-out"
  leaveFrom="translate-x-0"
  leaveTo="translate-x-full"
>
  <MenuItems />
</Transition>

After:

<MenuItems
  transition
  className={clsx([
    // Defaults
    'transition duration-200 ease-out',

    // Enter specific
    'data-[enter]:data-[from]:-translate-x-full',

    // Exit specific
    'data-[exit]:data-[from]:translate-x-full',
  ])}
/>

Notice that we didn't specify the translate-x-0 case because that's again the default state of an element.


The existing <Transition> and <TransitionChild> will also expose these data attributes to the underlying DOM node so that you can use them in your CSS for components or elements that don't have a transition prop.

Copy link

vercel bot commented Jun 6, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
headlessui-react ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 7, 2024 9:31am
headlessui-vue ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 7, 2024 9:31am

This has been around since 2020, but JSDOM doesn't know about this yet,
so tests using JSDOM will fail otherwise.
@RobinMalfait RobinMalfait merged commit 6b6c259 into main Jun 11, 2024
8 checks passed
@RobinMalfait RobinMalfait deleted the feat/css-based-transitions branch June 11, 2024 15:53
RobinMalfait added a commit that referenced this pull request Jun 19, 2024
Due to a timing issue bug, we updated the snapshot tests in
#3273 incorrectly so this
commit fixes that which is why there are a lot of changes.

Most tests have `show={true}` but not `appear` which means that they
should _not_ transition which means that no data attributes should be
present.
RobinMalfait added a commit that referenced this pull request Jun 19, 2024
Due to a timing issue bug, we updated the snapshot tests in
#3273 incorrectly so this
commit fixes that which is why there are a lot of changes.

Most tests have `show={true}` but not `appear` which means that they
should _not_ transition which means that no data attributes should be
present.
RobinMalfait added a commit that referenced this pull request Jun 19, 2024
Due to a timing issue bug, we updated the snapshot tests in
#3273 incorrectly so this
commit fixes that which is why there are a lot of changes.

Most tests have `show={true}` but not `appear` which means that they
should _not_ transition which means that no data attributes should be
present.
RobinMalfait added a commit that referenced this pull request Jun 19, 2024
…ttributes (#3303)

* add optional `start` and `end` events to `useTransitionData`

This will be used when we implement the `<Transition />` component
purely using the `useTransitionData` information. But because there is a
hierarchy between `<Transition />` and `<TransitionChild />` we need to
know when transitions start and end.

* implement `<Transition />` and `<TransitionChild />` on top of `useTransitionData()`

* update tests

Due to a timing issue bug, we updated the snapshot tests in
#3273 incorrectly so this
commit fixes that which is why there are a lot of changes.

Most tests have `show={true}` but not `appear` which means that they
should _not_ transition which means that no data attributes should be
present.

* wait a microTask to ensure that `prepare()` has the time to render

Now that we set state instead of mutating the DOM directly we need to
wait a tiny bit and then we can trigger the transition to ensure
a smooth transition.

* cleanup `prepareTransition` now that it returns a cleanup function

* move `waitForTransition` and `prepareTransition` into `useTransitionData`

* remove existing `useTransition` hook and related utilities

* rename `useTransitionData` to `useTransition`

* update changelog

* Update packages/@headlessui-react/src/components/transition/transition.tsx

Co-authored-by: Jordan Pittman <jordan@cryptica.me>

* add missing `TransitionState.Enter`

This makes sure that the `Enter` state is applied initially when it has
to.

This also means that we can simplify the `prepareTransition` code again
because we don't need to wait for the next microTask which made sure
`TransitionState.Enter` was available.

* add transition playground page with both APIs

* update tests to reflect latest bug fix

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
@reinink reinink changed the title Introduce CSS based transitions Add new data attribute based transition API Jun 21, 2024
@reinink reinink changed the title Add new data attribute based transition API Add new data-attribute-based transition API Jun 21, 2024
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

Successfully merging this pull request may close these issues.

2 participants