Не закрывайте диалоговое окно (модальное) при нажатии снаружи в React & HeadlessUI
Моя проблема проста ... Я использую компонент HeadlessUI Dialog для React в своем приложении, и когда я выхожу из модального окна, я хочу, чтобы он не закрывался. В документации есть диалог. Параметр наложения, который имеет дело с этим взаимодействием, но нет настроек для его отключения.
Какие-нибудь решения? Это ссылка на документы HeadlessUI компонента, который я использую: https://headlessui.dev/react/dialog
Может быть, вы знали, что такое "модальное окно блокировки предупреждений" для React ??
12 ответов
Вы можете удалить
onClose={closeModal}
из
Dialog
и вместо этого передайте
closeModal
в обработчик onClick любой кнопки в примере из предоставленной вами ссылки:
<Dialog
as="div"
className="fixed inset-0 z-10 overflow-y-auto" // removed onClose
>
Вместо этого передайте его кнопке закрытия внутри диалогового окна.
<button onClick={handleClose}>Close</button>
Добавьте возвращаемый пустой объект в функцию onClose диалогового интерфейса. вот мой код:
<Dialog
initialFocus={completeButtonRef}
as='div'
className='fixed inset-0 z-10 overflow-y-auto'
onClose={() => {}}
open={false}
>
добавлять
pointer-events: none
в Диалог.Оверлей.
Это можно сделать, добавив
pointer-events-none
учебный класс
https://github.com/tailwindlabs/headlessui/issues/621#issuecomment-867161749
Если вы также хотите отключить закрытие модального окна наEscape
нажатие клавиши:
Передайте функцию "noop" вonClose
свойство, как было предложено другими в этой теме.
<Dialog
...
onClose={() => {}}
// or onClose={noop} if you have lodash installed or a noop function helper
Если вы хотите, чтобы клавиша escape была отключена, выполните следующие действия :
Просто не используйDialog.Panel
внутри вашего модального/диалогового компонента.
<Dialog
...
onClose={onClose}>
<div> // <-- use a regular div as your PANEL
onClose={() => null }>
вы также можете вернуть null, это предотвращает внешний щелчок!
Мне нужно было только установить для onClose значение null. Добавление статического свойства предотвратило работу пользовательских событий закрытия.
<Dialog
static
onClose={() => null}>
</Dialog>
У меня была такая же проблема с https://headlessui.dev/react/dialog ReactJS и Typescript
Я исправил передачу нового свойства с именем onClose(value: boolean): void; где я могу обрабатывать состояние вне компонента onClose = {() => {setIsOpen(false)}} и в диалоговом окне вызывая опору onClose = {onClose}
Часть 1
<Transition appear show={showModal} as={Fragment}>
<Dialog
as="div"
className={clsx("fixed inset-0 z-50 overflow-y-auto",
className)}
onClose={onClose}
>
....
Часть 2
enter code here
<Modal
{...args}
showModal={isOpen}
onClose={() => {setIsOpen(false) }}
/>
Вам придется пройти.
Искатьstatic
на этой странице
Затем вам нужно будет обрабатывать открытие и закрытие.
Я хотел сохранить логику доступности, которую добавляет HeadlessUI (например, Esc для закрытия), а также добавить кнопки в том же переходе, но за пределами диалоговой области. Для этого я добавилhandleClose
логику как к прозрачному фону, так и к новой оболочке моего фактического содержимого диалога, а затем использовалevent.stopPropagation()
при любых щелчках внутри диалогового окна. В Vue с дополнительной логикой TransitionRoot мой код примерно выглядит так:
<TransitionRoot :show="isOpen">
<Dialog @close="handleClose">
<TransitionChild>
<div aria-hidden="true" class="fixed inset-0 bg-black/70"
@click="handleClose" />
<!-- ... a few <button @click="..." class="fixed z-20 ..."> items ... -->
</TransitionChild>
<div class="fixed inset-0 h-screen ..."
@click="handleClose">
<div class="flex items-center justify-center ...">
<TransitionChild as="div" class="rounded-lg bg-white ..."
@click.stop>
<!-- @click.stop is equivalent to onClick(e) => e.stopPropagation() -->
<!-- ... add dialog body in here ... -->
</TransitionChild>
</div>
</div>
</Dialog>
</TransitionRoot>
У меня сработало следующее, я буду использовать пример кода из предоставленной вами ссылки.
Пример кода:
import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
export default function MyModal() {
let [isOpen, setIsOpen] = useState(true)
function closeModal() {
setIsOpen(false)
}
function openModal() {
setIsOpen(true)
}
return (
<>
<div className="fixed inset-0 flex items-center justify-center">
<button
type="button"
onClick={openModal}
className="rounded-md bg-black bg-opacity-20 px-4 py-2 text-sm font-medium text-white hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
Open dialog
</button>
</div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
>
Payment successful
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Your payment has been successfully submitted. We’ve sent
you an email with all of the details of your order.
</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}
>
Got it, thanks!
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
</>
)
}
Мои изменения:
import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useState } from 'react'
export default function MyModal({ closeOnClickingOutside }) {
let [isOpen, setIsOpen] = useState(true)
const Overlay = closeOnClickingOutside ? 'div' : Dialog.Panel;
const Content = closeOnClickingOutside ? Dialog.Panel : 'div';
function closeModal() {
setIsOpen(false)
}
function openModal() {
setIsOpen(true)
}
return (
<>
<div className="fixed inset-0 flex items-center justify-center">
<button
type="button"
onClick={openModal}
className="rounded-md bg-black bg-opacity-20 px-4 py-2 text-sm font-medium text-white hover:bg-opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
>
Open dialog
</button>
</div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<Overlay className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Content className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
>
Payment successful
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Your payment has been successfully submitted. We’ve sent
you an email with all of the details of your order.
</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}
>
Got it, thanks!
</button>
</div>
</Content>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
</>
)
}
``
измените функцию handleClose на это:
const handleClose = (event, reason) => {
if (reason !== "backdropClick") {
setOpen(false)
}
}
В обновлении heandlessui 1.16 компонент Overlay был представлен только в Dialog.Panel, поэтому он выполнялся непосредственно в Dialog для интерпретации вызова onClose по щелчку мыши из Dialog.Panel.
Как оказалось:
<Dialog
as="div"
className="relative z-10"
onClose={() => {
if (event instanceof PointerEvent === false) {
closeModal()
}
}}
>