Composable Mode
The original design of <Float>
is simple to use, but it can only be used with a reference element and a floating element, which may not be sufficient for more complex components. With the introduction of composable mode, <Float.Reference>
and <Float.Content>
components are provided, which allows the positioning of the reference element and floating element to be changed freely in the DOM.
INFO
Requires upgrading to v0.11+ to use the composable mode.
For example, here's an example without using the composable mode:
<Popover>
<Float>
<Popover.Button className="...">
Options
</Popover.Button>
<Popover.Panel className="...">
...
</Popover.Panel>
</Float>
</Popover>
And the same result with composable mode:
<Popover>
<Float composable>
<Float.Reference>
<Popover.Button className="...">
Options
</Popover.Button>
</Float.Reference>
<Float.Content>
<Popover.Panel className="...">
...
</Popover.Panel>
</Float.Content>
</Float>
</Popover>
With composable mode, the design of components can be more flexible:
<Popover>
<Float composable>
<div>
<Float.Reference>
<Popover.Button className="...">
Options
</Popover.Button>
</Float.Reference>
</div>
<div>
<div>...</div>
<div>
<div>
<Float.Content>
<Popover.Panel className="...">
...
</Popover.Panel>
</Float.Content>
</div>
</div>
</div>
</Float>
</Popover>
Dialog mode
The mode for positioning dialog around a reference element is designed by integrating the dialog box, which can be positioned around a reference element. This mode uses composable mode and off the transform
. If <Transition>
is used, you also need to enable transitionChild
on <Float.Content>
and use <Transition.Child>
from Headless UI to set the transition.
Note that in dialog, <Float.Content>
needs to be set to as={Fragment}
, and the first element inside it must be <Dialog.Panel>
.
Here is an example of integrating dialog:
import { Fragment, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import { Float } from '@headlessui-float/react'
export default function ExampleDialog() {
const [isOpen, setIsOpen] = useState(false)
function closeModal() {
setIsOpen(false)
}
function openModal() {
setIsOpen(true)
}
return (
<Float
dialog
placement="bottom-start"
offset={4}
>
<Float.Reference>
<button
type="button"
onClick={openModal}
className="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-opacity-80 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
Open dialog
</button>
</Float.Reference>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="duration-300 ease-out"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="duration-200 ease-in"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Float.Content
as={Fragment}
transitionChild
enter="duration-300 ease-out"
enterFrom="opacity-0 scale-50"
enterTo="opacity-100 scale-100"
leave="duration-200 ease-in"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-50"
tailwindcssOriginClass
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-[transform,opacity] select-none">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
Dialog Title
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Some text...
</p>
</div>
<div className="mt-4">
<button
type="button"
className="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
onClick={closeModal}
>
Close
</button>
</div>
</Dialog.Panel>
</Float.Content>
</div>
</div>
</Dialog>
</Transition>
</Float>
)
}