Skip to content

Commit

Permalink
Add entered prop to Transition components (#504)
Browse files Browse the repository at this point in the history
* introduce `entered` prop on the Transition components

* update Dialog examples to make use of the `entered` prop
  • Loading branch information
RobinMalfait committed May 12, 2021
1 parent e40c66c commit d8e6f73
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 42 deletions.
10 changes: 5 additions & 5 deletions packages/@headlessui-react/pages/dialog/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@ export default function Home() {
<div className="fixed z-10 inset-0 overflow-y-auto">
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<Transition.Child
as={Fragment}
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"
>
<Dialog.Overlay className="fixed inset-0 transition-opacity">
<div className="absolute inset-0 bg-gray-500 opacity-75"></div>
</Dialog.Overlay>
<Dialog.Overlay className="fixed inset-0 bg-gray-500 transition-opacity" />
</Transition.Child>

<Transition.Child
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface TransitionClasses {
enter?: string
enterFrom?: string
enterTo?: string
entered?: string
leave?: string
leaveFrom?: string
leaveTo?: string
Expand Down Expand Up @@ -200,6 +201,7 @@ function TransitionChild<TTag extends ElementType = typeof DEFAULT_TRANSITION_CH
enter,
enterFrom,
enterTo,
entered,
leave,
leaveFrom,
leaveTo,
Expand Down Expand Up @@ -255,6 +257,8 @@ function TransitionChild<TTag extends ElementType = typeof DEFAULT_TRANSITION_CH
let enterFromClasses = useSplitClasses(enterFrom)
let enterToClasses = useSplitClasses(enterTo)

let enteredClasses = useSplitClasses(entered)

let leaveClasses = useSplitClasses(leave)
let leaveFromClasses = useSplitClasses(leaveFrom)
let leaveToClasses = useSplitClasses(leaveTo)
Expand Down Expand Up @@ -283,11 +287,11 @@ function TransitionChild<TTag extends ElementType = typeof DEFAULT_TRANSITION_CH
if (!show) events.current.beforeLeave()

return show
? transition(node, enterClasses, enterFromClasses, enterToClasses, reason => {
? 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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('<div class=""></div>')
expect(snapshots[2].content).toEqual('<div class="entered"></div>')

d.dispose()
})
Expand Down Expand Up @@ -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))
Expand All @@ -98,7 +98,7 @@ it('should wait the correct amount of time to finish a transition', async () =>

// Cleanup phase
expect(snapshots[3].content).toEqual(
`<div style="transition-duration: ${duration}ms;" class=""></div>`
`<div style="transition-duration: ${duration}ms;" class="entered"></div>`
)
})

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -74,6 +76,7 @@ export function transition(
d.add(
waitForTransition(node, reason => {
removeClasses(node, ...to, ...base)
addClasses(node, ...entered)
return _done(reason)
})
)
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<DialogOverlay class="fixed inset-0 transition-opacity">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</DialogOverlay>
<DialogOverlay className="fixed inset-0 bg-gray-500 transition-opacity" />
</TransitionChild>

<TransitionChild
Expand Down
51 changes: 35 additions & 16 deletions packages/@headlessui-vue/src/components/transitions/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export let TransitionChild = 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: '' },
Expand Down Expand Up @@ -168,6 +169,7 @@ export let TransitionChild = defineComponent({
enter,
enterFrom,
enterTo,
entered,
leave,
leaveFrom,
leaveTo,
Expand Down Expand Up @@ -243,6 +245,8 @@ export let TransitionChild = defineComponent({
let enterFromClasses = splitClasses(props.enterFrom)
let enterToClasses = splitClasses(props.enterTo)

let enteredClasses = splitClasses(props.entered)

let leaveClasses = splitClasses(props.leave)
let leaveFromClasses = splitClasses(props.leaveFrom)
let leaveToClasses = splitClasses(props.leaveTo)
Expand Down Expand Up @@ -277,23 +281,37 @@ export let TransitionChild = defineComponent({

onInvalidate(
show.value
? transition(node, enterClasses, enterFromClasses, enterToClasses, reason => {
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')
}
}
})
)
)
}

Expand Down Expand Up @@ -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: '' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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('<div class=""></div>')
expect(snapshots[2].content).toEqual('<div class="entered"></div>')

d.dispose()
})
Expand Down Expand Up @@ -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))
Expand All @@ -98,7 +98,7 @@ it('should wait the correct amount of time to finish a transition', async () =>

// Cleanup phase
expect(snapshots[3].content).toEqual(
`<div style="transition-duration: ${duration}ms;" class=""></div>`
`<div style="transition-duration: ${duration}ms;" class="entered"></div>`
)
})

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -72,6 +74,7 @@ export function transition(
d.add(
waitForTransition(node, reason => {
removeClasses(node, ...to, ...base)
addClasses(node, ...entered)
return _done(reason)
})
)
Expand All @@ -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))
Expand Down

0 comments on commit d8e6f73

Please sign in to comment.