Change default tag from div to Fragment on Transition components (#3110)
* use `Fragment` as the default element for `Transition` components * update tests to reflect default tag change * only error on missing `ref` if it's actually required If the `<Transition />` component "root" is used as a root placeholder (for state management) and not making actual transitions itself, then we don't require a `ref` element. * add test to ensure we don't error on missing `ref` when not required + add `className="…"` to some places to indicate that we _do_ want to perform a transition and thus have to fail if the `ref` is missing. * improve `requiresRef` check Also ensure that a ref is required if the `as` prop is provided and it's not a `Fragment` * add `shouldForwardRef` helper * fix broken tests These tests were rendering a `Debug` element that didn't render any DOM nodes. Adding `as="div"` ensures that we are forwarding the ref correctly. * update changelog * update playgrounds to reflect tag change * Tweak changelog --------- Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
This commit is contained in:
@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Use native `useId` and `useSyncExternalStore` hooks ([#3092](https://github.com/tailwindlabs/headlessui/pull/3092))
|
||||
- Use `absolute` as the default Floating UI strategy ([#3097](https://github.com/tailwindlabs/headlessui/pull/3097))
|
||||
- Change default tags for `ListboxOptions`, `ListboxOption`, `ComboboxOptions`, `ComboboxOption` and `TabGroup` components ([#3109](https://github.com/tailwindlabs/headlessui/pull/3109))
|
||||
- Change default tag from `div` to `Fragment` on `Transition` components ([#3110](https://github.com/tailwindlabs/headlessui/pull/3110))
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ describe('Composition', () => {
|
||||
<Transition>
|
||||
<Debug name="Transition" fn={orderFn} />
|
||||
<Disclosure.Panel>
|
||||
<Transition.Child>
|
||||
<Transition.Child as="div">
|
||||
<Debug name="Transition.Child" fn={orderFn} />
|
||||
</Transition.Child>
|
||||
</Disclosure.Panel>
|
||||
|
||||
@@ -977,7 +977,7 @@ describe('Composition', () => {
|
||||
<Transition>
|
||||
<Debug name="Transition" fn={orderFn} />
|
||||
<Popover.Panel>
|
||||
<Transition.Child>
|
||||
<Transition.Child as="div">
|
||||
<Debug name="Transition.Child" fn={orderFn} />
|
||||
</Transition.Child>
|
||||
</Popover.Panel>
|
||||
|
||||
-6
@@ -111,12 +111,6 @@ exports[`Setup API shallow should passthrough all the props (that we do not use
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`Setup API shallow should render a div and its children by default 1`] = `
|
||||
<div>
|
||||
Children
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Setup API shallow should render another component if the \`as\` prop is used and its children by default 1`] = `
|
||||
<a>
|
||||
Children
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { act as _act, fireEvent, render } from '@testing-library/react'
|
||||
import React, { Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { getByText } from '../../test-utils/accessibility-assertions'
|
||||
import { executeTimeline } from '../../test-utils/execute-timeline'
|
||||
import { click } from '../../test-utils/interactions'
|
||||
@@ -22,7 +22,7 @@ function nextFrame() {
|
||||
it('should not steal the ref from the child', async () => {
|
||||
let fn = jest.fn()
|
||||
render(
|
||||
<Transition show={true} as={Fragment}>
|
||||
<Transition show={true}>
|
||||
<div ref={fn}>...</div>
|
||||
</Transition>
|
||||
)
|
||||
@@ -40,11 +40,6 @@ it('should render without crashing', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should be possible to render a Transition without children', () => {
|
||||
render(<Transition show={true} className="transition" />)
|
||||
expect(document.getElementsByClassName('transition')).not.toBeNull()
|
||||
})
|
||||
|
||||
it(
|
||||
'should yell at us when we forget the required show prop',
|
||||
suppressConsoleLogs(() => {
|
||||
@@ -62,16 +57,15 @@ it(
|
||||
|
||||
describe('Setup API', () => {
|
||||
describe('shallow', () => {
|
||||
it('should render a div and its children by default', () => {
|
||||
let { container } = render(<Transition show={true}>Children</Transition>)
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should passthrough all the props (that we do not use internally)', () => {
|
||||
let { container } = render(
|
||||
/**
|
||||
* Renders a Fragment by default and forwards props. But not possible to
|
||||
* type in TypeScript land. This is also discouraged, but it works.
|
||||
*/
|
||||
// @ts-expect-error
|
||||
<Transition show={true} id="root" className="text-blue-400">
|
||||
Children
|
||||
<div>Children</div>
|
||||
</Transition>
|
||||
)
|
||||
|
||||
@@ -99,7 +93,11 @@ describe('Setup API', () => {
|
||||
})
|
||||
|
||||
it('should render nothing when the show prop is false', () => {
|
||||
let { container } = render(<Transition show={false}>Children</Transition>)
|
||||
let { container } = render(
|
||||
<Transition show={false}>
|
||||
<div>Children</div>
|
||||
</Transition>
|
||||
)
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot()
|
||||
})
|
||||
@@ -115,11 +113,7 @@ describe('Setup API', () => {
|
||||
})
|
||||
|
||||
it('should be possible to use a render prop', () => {
|
||||
let { container } = render(
|
||||
<Transition show={true} as={Fragment}>
|
||||
{() => <span>Children</span>}
|
||||
</Transition>
|
||||
)
|
||||
let { container } = render(<Transition show={true}>{() => <span>Children</span>}</Transition>)
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot()
|
||||
})
|
||||
@@ -135,7 +129,7 @@ describe('Setup API', () => {
|
||||
|
||||
expect(() => {
|
||||
render(
|
||||
<Transition show={true} as={Fragment}>
|
||||
<Transition show={true} enter="duration-200">
|
||||
{() => <Dummy />}
|
||||
</Transition>
|
||||
)
|
||||
@@ -153,7 +147,9 @@ describe('Setup API', () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<div className="My Page">
|
||||
<Transition.Child>Oops</Transition.Child>
|
||||
<Transition.Child>
|
||||
<div>Oops</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
)
|
||||
}).toThrowErrorMatchingSnapshot()
|
||||
@@ -163,7 +159,9 @@ describe('Setup API', () => {
|
||||
it('should be possible to render a Transition.Child without children', () => {
|
||||
render(
|
||||
<Transition show={true}>
|
||||
<Transition.Child className="transition" />
|
||||
<Transition.Child>
|
||||
<div className="transition" />
|
||||
</Transition.Child>
|
||||
</Transition>
|
||||
)
|
||||
expect(document.getElementsByClassName('transition')).not.toBeNull()
|
||||
@@ -172,7 +170,9 @@ describe('Setup API', () => {
|
||||
it('should be possible to use a Transition.Root and a Transition.Child', () => {
|
||||
render(
|
||||
<Transition.Root show={true}>
|
||||
<Transition.Child className="transition" />
|
||||
<Transition.Child>
|
||||
<div className="transition" />
|
||||
</Transition.Child>
|
||||
</Transition.Root>
|
||||
)
|
||||
expect(document.getElementsByClassName('transition')).not.toBeNull()
|
||||
@@ -182,8 +182,14 @@ describe('Setup API', () => {
|
||||
let { container } = render(
|
||||
<div className="My Page">
|
||||
<Transition show={true}>
|
||||
<Transition.Child>Sidebar</Transition.Child>
|
||||
<Transition.Child>Content</Transition.Child>
|
||||
<div>
|
||||
<Transition.Child>
|
||||
<div>Sidebar</div>
|
||||
</Transition.Child>
|
||||
<Transition.Child>
|
||||
<div>Content</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
@@ -195,8 +201,10 @@ describe('Setup API', () => {
|
||||
let { container } = render(
|
||||
<div className="My Page">
|
||||
<Transition show={true}>
|
||||
<Transition.Child as="aside">Sidebar</Transition.Child>
|
||||
<Transition.Child as="section">Content</Transition.Child>
|
||||
<div>
|
||||
<Transition.Child as="aside">Sidebar</Transition.Child>
|
||||
<Transition.Child as="section">Content</Transition.Child>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
@@ -221,8 +229,10 @@ describe('Setup API', () => {
|
||||
let { container } = render(
|
||||
<div className="My Page">
|
||||
<Transition show={true}>
|
||||
<Transition.Child as={Fragment}>{() => <aside>Sidebar</aside>}</Transition.Child>
|
||||
<Transition.Child as={Fragment}>{() => <section>Content</section>}</Transition.Child>
|
||||
<div>
|
||||
<Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>
|
||||
<Transition.Child>{() => <section>Content</section>}</Transition.Child>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
@@ -233,13 +243,11 @@ describe('Setup API', () => {
|
||||
it('should be possible to use render props on the Transition and Transition.Child components', () => {
|
||||
let { container } = render(
|
||||
<div className="My Page">
|
||||
<Transition show={true} as={Fragment}>
|
||||
<Transition show={true}>
|
||||
{() => (
|
||||
<article>
|
||||
<Transition.Child as={Fragment}>{() => <aside>Sidebar</aside>}</Transition.Child>
|
||||
<Transition.Child as={Fragment}>
|
||||
{() => <section>Content</section>}
|
||||
</Transition.Child>
|
||||
<Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>
|
||||
<Transition.Child>{() => <section>Content</section>}</Transition.Child>
|
||||
</article>
|
||||
)}
|
||||
</Transition>
|
||||
@@ -262,8 +270,14 @@ describe('Setup API', () => {
|
||||
render(
|
||||
<div className="My Page">
|
||||
<Transition show={true}>
|
||||
<Transition.Child as={Fragment}>{() => <Dummy>Sidebar</Dummy>}</Transition.Child>
|
||||
<Transition.Child as={Fragment}>{() => <Dummy>Content</Dummy>}</Transition.Child>
|
||||
<div>
|
||||
<Transition.Child enter="duration-200">
|
||||
{() => <Dummy>Sidebar</Dummy>}
|
||||
</Transition.Child>
|
||||
<Transition.Child enter="duration-200">
|
||||
{() => <Dummy>Content</Dummy>}
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
@@ -271,6 +285,28 @@ describe('Setup API', () => {
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should not error when the `Transition` component does not contain any props that need to be forwarded',
|
||||
suppressConsoleLogs(() => {
|
||||
expect.assertions(1)
|
||||
|
||||
expect(() => {
|
||||
render(
|
||||
<div className="My Page">
|
||||
<Transition show={true}>
|
||||
<Transition.Child>
|
||||
<div>Sidebar</div>
|
||||
</Transition.Child>
|
||||
<Transition.Child>
|
||||
<div>Content</div>
|
||||
</Transition.Child>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}).not.toThrow()
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should yell at us when we forgot to forward a ref on the Transition component',
|
||||
suppressConsoleLogs(() => {
|
||||
@@ -283,7 +319,7 @@ describe('Setup API', () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<div className="My Page">
|
||||
<Transition show={true} as={Fragment}>
|
||||
<Transition show={true} enter="duration-200">
|
||||
{() => (
|
||||
<Dummy>
|
||||
<Transition.Child>{() => <aside>Sidebar</aside>}</Transition.Child>
|
||||
@@ -362,7 +398,7 @@ describe('Setup API', () => {
|
||||
leaveFrom="leave-from"
|
||||
leaveTo="leave-to"
|
||||
>
|
||||
Children
|
||||
<div>Children</div>
|
||||
</Transition>
|
||||
)
|
||||
|
||||
@@ -387,7 +423,7 @@ describe('Setup API', () => {
|
||||
leaveFrom="leave-from"
|
||||
leaveTo="leave-to"
|
||||
>
|
||||
Children
|
||||
<div>Children</div>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
@@ -732,10 +768,10 @@ describe('Transitions', () => {
|
||||
|
||||
<Transition show={show}>
|
||||
<Transition.Child leave="leave-fast" leaveFrom="leave-from" leaveTo="leave-to">
|
||||
I am fast
|
||||
<div>I am fast</div>
|
||||
</Transition.Child>
|
||||
<Transition.Child leave="leave-slow" leaveFrom="leave-from" leaveTo="leave-to">
|
||||
I am slow
|
||||
<div>I am slow</div>
|
||||
</Transition.Child>
|
||||
</Transition>
|
||||
|
||||
@@ -781,11 +817,11 @@ describe('Transitions', () => {
|
||||
<Transition.Child leave="leave-fast" leaveFrom="leave-from" leaveTo="leave-to">
|
||||
<span>I am fast</span>
|
||||
<Transition show={show} leave="leave-slow">
|
||||
I am my own root component and I don't talk to the parent
|
||||
<div>I am my own root component and I don't talk to the parent</div>
|
||||
</Transition>
|
||||
</Transition.Child>
|
||||
<Transition.Child leave="leave-slow" leaveFrom="leave-from" leaveTo="leave-to">
|
||||
I am slow
|
||||
<div>I am slow</div>
|
||||
</Transition.Child>
|
||||
</Transition>
|
||||
|
||||
@@ -953,12 +989,22 @@ describe('Events', () => {
|
||||
<style>{`.child-2-2.leave { transition-duration: ${leaveDuration * 2.5}ms; }`}</style>
|
||||
|
||||
<Transition show={show} {...getProps('root')}>
|
||||
<Transition.Child {...getProps('child-1')}>Child 1.</Transition.Child>
|
||||
<Transition.Child {...getProps('child-2')}>
|
||||
Child 2.
|
||||
<Transition.Child {...getProps('child-2-1')}>Child 2.1.</Transition.Child>
|
||||
<Transition.Child {...getProps('child-2-2')}>Child 2.2.</Transition.Child>
|
||||
</Transition.Child>
|
||||
<div>
|
||||
<Transition.Child {...getProps('child-1')}>
|
||||
<div>Child 1.</div>
|
||||
</Transition.Child>
|
||||
<Transition.Child {...getProps('child-2')}>
|
||||
<div>
|
||||
<div>Child 2.</div>
|
||||
<Transition.Child {...getProps('child-2-1')}>
|
||||
<div>Child 2.1.</div>
|
||||
</Transition.Child>
|
||||
<Transition.Child {...getProps('child-2-2')}>
|
||||
<div>Child 2.2.</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<button
|
||||
@@ -1104,7 +1150,9 @@ describe('Events', () => {
|
||||
leave="leave-1"
|
||||
leaveFrom="leave-from"
|
||||
leaveTo="leave-to"
|
||||
/>
|
||||
>
|
||||
<div />
|
||||
</Transition.Child>
|
||||
<Transition.Child
|
||||
enter="enter-1"
|
||||
enterFrom="enter-from"
|
||||
|
||||
@@ -49,6 +49,44 @@ function splitClasses(classes: string = '') {
|
||||
return classes.split(/\s+/).filter((className) => className.length > 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should forward the ref to the child element or not. This is to
|
||||
* prevent crashes when the `as` prop is a Fragment _and_ the component just acts
|
||||
* as a state container (aka, there is no actual transition happening).
|
||||
*
|
||||
* E.g.:
|
||||
*
|
||||
* ```tsx
|
||||
* <Transition show={true}>
|
||||
* <Transition.Child enter="duration-100"><div>Child 1</div></Transition.Child>
|
||||
* <Transition.Child enter="duration-200"><div>Child 2</div></Transition.Child>
|
||||
* </Transition>
|
||||
* ```
|
||||
*
|
||||
* In this scenario, the child components are transitioning, but the
|
||||
* `Transition` parent, which is a `Fragment`, is not. So we should not forward
|
||||
* the ref to the `Fragment`.
|
||||
*/
|
||||
function shouldForwardRef<TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG>(
|
||||
props: TransitionRootProps<TTag>
|
||||
) {
|
||||
return (
|
||||
// If we have any of the enter/leave classes
|
||||
Boolean(
|
||||
props.enter ||
|
||||
props.enterFrom ||
|
||||
props.enterTo ||
|
||||
props.leave ||
|
||||
props.leaveFrom ||
|
||||
props.leaveTo
|
||||
) ||
|
||||
// If the `as` prop is not a Fragment
|
||||
(props.as ?? DEFAULT_TRANSITION_CHILD_TAG) !== Fragment ||
|
||||
// If we have a single child, then we can forward the ref directly
|
||||
React.Children.count(props.children) === 1
|
||||
)
|
||||
}
|
||||
|
||||
interface TransitionContextValues {
|
||||
show: boolean
|
||||
appear: boolean
|
||||
@@ -264,7 +302,7 @@ function useNesting(done?: () => void, parent?: NestingContextValues) {
|
||||
|
||||
// ---
|
||||
|
||||
let DEFAULT_TRANSITION_CHILD_TAG = 'div' as const
|
||||
let DEFAULT_TRANSITION_CHILD_TAG = Fragment
|
||||
type TransitionChildRenderPropArg = MutableRefObject<HTMLDivElement>
|
||||
let TransitionChildRenderFeatures = RenderFeatures.RenderStrategy
|
||||
|
||||
@@ -292,7 +330,9 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
...rest
|
||||
} = props as typeof props
|
||||
let container = useRef<HTMLElement | null>(null)
|
||||
let transitionRef = useSyncRefs(container, ref)
|
||||
let requiresRef = shouldForwardRef(props)
|
||||
|
||||
let transitionRef = useSyncRefs(...(requiresRef ? [container, ref] : ref === null ? [] : [ref]))
|
||||
let strategy = rest.unmount ?? true ? RenderStrategy.Unmount : RenderStrategy.Hidden
|
||||
|
||||
let { show, appear, initial } = useTransitionContext()
|
||||
@@ -342,10 +382,12 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
let ready = useServerHandoffComplete()
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
if (!requiresRef) return
|
||||
|
||||
if (ready && state === TreeStates.Visible && container.current === null) {
|
||||
throw new Error('Did you forget to passthrough the `ref` to the actual DOM node?')
|
||||
}
|
||||
}, [container, state, ready])
|
||||
}, [container, state, ready, requiresRef])
|
||||
|
||||
// Skipping initial transition
|
||||
let skip = initial && !appear
|
||||
@@ -495,7 +537,11 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
|
||||
// @ts-expect-error
|
||||
let { show, appear = false, unmount = true, ...theirProps } = props as typeof props
|
||||
let internalTransitionRef = useRef<HTMLElement | null>(null)
|
||||
let transitionRef = useSyncRefs(internalTransitionRef, ref)
|
||||
let requiresRef = shouldForwardRef(props)
|
||||
|
||||
let transitionRef = useSyncRefs(
|
||||
...(requiresRef ? [internalTransitionRef, ref] : ref === null ? [] : [ref])
|
||||
)
|
||||
|
||||
// The TransitionChild will also call this hook, and we have to make sure that we are ready.
|
||||
useServerHandoffComplete()
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
function MyDialog({ open, close }) {
|
||||
return (
|
||||
<>
|
||||
<Transition show={open} as={Fragment}>
|
||||
<Transition show={open}>
|
||||
<Dialog onClose={close} className="relative z-50">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transition duration-500 ease-out"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Dialog, Menu, Portal, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import Flatpickr from 'react-flatpickr'
|
||||
import { Button } from '../../components/button'
|
||||
import { classNames } from '../../utils/class-names'
|
||||
@@ -64,7 +64,6 @@ export default function Home() {
|
||||
<Transition
|
||||
data-debug="Dialog"
|
||||
show={isOpen}
|
||||
as={Fragment}
|
||||
beforeEnter={() => console.log('[Transition] Before enter')}
|
||||
afterEnter={() => console.log('[Transition] After enter')}
|
||||
beforeLeave={() => console.log('[Transition] Before leave')}
|
||||
@@ -79,7 +78,6 @@ export default function Home() {
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-75"
|
||||
@@ -96,6 +94,7 @@ export default function Home() {
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as="div"
|
||||
enter="ease-out transform duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { ExclamationIcon } from '@heroicons/react/outline'
|
||||
import { Fragment, useRef, useState } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
export default function Example() {
|
||||
const [open, setOpen] = useState(false)
|
||||
@@ -18,10 +18,9 @@ export default function Example() {
|
||||
Open Dialog
|
||||
</button>
|
||||
</div>
|
||||
<Transition.Root show={open} as={Fragment}>
|
||||
<Transition.Root show={open}>
|
||||
<Dialog as="div" className="relative z-10" initialFocus={cancelButtonRef} onClose={setOpen}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
@@ -35,7 +34,6 @@ export default function Example() {
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="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"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Dialog, Transition } from '@headlessui/react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function Home() {
|
||||
let [isOpen, setIsOpen] = useState(false)
|
||||
@@ -37,7 +37,6 @@ export default function Home() {
|
||||
<Transition
|
||||
data-debug="Dialog"
|
||||
show={isOpen}
|
||||
as={Fragment}
|
||||
beforeEnter={() => console.log('[Transition] Before enter')}
|
||||
afterEnter={() => console.log('[Transition] After enter')}
|
||||
beforeLeave={() => console.log('[Transition] Before leave')}
|
||||
@@ -52,7 +51,6 @@ export default function Home() {
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-75"
|
||||
@@ -69,6 +67,7 @@ export default function Home() {
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as="div"
|
||||
enter="ease-out transform duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Listbox, Transition } from '@headlessui/react'
|
||||
import { Fragment, useEffect, useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
let people = [
|
||||
'Wade Cooper',
|
||||
@@ -60,7 +60,6 @@ export default function Home() {
|
||||
</span>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition duration-500 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { Fragment } from 'react'
|
||||
import { Button } from '../../components/button'
|
||||
import { classNames } from '../../utils/class-names'
|
||||
|
||||
@@ -30,7 +29,6 @@ export default function Home() {
|
||||
</span>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition duration-500 ease-out"
|
||||
enterFrom="transform scale-95 opacity-0"
|
||||
enterTo="transform scale-100 opacity-100"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Popover, Portal, Transition } from '@headlessui/react'
|
||||
import React, { Fragment, forwardRef } from 'react'
|
||||
import React, { forwardRef } from 'react'
|
||||
import { usePopper } from '../../utils/hooks/use-popper'
|
||||
|
||||
let Button = forwardRef(
|
||||
@@ -33,7 +33,6 @@ export default function Home() {
|
||||
<Popover.Group as="nav" aria-label="Mythical University" className="flex space-x-3">
|
||||
<Popover as="div" className="relative">
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-300 transform"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
|
||||
@@ -18,6 +18,7 @@ export default function AppearExample() {
|
||||
<span className="mb-2">Initial render</span>
|
||||
<div className="grid max-w-6xl grid-cols-4 gap-4">
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={true}
|
||||
@@ -50,6 +51,7 @@ export default function AppearExample() {
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={true}
|
||||
@@ -82,6 +84,7 @@ export default function AppearExample() {
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={false}
|
||||
@@ -114,6 +117,7 @@ export default function AppearExample() {
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={false}
|
||||
@@ -152,6 +156,7 @@ export default function AppearExample() {
|
||||
<span className="mb-2">Not on the initial render</span>
|
||||
<div className="grid max-w-6xl grid-cols-4 gap-4">
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={true}
|
||||
@@ -184,6 +189,7 @@ export default function AppearExample() {
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={true}
|
||||
@@ -216,6 +222,7 @@ export default function AppearExample() {
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={true}
|
||||
unmount={false}
|
||||
@@ -248,6 +255,7 @@ export default function AppearExample() {
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={show}
|
||||
appear={false}
|
||||
unmount={false}
|
||||
|
||||
@@ -44,6 +44,7 @@ function Dropdown() {
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={isOpen}
|
||||
enter="transition ease-out duration-75"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
|
||||
@@ -41,6 +41,7 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={isOpen}
|
||||
className="fixed inset-0 z-10 overflow-y-auto"
|
||||
beforeEnter={() => {
|
||||
@@ -78,6 +79,7 @@ export default function Home() {
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span className="hidden sm:inline-block sm:h-screen sm:align-middle"></span>​
|
||||
<Transition.Child
|
||||
as="div"
|
||||
className="inline-block transform overflow-hidden rounded-lg bg-white text-left align-bottom shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:align-middle"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Home() {
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<Transition show={isOpen} unmount={false}>
|
||||
<Transition as="div" show={isOpen} unmount={false}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Home() {
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<Transition show={isOpen} unmount={true}>
|
||||
<Transition as="div" show={isOpen} unmount={true}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Box>
|
||||
@@ -43,6 +43,7 @@ export default function Home() {
|
||||
function Box({ children }: { children?: ReactNode }) {
|
||||
return (
|
||||
<Transition.Child
|
||||
as="div"
|
||||
unmount={true}
|
||||
enter="transition translate duration-300"
|
||||
enterFrom="transform -translate-x-full"
|
||||
|
||||
@@ -19,6 +19,7 @@ export default function Home() {
|
||||
</span>
|
||||
|
||||
<Transition
|
||||
as="div"
|
||||
show={isOpen}
|
||||
appear={false}
|
||||
beforeEnter={() => console.log('beforeEnter')}
|
||||
|
||||
@@ -160,6 +160,7 @@ function FullPageTransition() {
|
||||
<div className="relative h-96 overflow-hidden rounded-lg">
|
||||
{pages.map((page, i) => (
|
||||
<Transition
|
||||
as="div"
|
||||
appear={false}
|
||||
key={page}
|
||||
show={activePage === i}
|
||||
|
||||
@@ -26,9 +26,10 @@ export default function App() {
|
||||
|
||||
<div className="bg-cool-gray-100 flex h-screen overflow-hidden">
|
||||
{/* Off-canvas menu for mobile */}
|
||||
<Transition show={mobileOpen} unmount={false} className="fixed inset-0 z-40 flex">
|
||||
<Transition as="div" show={mobileOpen} unmount={false} className="fixed inset-0 z-40 flex">
|
||||
{/* Off-canvas menu overlay, show/hide based on off-canvas menu state. */}
|
||||
<Transition.Child
|
||||
as="div"
|
||||
unmount={false}
|
||||
enter="transition-opacity ease-linear duration-300"
|
||||
enterFrom="opacity-0"
|
||||
@@ -49,6 +50,7 @@ export default function App() {
|
||||
|
||||
{/* Off-canvas menu, show/hide based on off-canvas menu state. */}
|
||||
<Transition.Child
|
||||
as="div"
|
||||
unmount={false}
|
||||
enter="transition ease-in-out duration-300 transform"
|
||||
enterFrom="-translate-x-full"
|
||||
|
||||
@@ -7,6 +7,7 @@ const TailwindToaster = () => {
|
||||
{(t) => (
|
||||
<Transition
|
||||
appear
|
||||
as="div"
|
||||
show={t.visible}
|
||||
className="flex transform rounded bg-white p-4 shadow-lg"
|
||||
enter="transition-all duration-500"
|
||||
|
||||
Reference in New Issue
Block a user