الانتقال إلى المحتوى

مربع حوار تنبيه

نافذة منبثقة توقف سير العملية عند القيام بأعمال مدمرة — مثل الحذف، أو تسجيل الخروج، أو التخلص من المحتوى — مع تصميم يعتمد على أسلوب «النيوبروتاليزم» الذي يركز على الحدود والظلال.

import {
  AlertDialog,
  AlertDialogAction,

يقطع مربع حوار التنبيه (alert dialog) سير العمل بنافذة تتطلّب اختيارًا صريحًا — النقر خارجها لا يغلقها. مبني على مكوّن Base UI Alert Dialog ومصمَّم وفق وصفة النيوبروتالية: حدود سميكة، وظلال حادة، وخط عريض.

استخدمه عندما:

  • تأكيدات مدمّرة — حذف حساب، إزالة عضو فريق، مسح مشروع.
  • عمليات لا رجعة فيها — نشر، إرسال، تقديم؛ أي شيء بلا تراجع.
  • حماية التغييرات غير المحفوظة — تنبيه قبل مغادرة نموذج معدَّل.

التثبيت

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
npx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/alert-dialog.json

ثبّت التبعيات التالية:

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

انسخ الشيفرة التالية والصقها في مشروعك.

components/ui/alert-dialog.tsx
"use client"

import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"

function AlertDialog({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}

function AlertDialogTrigger({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
  return (
    <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
  )
}

function AlertDialogPortal({
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
  return (
    <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
  )
}

function AlertDialogOverlay({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
  return (
    <AlertDialogPrimitive.Overlay
      data-slot="alert-dialog-overlay"
      className={cn(
        "fixed inset-0 z-50 bg-foreground/20 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogContent({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
  size?: "default" | "sm"
}) {
  return (
    <AlertDialogPortal>
      <AlertDialogOverlay />
      <AlertDialogPrimitive.Content
        data-slot="alert-dialog-content"
        data-size={size}
        className={cn(
          "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded border-2 bg-popover p-4 text-popover-foreground shadow-md duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
          className
        )}
        {...props}
      />
    </AlertDialogPortal>
  )
}

function AlertDialogHeader({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-header"
      className={cn(
        "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogFooter({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-footer"
      className={cn(
        "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t-2 bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogMedia({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="alert-dialog-media"
      className={cn(
        "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogTitle({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
  return (
    <AlertDialogPrimitive.Title
      data-slot="alert-dialog-title"
      className={cn(
        "font-head text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogDescription({
  className,
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
  return (
    <AlertDialogPrimitive.Description
      data-slot="alert-dialog-description"
      className={cn(
        "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
        className
      )}
      {...props}
    />
  )
}

function AlertDialogAction({
  className,
  variant = "default",
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
  Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
  return (
    <Button variant={variant} size={size} asChild>
      <AlertDialogPrimitive.Action
        data-slot="alert-dialog-action"
        className={cn(className)}
        {...props}
      />
    </Button>
  )
}

function AlertDialogCancel({
  className,
  variant = "outline",
  size = "default",
  ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
  Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
  return (
    <Button variant={variant} size={size} asChild>
      <AlertDialogPrimitive.Cancel
        data-slot="alert-dialog-cancel"
        className={cn(className)}
        {...props}
      />
    </Button>
  )
}

export {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogOverlay,
  AlertDialogPortal,
  AlertDialogTitle,
  AlertDialogTrigger,
}

حدّث مسارات الاستيراد لتطابق إعداد مشروعك.

الاستخدام

import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
<AlertDialog>
  <AlertDialogTrigger render={<Button variant="outline" />}>
    Show Dialog
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone. This will permanently delete your account
        from our servers.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction>Continue</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

التركيب

استخدم التركيب التالي لبناء AlertDialog:

AlertDialog
├── AlertDialogTrigger
└── AlertDialogContent
    ├── AlertDialogHeader
    │   ├── AlertDialogMedia
    │   ├── AlertDialogTitle
    │   └── AlertDialogDescription
    └── AlertDialogFooter
        ├── AlertDialogCancel
        └── AlertDialogAction

أمثلة

أساسي

مربّع حوار أساسي مع عنوان ووصف وزرَّي إلغاء ومتابعة.

import {
  AlertDialog,
  AlertDialogAction,

صغير

استخدم خاصية size="sm" لجعل مربّع الحوار أصغر.

import {
  AlertDialog,
  AlertDialogAction,

وسائط

استخدم مكوّن AlertDialogMedia لإضافة عنصر وسائط مثل أيقونة أو صورة إلى مربّع الحوار.

import { CircleFadingPlusIcon } from "lucide-react"

import {

صغير مع وسائط

استخدم خاصية size="sm" لجعل مربّع الحوار أصغر، ومكوّن AlertDialogMedia لإضافة عنصر وسائط مثل أيقونة أو صورة إلى مربّع الحوار.

import { BluetoothIcon } from "lucide-react"

import {

مدمّر

استخدم مكوّن AlertDialogAction لإضافة زر إجراء مدمّر إلى مربّع الحوار.

import { Trash2Icon } from "lucide-react"

import {

RTL

لتفعيل RTL في Neobrutalism، راجع دليل إعداد RTL.

"use client"

import { BluetoothIcon } from "lucide-react"

إمكانية الوصول

يتبع مربع حوار التنبيه نمط WAI-ARIA Alert Dialog: النافذة المنبثقة تُعرَض بـ role="alertdialog" مربوطة بالعنوان والوصف عبر aria-labelledby وaria-describedby، ويُحبس التركيز داخلها أثناء الفتح.

تفاعلات لوحة المفاتيح:

المفتاحالإجراء
Space / Enterفتح الحوار من المشغّل المركَّز
Tab / Shift + Tabتدوير التركيز بين العناصر القابلة للتركيز داخل الحوار
Escإغلاق الحوار وإعادة التركيز إلى المشغّل

مرجع API

size

استخدم خاصية size على مكوّن AlertDialogContent للتحكّم في حجم مربّع الحوار. يقبل القيم التالية:

PropTypeDefault
size"default" | "sm""default"

لمزيد من المعلومات حول المكوّنات الأخرى وخصائصها (props)، راجع وثائق Base UI.