diff --git a/packages/@headlessui-react/pages/dialog/dialog.tsx b/packages/@headlessui-react/pages/dialog/dialog.tsx
index 1717428f63..8e3f0552fc 100644
--- a/packages/@headlessui-react/pages/dialog/dialog.tsx
+++ b/packages/@headlessui-react/pages/dialog/dialog.tsx
@@ -74,16 +74,16 @@ export default function Home() {
-
-
-
+
{
+ ? transition(node, enterClasses, enterFromClasses, enterToClasses, enteredClasses, reason => {
isTransitioning.current = false
if (reason === Reason.Finished) events.current.afterEnter()
})
- : transition(node, leaveClasses, leaveFromClasses, leaveToClasses, reason => {
+ : transition(node, leaveClasses, leaveFromClasses, leaveToClasses, enteredClasses, reason => {
isTransitioning.current = false
if (reason !== Reason.Finished) return
diff --git a/packages/@headlessui-react/src/components/transitions/utils/transition.test.ts b/packages/@headlessui-react/src/components/transitions/utils/transition.test.ts
index f5b2e0c01b..9918716179 100644
--- a/packages/@headlessui-react/src/components/transitions/utils/transition.test.ts
+++ b/packages/@headlessui-react/src/components/transitions/utils/transition.test.ts
@@ -27,7 +27,7 @@ it('should be possible to transition', async () => {
)
await new Promise(resolve => {
- transition(element, ['enter'], ['enterFrom'], ['enterTo'], resolve)
+ transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
})
await new Promise(resolve => d.nextFrame(resolve))
@@ -42,7 +42,7 @@ it('should be possible to transition', async () => {
// necessary to put the classes on the element and immediately remove them.
// Cleanup phase
- expect(snapshots[2].content).toEqual('')
+ expect(snapshots[2].content).toEqual('')
d.dispose()
})
@@ -71,7 +71,7 @@ it('should wait the correct amount of time to finish a transition', async () =>
)
let reason = await new Promise(resolve => {
- transition(element, ['enter'], ['enterFrom'], ['enterTo'], resolve)
+ transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
})
await new Promise(resolve => d.nextFrame(resolve))
@@ -98,7 +98,7 @@ it('should wait the correct amount of time to finish a transition', async () =>
// Cleanup phase
expect(snapshots[3].content).toEqual(
- ``
+ ``
)
})
@@ -128,7 +128,7 @@ it('should keep the delay time into account', async () => {
)
let reason = await new Promise(resolve => {
- transition(element, ['enter'], ['enterFrom'], ['enterTo'], resolve)
+ transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
})
await new Promise(resolve => d.nextFrame(resolve))
@@ -178,7 +178,7 @@ it('should be possible to cancel a transition at any time', async () => {
expect.assertions(2)
// Setup the transition
- let cancel = transition(element, ['enter'], ['enterFrom'], ['enterTo'], reason => {
+ let cancel = transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], reason => {
expect(reason).toBe(Reason.Cancelled)
})
diff --git a/packages/@headlessui-react/src/components/transitions/utils/transition.ts b/packages/@headlessui-react/src/components/transitions/utils/transition.ts
index 67f1ffb661..6201eae0cb 100644
--- a/packages/@headlessui-react/src/components/transitions/utils/transition.ts
+++ b/packages/@headlessui-react/src/components/transitions/utils/transition.ts
@@ -60,11 +60,13 @@ export function transition(
base: string[],
from: string[],
to: string[],
+ entered: string[],
done?: (reason: Reason) => void
) {
let d = disposables()
let _done = done !== undefined ? once(done) : () => {}
+ removeClasses(node, ...entered)
addClasses(node, ...base, ...from)
d.nextFrame(() => {
@@ -74,6 +76,7 @@ export function transition(
d.add(
waitForTransition(node, reason => {
removeClasses(node, ...to, ...base)
+ addClasses(node, ...entered)
return _done(reason)
})
)
@@ -83,7 +86,7 @@ export function transition(
// the node itself will be nullified and will be a no-op. In case of a full transition the classes
// are already removed which is also a no-op. However if you go from enter -> leave mid-transition
// then we have some leftovers that should be cleaned.
- d.add(() => removeClasses(node, ...base, ...from, ...to))
+ d.add(() => removeClasses(node, ...base, ...from, ...to, ...entered))
// When we get disposed early, than we should also call the done method but switch the reason.
d.add(() => _done(Reason.Cancelled))
diff --git a/packages/@headlessui-vue/examples/src/components/dialog/dialog.vue b/packages/@headlessui-vue/examples/src/components/dialog/dialog.vue
index 4b744c5533..161e97eb25 100644
--- a/packages/@headlessui-vue/examples/src/components/dialog/dialog.vue
+++ b/packages/@headlessui-vue/examples/src/components/dialog/dialog.vue
@@ -17,14 +17,13 @@
as="template"
enter="ease-out duration-300"
enterFrom="opacity-0"
- enterTo="opacity-100"
+ enterTo="opacity-75"
leave="ease-in duration-200"
- leaveFrom="opacity-100"
+ leaveFrom="opacity-75"
leaveTo="opacity-0"
+ entered="opacity-75"
>
-
-
-
+
{
- isTransitioning.value = false
- if (reason === Reason.Finished) emit('afterEnter')
- })
- : transition(node, leaveClasses, leaveFromClasses, leaveToClasses, reason => {
- isTransitioning.value = false
-
- if (reason !== Reason.Finished) return
-
- // When we don't have children anymore we can safely unregister from the parent and hide
- // ourselves.
- if (!hasChildren(nesting)) {
- state.value = TreeStates.Hidden
- unregister(id)
- emit('afterLeave')
+ ? transition(
+ node,
+ enterClasses,
+ enterFromClasses,
+ enterToClasses,
+ enteredClasses,
+ reason => {
+ isTransitioning.value = false
+ if (reason === Reason.Finished) emit('afterEnter')
+ }
+ )
+ : transition(
+ node,
+ leaveClasses,
+ leaveFromClasses,
+ leaveToClasses,
+ enteredClasses,
+ reason => {
+ isTransitioning.value = false
+
+ if (reason !== Reason.Finished) return
+
+ // When we don't have children anymore we can safely unregister from the parent and hide
+ // ourselves.
+ if (!hasChildren(nesting)) {
+ state.value = TreeStates.Hidden
+ unregister(id)
+ emit('afterLeave')
+ }
}
- })
+ )
)
}
@@ -334,6 +352,7 @@ export let TransitionRoot = defineComponent({
enter: { type: [String], default: '' },
enterFrom: { type: [String], default: '' },
enterTo: { type: [String], default: '' },
+ entered: { type: [String], default: '' },
leave: { type: [String], default: '' },
leaveFrom: { type: [String], default: '' },
leaveTo: { type: [String], default: '' },
diff --git a/packages/@headlessui-vue/src/components/transitions/utils/transition.test.ts b/packages/@headlessui-vue/src/components/transitions/utils/transition.test.ts
index f5b2e0c01b..9918716179 100644
--- a/packages/@headlessui-vue/src/components/transitions/utils/transition.test.ts
+++ b/packages/@headlessui-vue/src/components/transitions/utils/transition.test.ts
@@ -27,7 +27,7 @@ it('should be possible to transition', async () => {
)
await new Promise(resolve => {
- transition(element, ['enter'], ['enterFrom'], ['enterTo'], resolve)
+ transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
})
await new Promise(resolve => d.nextFrame(resolve))
@@ -42,7 +42,7 @@ it('should be possible to transition', async () => {
// necessary to put the classes on the element and immediately remove them.
// Cleanup phase
- expect(snapshots[2].content).toEqual('')
+ expect(snapshots[2].content).toEqual('')
d.dispose()
})
@@ -71,7 +71,7 @@ it('should wait the correct amount of time to finish a transition', async () =>
)
let reason = await new Promise(resolve => {
- transition(element, ['enter'], ['enterFrom'], ['enterTo'], resolve)
+ transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
})
await new Promise(resolve => d.nextFrame(resolve))
@@ -98,7 +98,7 @@ it('should wait the correct amount of time to finish a transition', async () =>
// Cleanup phase
expect(snapshots[3].content).toEqual(
- ``
+ ``
)
})
@@ -128,7 +128,7 @@ it('should keep the delay time into account', async () => {
)
let reason = await new Promise(resolve => {
- transition(element, ['enter'], ['enterFrom'], ['enterTo'], resolve)
+ transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], resolve)
})
await new Promise(resolve => d.nextFrame(resolve))
@@ -178,7 +178,7 @@ it('should be possible to cancel a transition at any time', async () => {
expect.assertions(2)
// Setup the transition
- let cancel = transition(element, ['enter'], ['enterFrom'], ['enterTo'], reason => {
+ let cancel = transition(element, ['enter'], ['enterFrom'], ['enterTo'], ['entered'], reason => {
expect(reason).toBe(Reason.Cancelled)
})
diff --git a/packages/@headlessui-vue/src/components/transitions/utils/transition.ts b/packages/@headlessui-vue/src/components/transitions/utils/transition.ts
index 24baf21678..8382141da9 100644
--- a/packages/@headlessui-vue/src/components/transitions/utils/transition.ts
+++ b/packages/@headlessui-vue/src/components/transitions/utils/transition.ts
@@ -58,11 +58,13 @@ export function transition(
base: string[],
from: string[],
to: string[],
+ entered: string[],
done?: (reason: Reason) => void
) {
let d = disposables()
let _done = done !== undefined ? once(done) : () => {}
+ removeClasses(node, ...entered)
addClasses(node, ...base, ...from)
d.nextFrame(() => {
@@ -72,6 +74,7 @@ export function transition(
d.add(
waitForTransition(node, reason => {
removeClasses(node, ...to, ...base)
+ addClasses(node, ...entered)
return _done(reason)
})
)
@@ -81,7 +84,7 @@ export function transition(
// the node itself will be nullified and will be a no-op. In case of a full transition the classes
// are already removed which is also a no-op. However if you go from enter -> leave mid-transition
// then we have some leftovers that should be cleaned.
- d.add(() => removeClasses(node, ...base, ...from, ...to))
+ d.add(() => removeClasses(node, ...base, ...from, ...to, ...entered))
// When we get disposed early, than we should also call the done method but switch the reason.
d.add(() => _done(Reason.Cancelled))