diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 4b7fe6d..f98ab27 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add ability to render multiple `` components at once (without nesting them) ([#3242](https://github.com/tailwindlabs/headlessui/pull/3242)) - Add CSS based transitions using `data-*` attributes ([#3273](https://github.com/tailwindlabs/headlessui/pull/3273), [#3285](https://github.com/tailwindlabs/headlessui/pull/3285)) +- Add a `transition` prop to `` component ([#3307](https://github.com/tailwindlabs/headlessui/pull/3307)) +- Re-introduce `` component ([#3307](https://github.com/tailwindlabs/headlessui/pull/3307)) ### Fixed @@ -21,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `useId` instead of React internals (for React 19 compatibility) ([#3254](https://github.com/tailwindlabs/headlessui/pull/3254)) - Ensure `ComboboxInput` does not sync with current value while typing ([#3259](https://github.com/tailwindlabs/headlessui/pull/3259)) - Cancel outside click behavior on touch devices when scrolling ([#3266](https://github.com/tailwindlabs/headlessui/pull/3266)) -- Correctly apply conditional classses when using `` and `` components ([#3303](https://github.com/tailwindlabs/headlessui/pull/3303)) +- Correctly apply conditional classes when using `` and `` components ([#3303](https://github.com/tailwindlabs/headlessui/pull/3303)) - Improve UX by freezing `ComboboxOptions` while closing ([#3304](https://github.com/tailwindlabs/headlessui/pull/3304)) ### Changed diff --git a/packages/@headlessui-react/src/components/dialog/dialog.tsx b/packages/@headlessui-react/src/components/dialog/dialog.tsx index 7c2f4dd..21b4d09 100644 --- a/packages/@headlessui-react/src/components/dialog/dialog.tsx +++ b/packages/@headlessui-react/src/components/dialog/dialog.tsx @@ -2,6 +2,7 @@ // WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/ import React, { + Fragment, createContext, createRef, useContext, @@ -49,6 +50,9 @@ import { } from '../description/description' import { FocusTrap, FocusTrapFeatures } from '../focus-trap/focus-trap' import { Portal, PortalGroup, useNestedPortals } from '../portal/portal' +import { Transition, TransitionChild } from '../transition/transition' + +let WithTransitionWrapper = createContext(false) enum DialogStates { Open, @@ -126,6 +130,7 @@ export type DialogProps = role?: 'dialog' | 'alertdialog' autoFocus?: boolean __demoMode?: boolean + transition?: boolean } > @@ -141,6 +146,7 @@ function DialogFn( initialFocus, role = 'dialog', autoFocus = true, + transition = false, __demoMode = false, ...theirProps } = props @@ -337,6 +343,17 @@ function DialogFn( } } + if (transition) { + let { transition: _transition, open, ...rest } = props + return ( + + + + + + ) + } + return ( <> @@ -416,13 +433,62 @@ function PanelFn( onClick: handleClick, } - return render({ - ourProps, - theirProps, - slot, - defaultTag: DEFAULT_PANEL_TAG, - name: 'Dialog.Panel', - }) + let Wrapper = useContext(WithTransitionWrapper) ? TransitionChild : Fragment + + return ( + + + {render({ + ourProps, + theirProps, + slot, + defaultTag: DEFAULT_PANEL_TAG, + name: 'Dialog.Panel', + })} + + + ) +} + +// --- + +let DEFAULT_BACKDROP_TAG = 'div' as const +type BackdropRenderPropArg = { + open: boolean +} + +export type DialogBackdropProps = Props< + TTag, + BackdropRenderPropArg +> + +function BackdropFn( + props: DialogBackdropProps, + ref: Ref +) { + let theirProps = props + let [{ dialogState }] = useDialogContext('Dialog.Backdrop') + + let slot = useMemo( + () => ({ open: dialogState === DialogStates.Open }) satisfies BackdropRenderPropArg, + [dialogState] + ) + + let ourProps = { ref } + + let Wrapper = useContext(WithTransitionWrapper) ? TransitionChild : Fragment + + return ( + + {render({ + ourProps, + theirProps, + slot, + defaultTag: DEFAULT_BACKDROP_TAG, + name: 'Dialog.Backdrop', + })} + + ) } // --- @@ -482,6 +548,12 @@ export interface _internal_ComponentDialogPanel extends HasDisplayName { ): JSX.Element } +export interface _internal_ComponentDialogBackdrop extends HasDisplayName { + ( + props: DialogBackdropProps & RefProp + ): JSX.Element +} + export interface _internal_ComponentDialogTitle extends HasDisplayName { ( props: DialogTitleProps & RefProp @@ -492,6 +564,7 @@ export interface _internal_ComponentDialogDescription extends _internal_Componen let DialogRoot = forwardRefWithAs(DialogFn) as _internal_ComponentDialog export let DialogPanel = forwardRefWithAs(PanelFn) as _internal_ComponentDialogPanel +export let DialogBackdrop = forwardRefWithAs(BackdropFn) as _internal_ComponentDialogBackdrop export let DialogTitle = forwardRefWithAs(TitleFn) as _internal_ComponentDialogTitle /** @deprecated use `` instead of `` */ export let DialogDescription = Description as _internal_ComponentDialogDescription diff --git a/packages/@headlessui-react/src/index.test.ts b/packages/@headlessui-react/src/index.test.ts index ea86956..bcce250 100644 --- a/packages/@headlessui-react/src/index.test.ts +++ b/packages/@headlessui-react/src/index.test.ts @@ -24,6 +24,7 @@ it('should expose the correct components', () => { 'Description', 'Dialog', + 'DialogBackdrop', 'DialogDescription', 'DialogPanel', 'DialogTitle', diff --git a/playgrounds/react/pages/dialog/dialog-built-in-transition.tsx b/playgrounds/react/pages/dialog/dialog-built-in-transition.tsx new file mode 100644 index 0000000..a5de69e --- /dev/null +++ b/playgrounds/react/pages/dialog/dialog-built-in-transition.tsx @@ -0,0 +1,47 @@ +import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react' +import { useState } from 'react' +import { Button } from '../../components/button' +import { classNames } from '../../utils/class-names' + +export default function Home() { + let [isOpen, setIsOpen] = useState(false) + let [transition, setTransition] = useState(true) + + return ( + <> +
+ + +
+ + setIsOpen(false)} + className="relative z-50" + > + +
+ +

Dialog

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar, nunc nec + vehicula fermentum, nunc sapien tristique ipsum, nec facilisis dolor sapien non dui. + Nullam vel sapien ultrices, lacinia felis sit amet, fermentum odio. Nullam vel sapien + ultrices, lacinia felis sit amet, fermentum odio. +

+ +
+
+
+ + ) +}