تنبيه (Alert)

صندوق رسالة يُعلم المستخدم بخبر جديد، أو يرشده، أو يؤكد له اكتمال مهمة، أو ينبّهه إلى أمر يحتاج إلى انتباهه، أو يبلغه بخطأ. يأتي بخمسة أنماط وثلاثة أنواع كما في ملف التصميم، ومعه زر إجراء وزر إغلاق عند الحاجة.

التثبيت

bash
npx @smicolon/fasla-ui@latest add alert

قبل إضافة أي مكوّن، شغّل الأمر init مرة واحدة في مشروعك. التفاصيل في صفحة التثبيت.

التنبيه مبني على مكوّن زر، فإضافته تضيف معه العنصر button من السجل.

معاينة

صيانة مجدولة الليلة
تتوقف لوحة التحكم عن العمل من الساعة 22:00 إلى 23:00.

الأنماط (variant)

تحدد الخاصية variant موضوع الرسالة: النمط default للأخبار، والنمط info للإرشاد، والنمط success لمهمة اكتملت، والنمط warning لأمر يحتاج إلى انتباه قريب، والنمط destructive للأخطاء. ولكل نمط أيقونة خاصة به إلى جانب لونه، فلا يعتمد المعنى على اللون وحده.

default
رسالة جديدة
لديك 12 رسالة غير مقروءة في صندوق الوارد.
info
صيانة مجدولة الليلة
تتوقف لوحة التحكم عن العمل من الساعة 22:00 إلى 23:00.
success
تم استلام الدفعة
دُفعت فاتورة شهر مارس بالكامل.
warning
مساحة التخزين تكاد تمتلئ
استخدمت 90% من مساحة خطتك. احذف بعض الملفات أو رقِّ خطتك.
destructive

الأنواع

تحدد الخاصية type مدى بروز التنبيه. النوع outline هو الافتراضي، وهو بطاقة لها حافة بعرض بكسل واحد بلون النمط. ويضع النوع soft فوق البطاقة طبقة من لون النمط بنسبة 10%. أما النوع solid فيملأ التنبيه باللون، وهو الأكثر بروزًا، فخصّصه للرسائل التي يجب ألا تفوت المستخدم. ويتبع زر الإجراء نوع التنبيه: زر بحدود في النوع outline، وزر مصمت في النوع soft، وزر ثانوي في النوع solid.

outline
تم استلام الدفعة
دُفعت فاتورة شهر مارس بالكامل.
soft
تم استلام الدفعة
دُفعت فاتورة شهر مارس بالكامل.
solid
تم استلام الدفعة
دُفعت فاتورة شهر مارس بالكامل.

الأيقونة والصورة الرمزية

يعرض كل نمط أيقونته الخاصة بحجم 16 بكسل، في منتصف السطر الأول من العنوان: علامة معلومات في دائرة للنمط default والنمط info، وعلامة صح للنمط success، ومثلث للنمط warning، وعلامة تعجب في دائرة للنمط destructive. ولتغيير الأيقونة مرّر أيقونة أخرى إلى الخاصية icon، أو مرّر صورة رمزية لرسالة من شخص، أو أعطِ الخاصية icon القيمة false لإخفائها. وتظهر الصورة الرمزية بحجم 32 بكسل في أعلى التنبيه.

أيقونة
مساحة التخزين تكاد تمتلئ
استخدمت 90% من مساحة خطتك. احذف بعض الملفات أو رقِّ خطتك.
صورة رمزية
ردّت ليلى حداد
«التصميم الجديد يعمل جيدًا. لننشره يوم الاثنين.»
بلا أيقونة
مساحة التخزين تكاد تمتلئ
استخدمت 90% من مساحة خطتك. احذف بعض الملفات أو رقِّ خطتك.

زر الإجراء وزر الإغلاق

المكوّن AlertAction زر بالحجم xs ارتفاعه 26 بكسل، ويقبل كل خصائص الزر، فتجعله الخاصية asChild رابطًا. أما المكوّن AlertClose فزر أيقونة شفاف فيه علامة × بحجم 16 بكسل. ويقع الزران في نهاية السطر الأول، زر الإجراء أولًا، وبينهما 12 بكسل.

الزران معًازر الإجراء فقطزر الإغلاق فقط

النص الطويل

يلتف العنوان والوصف داخل عمودهما. وتبقى الأيقونة في منتصف السطر الأول من العنوان، ويبقى زر الإجراء وزر الإغلاق في الأعلى. ويتسع المكوّن AlertDescription لأكثر من جملة: فقرات، أو قائمة، أو رابط.

تنتقل مساحة عملك إلى خطة الفوترة الجديدة في الأول من أبريل، وتتغير بعض الميزات

تنتقل المشاريع والأعضاء والملفات كما هي. وتبقى الروابط المشتركة التي أنشأتها قبل الانتقال صالحة 30 يومًا، ثم تطلب من زائريها تسجيل الدخول.

اقرأ القائمة الكاملة للتغييرات قبل الانتقال، وأخبر فريقك بالروابط التي يجب استبدالها.

الإغلاق

يُخفي الضغط على المكوّن AlertClose التنبيه، ويستدعي onOpenChange(false). ولإظهار التنبيه مرة أخرى، أو لتذكّر أن المستخدم أغلقه، تحكّم فيه بالخاصية open. ولإبقائه ظاهرًا، استدعِ event.preventDefault() داخل الدالة onClick في زر الإغلاق.

رسالة جديدة
لديك 12 رسالة غير مقروءة في صندوق الوارد.

إتاحة الوصول (accessibility)

  • يتبع الدور النمط. فللنمط destructive الدور role="alert"، لأن الخطأ يحتاج إلى انتباه فوري، فيقطع قارئ الشاشة ما يقرؤه ليعلنه. ولبقية الأنماط الدور role="status"، فيعلنه قارئ الشاشة بعد أن ينهي الجملة الحالية، لأن الخبر والإرشاد واكتمال المهمة والتحذير تحتمل هذا الانتظار. ولاختيار دور آخر استخدم الخاصية role، مثل role="note" لرسالة تظهر مع الصفحة منذ تحميلها.
  • يعلن قارئ الشاشة التنبيه عند ظهوره. لذلك اعرض التنبيه لحظة وقوع الحدث، واجعل العنوان والوصف قصيرين، لأن قارئ الشاشة يقرؤهما معًا.
  • الأيقونة للزينة، وهي مخفية عن التقنيات المساعدة، فالعنوان يوضح موضوع الرسالة.
  • اسم زر الإغلاق «إغلاق»، ويصبح بالإنجليزية حين تكون أقرب قيمة للسمة lang هي الإنجليزية. ولتوضيح ما يغلقه الزر، استخدم الخاصية label.
  • إذا أُغلق التنبيه والتركيز داخله، فانقل التركيز في الدالة onOpenChange إلى عنصر مناسب، مثل العنصر الذي أدّى إلى ظهور الرسالة. وإلا فسيبدأ التنقّل بلوحة المفاتيح من أول الصفحة.
  • في وضع التباين العالي على نظام ويندوز يحتفظ التنبيه بحدود بلون نص النظام، فتبقى حافته ظاهرة حين يُلغى لون خلفيته.
  • يتبع الاتجاه السمة dir في الصفحة، ولا توجد له خاصية. ففي الصفحات العربية تبدأ الأيقونة الصفّ وينتهي بزر الإغلاق، فينعكس ترتيب الأجزاء كله، ويظهر النص بالخط العربي على سطر أطول.

الخصائص (props)

Alert

الخاصيةالنوعالافتراضيالوصف
variant"default" | "info" | "success" | "warning" | "destructive""default"موضوع الرسالة. يحدد الأيقونة واللون والدور الافتراضي.
type"outline" | "solid" | "soft""outline"مدى بروز التنبيه: بطاقة بحافة، أو طبقة لون خفيفة، أو لون مصمت.
iconboolean | ReactNodetrueأيقونة النمط افتراضيًا. القيمة false تخفيها، وأي عنصر آخر، مثل أيقونة أو صورة رمزية، يحل محلها.
openbooleanلا يوجدتحدد ظهور التنبيه. استخدمها لتتحكم فيه بنفسك.
defaultOpenbooleantrueتحدد ظهور التنبيه في البداية، حين لا تتحكم فيه بنفسك.
onOpenChange(open: boolean) => voidلا يوجدتُستدعى بالقيمة false عند الضغط على زر الإغلاق.
rolestringلا يوجدالدور alert للنمط destructive، والدور status لبقية الأنماط، ما لم تمرّر دورًا آخر.
childrenReactNodeلا يوجدأجزاء التنبيه بهذا الترتيب: AlertTitle، AlertDescription، AlertAction، AlertClose.
classNamestringلا يوجدأصناف إضافية للتنبيه. ويملأ التنبيه عرض حاويته.

AlertTitle

الخاصيةالنوعالافتراضيالوصف
childrenReactNodeلا يوجدالعنوان: سطر واحد قصير يوضح ما حدث.
classNamestringلا يوجدأصناف إضافية للعنوان.

AlertDescription

الخاصيةالنوعالافتراضيالوصف
childrenReactNodeلا يوجدتفاصيل إضافية: جملة، أو فقرات، أو قائمة، أو رابط.
classNamestringلا يوجدأصناف إضافية للوصف.

AlertAction

الخاصيةالنوعالافتراضيالوصف
variant"default" | "outline" | "ghost" | "soft" | "link"لا يوجدنوع الزر. يتبع نوع التنبيه ما لم تحدده.
tone"primary" | "secondary" | "destructive"لا يوجداللون الدلالي للزر. يتبع نوع التنبيه ما لم تحدده.
size"xs" | "sm" | "md" | "lg""xs"حجم الزر.
asChildbooleanfalseتجعل العنصر الابن الوحيد زرَّ الإجراء بمظهر الزر نفسه، مثل رابط.
onClick(event) => voidلا يوجدتُستدعى عند الضغط على زر الإجراء.
childrenReactNodeلا يوجدالتسمية: فعل يصف ما سيحدث بعد الضغط.
classNamestringلا يوجدأصناف إضافية لزر الإجراء. ويقبل الزر كل خصائص مكوّن زر الأخرى أيضًا.

AlertClose

الخاصيةالنوعالافتراضيالوصف
labelstringلا يوجداسم الزر لقارئ الشاشة. وهو افتراضيًا «إغلاق» بلغة الصفحة.
onClick(event) => voidلا يوجدتُستدعى قبل إغلاق التنبيه. ولإبقائه ظاهرًا استدعِ event.preventDefault().
childrenReactNodeلا يوجدأيقونة أخرى بدل العلامة ×.
classNamestringلا يوجدأصناف إضافية لزر الإغلاق.

الاستخدام

رسالة

tsx
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"

export function MaintenanceNotice() {
  return (
    <Alert variant="info">
      <AlertTitle>صيانة مجدولة الليلة</AlertTitle>
      <AlertDescription>تتوقف لوحة التحكم عن العمل من الساعة 22:00 إلى 23:00.</AlertDescription>
    </Alert>
  )
}

مع زر إجراء وزر إغلاق

tsx
import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"

export function SaveError({ onRetry }: { onRetry: () => void }) {
  return (
    <Alert variant="destructive" type="soft">
      <AlertTitle>تعذّر حفظ التغييرات</AlertTitle>
      <AlertDescription>انقطع الاتصال بالخادم. تحقّق من اتصالك ثم حاول مرة أخرى.</AlertDescription>
      <AlertAction onClick={onRetry}>إعادة المحاولة</AlertAction>
      <AlertClose />
    </Alert>
  )
}

إظهار تنبيه مغلق مرة أخرى

tsx
"use client"

import { useState } from "react"
import { Alert, AlertClose, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function InboxNotice() {
  const [open, setOpen] = useState(true)

  if (!open) {
    return (
      <Button variant="outline" size="sm" onClick={() => setOpen(true)}>
        أظهره مرة أخرى
      </Button>
    )
  }

  return (
    <Alert open={open} onOpenChange={setOpen}>
      <AlertTitle>رسالة جديدة</AlertTitle>
      <AlertDescription>لديك 12 رسالة غير مقروءة في صندوق الوارد.</AlertDescription>
      <AlertClose />
    </Alert>
  )
}

رسالة من شخص

tsx
import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Avatar } from "@/components/ui/avatar"

export function ReplyNotice() {
  return (
    <Alert icon={<Avatar src="/avatars/layla.png" name="" radius="rounded" />}>
      <AlertTitle>ردّت ليلى حداد</AlertTitle>
      <AlertDescription>«التصميم الجديد يعمل جيدًا. لننشره يوم الاثنين.»</AlertDescription>
      <AlertAction asChild>
        <a href="/messages/42">ردّ</a>
      </AlertAction>
    </Alert>
  )
}