diff --git a/.claude/prompt/fix-modal-portal-positioning.md b/.claude/prompt/fix-modal-portal-positioning.md new file mode 100644 index 00000000..8b71311f --- /dev/null +++ b/.claude/prompt/fix-modal-portal-positioning.md @@ -0,0 +1,133 @@ +# رفع افتادن مودالها به پایین صفحه (portal برای همه مودالها) + +## پروژه + +`clinicpro` (پنل ادمین React — `assets/admin/`) + +## زمینه + +بعضی مودالها هنگام باز شدن وسط صفحه نمیافتند و «خیلی پایین» یا آفست ظاهر میشوند. نمونهاش مودال کراپ عکس در صفحهی دکتر بود که با انتقال به `createPortal(document.body)` حل شد. + +## مشکل / هدف + +کلاس مشترک `.overlay` از `position: fixed; inset: 0` استفاده میکند (تعریف در `assets/admin/styles.css:613`). طبق مشخصات CSS، وقتی یک عنصر `position: fixed` داخل ancestorـی رندر شود که یکی از این خواص را دارد، دیگر نسبت به viewport نیست بلکه نسبت به همان ancestor محاسبه میشود: + +- `transform` (حتی `transform: translateZ(0)` یا انیمیشنهای `pop`/`slidein` که transform دارند) +- `filter` / `backdrop-filter` +- `perspective`, `will-change: transform`, `contain: paint/layout` + +در نتیجه هر مودالی که `.overlay` را **inline در عمق درخت کامپوننت** (داخل کارتها/کانتینرهای transformدار) رندر کند، پایین/آفست میافتد. + +**راهحل استاندارد پروژه:** رندر مودال از طریق `createPortal(..., document.body)` تا از زنجیرهی ancestorها خارج شود. کامپوننتهای `components/ui/Modal.tsx` و `components/ImageCropModal.tsx` همین کار را میکنند و درستاند — الگوی مرجع همینهاست. + +هدف: **همهی مودالهای `.overlay` که هنوز portal ندارند** به portal منتقل شوند تا این مشکل در «هر جایی که مودال باز میشود» حل شود. + +## فایلهای مرتبط + +| فایل | وضعیت | نقش | +|------|------|-----| +| `assets/admin/components/ui/Modal.tsx` | ✅ portal دارد | الگوی مرجع — تغییر نده | +| `assets/admin/components/ImageCropModal.tsx` | ✅ portal دارد | الگوی مرجع — تغییر نده | +| `assets/admin/components/ui/ConfirmDialog.tsx` | ❌ بدون portal | پرکاربردترین؛ اولویت اول | +| `assets/admin/components/ui/InviteDoctorModal.tsx` | ❌ بدون portal | مودال دعوت دکتر | +| `assets/admin/components/layout/Topbar.tsx` | ❌ بدون portal | overlay (خط ~75) | +| `assets/admin/pages/UsersPage.tsx` | ❌ بدون portal | مودال inline (خط ~104) | +| `assets/admin/pages/ClinicsPage.tsx` | ❌ بدون portal | مودال افزودن (خط ~247) | +| `assets/admin/pages/PreRegistrationsPage.tsx` | ❌ بدون portal | مودال رد کردن (خط ~207) | +| `assets/admin/pages/ClinicDetailPage.tsx` | ❌ یک مورد بدون portal (خط ~354) | بقیهی overlayهایش portal دارند | +| `assets/admin/styles.css` | مرجع | تعریف `.overlay` (خط 613) — تغییر نده | + +## وضعیت فعلی + +نمونهی `ConfirmDialog.tsx` (بدون portal — همین الگو در بقیه هم هست): + +```tsx +import React from 'react'; +import { XMarkIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'; + +export default function ConfirmDialog({ open, /* ... */ onConfirm, onCancel }: Props) { + if (!open) return null; + + return ( +