From 3365a0427ee6e24ac96f49434f09d4255b7b52bf Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Mon, 10 Aug 2026 11:27:16 +0330 Subject: [PATCH] feat: implement useOverlayDismiss hook for consistent modal dismissal behavior --- .../components/WalletTransactionModal.tsx | 12 +- assets/admin/components/ui/ConfirmDialog.tsx | 7 +- .../admin/components/ui/InviteDoctorModal.tsx | 7 +- assets/admin/components/ui/Modal.tsx | 18 +-- .../admin/components/ui/PersianCalendar.tsx | 2 +- .../admin/components/ui/SearchableSelect.tsx | 4 + assets/admin/hooks/useOverlayDismiss.test.tsx | 129 ++++++++++++++++++ assets/admin/hooks/useOverlayDismiss.ts | 52 +++++++ assets/admin/pages/ClinicDetailPage.tsx | 7 +- assets/admin/pages/ClinicsPage.tsx | 9 +- assets/admin/pages/PreRegistrationsPage.tsx | 9 +- assets/admin/pages/UsersPage.tsx | 7 +- 12 files changed, 227 insertions(+), 36 deletions(-) create mode 100644 assets/admin/hooks/useOverlayDismiss.test.tsx create mode 100644 assets/admin/hooks/useOverlayDismiss.ts diff --git a/assets/admin/components/WalletTransactionModal.tsx b/assets/admin/components/WalletTransactionModal.tsx index 1730d0f0..ed6ab458 100644 --- a/assets/admin/components/WalletTransactionModal.tsx +++ b/assets/admin/components/WalletTransactionModal.tsx @@ -4,6 +4,7 @@ import { XMarkIcon } from '@heroicons/react/24/outline'; import SearchableSelect from './ui/SearchableSelect'; import { useBankAccounts, usePosDevices } from '../hooks/usePaymentMethods'; import { formatRial, formatNumber, tomanToRial, digitsOnly } from '../lib/utils'; +import { useOverlayDismiss } from '../hooks/useOverlayDismiss'; export type WalletMode = 'charge' | 'withdraw'; @@ -59,12 +60,7 @@ export default function WalletTransactionModal({ open, balanceRials, submitting, if (open) { setMode('charge'); setAmountToman(0); setMethodValue('cash'); setDescription(''); } }, [open]); - useEffect(() => { - if (!open) return; - const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; - document.addEventListener('keydown', onKey); - return () => document.removeEventListener('keydown', onKey); - }, [open, onClose]); + const dismiss = useOverlayDismiss(onClose, open); if (!open) return null; @@ -103,8 +99,8 @@ export default function WalletTransactionModal({ open, balanceRials, submitting, }; return createPortal( -
-
e.stopPropagation()}> +
+

{isWithdraw ? 'برداشت از کیف پول' : 'شارژ کیف پول'}

diff --git a/assets/admin/components/ui/Modal.tsx b/assets/admin/components/ui/Modal.tsx index f6b1acb1..6dc24c86 100644 --- a/assets/admin/components/ui/Modal.tsx +++ b/assets/admin/components/ui/Modal.tsx @@ -1,6 +1,7 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { createPortal } from 'react-dom'; import { XMarkIcon } from '@heroicons/react/24/outline'; +import { useOverlayDismiss } from '../../hooks/useOverlayDismiss'; type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; @@ -21,22 +22,13 @@ interface Props { } export default function Modal({ open, title, size = 'md', onClose, children, footer }: Props) { - useEffect(() => { - if (!open) return; - const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; - document.addEventListener('keydown', onKey); - return () => document.removeEventListener('keydown', onKey); - }, [open, onClose]); + const dismiss = useOverlayDismiss(onClose, open); if (!open) return null; return createPortal( -
-
e.stopPropagation()} - > +
+

{title}