إشعار
إشعار قصير بأن شيئًا حدث. يظهر في زاوية ويغادر وحده، وفيه دائمًا زر إغلاق، لأن السحب بالإصبع حركة جرّ.
client - الحجم لم يُقَس بعد
إشعار
client - الحجم لم يُقَس بعدإشعار قصير بأن شيئًا حدث. يظهر في زاوية ويغادر وحده، وفيه دائمًا زر إغلاق، لأن السحب بالإصبع حركة جرّ.
جرّبه
tone
duration
priority
الشيفرة
toast.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
import type { ToastDesignProps } from 'topclassui/toast'
/** The docs stage: the Try it controls set the options of the toast the press adds. */
export default function Demo(props: ToastDesignProps = {}) {
return (
<Button
variant="outline"
onClick={() => { toast.add({ ...props, title: 'Project created', description: 'It is ready for its first deploy.' }) }}
>
Show a toast
</Button>
)
}
TopClassProvider يركّب منطقة العرض الوحيدة، فلا شيء عليك تركيبه؛ وtoast.add يعمل من أي وحدة في المتصفح، داخل React أو خارجه.
كل الحالات، بدون تمرير الفأرة
11 حالةأو استورده من الحزمة
topclassui/toast
import { Toast } from 'topclassui/toast'ثبّت مرة واحدة:npm i topclassui@beta zod
أمثلة
toastأربع دوال عادية:addوupdateوcloseوpromise. الجدول يسرد خياراتtoast.addلا خصائص مكوّن؛ وتأخذ أيضًا عنوانًا إلزاميًا ووصفًا وإجراءً واحدًا.durationمحصورة بين 0 و600000 ملّي ثانية، والقيمة 0 تُبقيه حتى يُغلق. إن لم تُمرَّر طُبّقت مدة المزوّد: 5000 ما لم يحددTopClassProviderغيرها.- الإجراء لا يغلق الإشعار: استدعِ
toast.close(id)داخله، بالمعرّف الذي أعادهadd. وtoast.close()بلا معرّف يغلقها كلها. toast.promiseيعرض إشعارًا واحدًا: مؤشر تحميل، ثم أيقونة النجاح أو الخطأ ونصه. ويعيد الوعد نفسه، بما في ذلك رفضه، فالتقطه.- الموضع والحد الأقصى والمدة تُضبط مرة واحدة على المزوّد. أحدث إشعار هو الأقرب إلى الحافة، وتظهر ثلاثة معًا؛ ويظهر التالي عندما يُغلق أحدها.
with-description
with-description.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** The drawn sample: a title and a line under it. */
export default function Demo() {
return (
<Button
variant="outline"
onClick={() => { toast.add({ tone: 'success', title: 'Invitation sent', description: 'They get an email within the minute.' }) }}
>
Invite
</Button>
)
}
basic
basic.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** One press, one toast. The viewport is already there: TopClassProvider mounts it. */
export default function Demo() {
return (
<Button variant="outline" onClick={() => { toast.add({ title: 'Project created' }) }}>
Create project
</Button>
)
}
tones
tones.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** The six tones. Four carry an icon, so the meaning never rests on colour alone; neutral and primary carry none. */
export default function Demo() {
return (
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={() => { toast.add({ title: 'Draft saved' }) }}>Neutral</Button>
<Button variant="outline" onClick={() => { toast.add({ tone: 'primary', title: 'New version available' }) }}>Primary</Button>
<Button variant="outline" onClick={() => { toast.add({ tone: 'success', title: 'Invitation sent' }) }}>Success</Button>
<Button variant="outline" onClick={() => { toast.add({ tone: 'warning', title: 'Storage almost full' }) }}>Warning</Button>
<Button variant="outline" onClick={() => { toast.add({ tone: 'danger', title: 'Could not save' }) }}>Danger</Button>
<Button variant="outline" onClick={() => { toast.add({ tone: 'info', title: 'Maintenance tonight' }) }}>Info</Button>
</div>
)
}
with-action
with-action.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** An action under the text. It does not close the toast by itself, so Undo closes it by the id add() returned. */
export default function Demo() {
const archive = () => {
const id = toast.add({
title: 'Project archived',
action: { label: 'Undo', onClick: () => { toast.close(id) } },
})
}
return <Button variant="outline" onClick={archive}>Archive</Button>
}
promise
promise.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** Stands in for a request: it settles after a second, one way or the other. */
const upload = (succeeds: boolean) => new Promise<number>((resolve, reject) => {
setTimeout(() => { if (succeeds) resolve(3); else reject(new Error('The server did not answer.')) }, 1000)
})
/** One toast for the whole request: a spinner, then the success or the error icon and text in its place.
* promise() hands the rejection back, so the caller catches it. */
export default function Demo() {
const send = (succeeds: boolean) => {
toast.promise(upload(succeeds), {
loading: 'Uploading',
success: (count) => ({ title: 'Uploaded', description: `${String(count)} files are in the project.` }),
error: (error) => ({ title: 'Upload failed', description: error instanceof Error ? error.message : 'Try again.' }),
}).catch(() => undefined)
}
return (
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={() => { send(true) }}>Upload</Button>
<Button variant="outline" onClick={() => { send(false) }}>Upload and fail</Button>
</div>
)
}
sticky
sticky.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** duration 0: it stays until the x, a swipe or Escape closes it. */
export default function Demo() {
return (
<Button
variant="outline"
onClick={() => { toast.add({ tone: 'warning', title: 'You are offline', description: 'Changes are kept here until you reconnect.', duration: 0 }) }}
>
Go offline
</Button>
)
}
high-priority
high-priority.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** priority high: a screen reader announces it at once, as an alert, instead of after what it is reading. */
export default function Demo() {
return (
<Button
variant="outline"
onClick={() => { toast.add({ tone: 'danger', priority: 'high', title: 'Payment failed', description: 'Your card was declined.' }) }}
>
Pay now
</Button>
)
}
many
many.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
/** Twenty at once. Three show, the provider's limit; the next one appears each time one closes. */
export default function Demo() {
const flood = () => {
for (let n = 1; n <= 20; n++) toast.add({ title: `Message ${String(n)}`, duration: 0 })
}
return (
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={flood}>Twenty messages</Button>
<Button variant="outline" onClick={() => { toast.close() }}>Close all</Button>
</div>
)
}
over-dialog
over-dialog.tsx
'use client'
import { Button } from 'topclassui/button'
import {
DialogBody, DialogClose, DialogFooter, DialogHeader, DialogPopup, DialogRoot, DialogTitle, DialogTrigger,
} from 'topclassui/dialog'
import { toast } from 'topclassui/toast'
/** A toast shown from inside a modal dialog. It is painted above the scrim, on the toast layer. */
export default function Demo() {
return (
<DialogRoot>
<DialogTrigger render={<Button variant="outline">Open settings</Button>} />
<DialogPopup>
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
</DialogHeader>
<DialogBody>Saving keeps this dialog open.</DialogBody>
<DialogFooter>
<DialogClose render={<Button variant="outline">Close</Button>} />
<Button onClick={() => { toast.add({ tone: 'success', title: 'Settings saved', duration: 0 }) }}>Save</Button>
</DialogFooter>
</DialogPopup>
</DialogRoot>
)
}
long-content
long-content.tsx
'use client'
import { Button } from 'topclassui/button'
import { toast } from 'topclassui/toast'
const WORD = 'a'.repeat(300)
const PARAGRAPH = 'Every file of the project is checked again before it is published, and the ones that changed are listed here. '.repeat(4)
/** Hostile content: a 300-letter word as the title and as the text, a long action label, and three long toasts. */
export default function Demo() {
const words = () => {
toast.add({ tone: 'info', title: WORD, description: WORD, action: { label: `Open the full report of ${WORD}`, onClick: () => undefined } })
}
const tall = () => {
for (const title of ['First check', 'Second check', 'Third check']) {
toast.add({ tone: 'warning', title, description: PARAGRAPH, duration: 0 })
}
}
return (
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={words}>One long word</Button>
<Button variant="outline" onClick={tall}>Three tall toasts</Button>
</div>
)
}
بالعربية، من اليمين إلى اليسار
rtl.tsx
'use client'
import { Button } from 'topclassui/button'
import { TopClassProvider } from 'topclassui/provider'
import { toast } from 'topclassui/toast'
/** Arabic inside TopClassProvider. The one viewport is the outermost provider's: on an Arabic page the toast sits
* at the bottom left, and from an Arabic island on an English page it shows in the page's own corner and language. */
export default function Demo() {
return (
<TopClassProvider direction="rtl" locale="ar">
<div dir="rtl" lang="ar">
<Button
variant="outline"
onClick={() => { toast.add({ tone: 'success', title: 'تم إرسال الدعوة', description: 'تصلهم رسالة خلال دقيقة.' }) }}
>
دعوة
</Button>
</div>
</TopClassProvider>
)
}
الخصائص
لوحة المفاتيح وإمكانية الوصول
- منطقة العرض منطقة حية مهذّبة يسمّيها
labels.toast.region. أماpriority="high"فيُقرأ فورًا كتنبيه: احتفظ به لما لا يحتمل الانتظار. - ينتهي كل إشعار بزر إغلاق لا يُزال أبدًا: السحب حركة جرّ، والجرّ يحتاج إلى طريقة ثانية (WCAG 2.5.7).
- تتوقف المؤقتات ما دام المؤشر فوق الإشعارات، وما دام أحدها يحمل التركيز، وما دامت النافذة في الخلفية. وفوق حوار نمطي يبقى Tab داخل الحوار ويصل F6 إلى الإشعار.
من اليمين إلى اليسار
الموضع الافتراضي bottom-end هو أسفل اليمين في الإنجليزية وأسفل اليسار في العربية، والسحب الجانبي يتبع الحافة التي يجلس عندها الإشعار. والجزيرة العربية داخل صفحة إنجليزية تعرض إشعاراتها في زاوية الصفحة وبلغتها.
الخادم والمتصفح
مكوّن متصفح: يُعرض على الخادم ثم يُرطَّب في المتصفح.
الملفات: index.tsx, client.tsx.
على الشارة: client - الحجم لم يُقَس بعد.