feat(toast): support buttons

This commit is contained in:
Elian Doran 2025-12-07 20:39:27 +02:00
parent 3fc7067c59
commit 522f3ae0a1
No known key found for this signature in database
2 changed files with 20 additions and 2 deletions

View File

@ -9,6 +9,10 @@ export interface ToastOptions {
message: string; message: string;
timeout?: number; timeout?: number;
progress?: number; progress?: number;
buttons?: {
text: string;
onClick: (api: { dismissToast: () => void }) => void;
}[];
} }
export type ToastOptionsWithRequiredId = Omit<ToastOptions, "id"> & Required<Pick<ToastOptions, "id">>; export type ToastOptionsWithRequiredId = Omit<ToastOptions, "id"> & Required<Pick<ToastOptions, "id">>;

View File

@ -6,6 +6,7 @@ import { useEffect } from "preact/hooks";
import { removeToastFromStore, ToastOptionsWithRequiredId, toasts } from "../services/toast"; import { removeToastFromStore, ToastOptionsWithRequiredId, toasts } from "../services/toast";
import Icon from "./react/Icon"; import Icon from "./react/Icon";
import { RawHtmlBlock } from "./react/RawHtml"; import { RawHtmlBlock } from "./react/RawHtml";
import Button from "./react/Button";
export default function ToastContainer() { export default function ToastContainer() {
return ( return (
@ -15,7 +16,7 @@ export default function ToastContainer() {
) )
} }
function Toast({ id, title, timeout, progress, message, icon }: ToastOptionsWithRequiredId) { function Toast({ id, title, timeout, progress, message, icon, buttons }: ToastOptionsWithRequiredId) {
// Autohide. // Autohide.
useEffect(() => { useEffect(() => {
if (!timeout || timeout <= 0) return; if (!timeout || timeout <= 0) return;
@ -23,10 +24,14 @@ function Toast({ id, title, timeout, progress, message, icon }: ToastOptionsWith
return () => clearTimeout(timerId); return () => clearTimeout(timerId);
}, [ id, timeout ]); }, [ id, timeout ]);
function dismissToast() {
removeToastFromStore(id);
}
const closeButton = ( const closeButton = (
<button <button
type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close" type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"
onClick={() => removeToastFromStore(id)} onClick={dismissToast}
/> />
); );
const toastIcon = <Icon icon={icon.startsWith("bx ") ? icon : `bx bx-${icon}`} />; const toastIcon = <Icon icon={icon.startsWith("bx ") ? icon : `bx bx-${icon}`} />;
@ -52,6 +57,15 @@ function Toast({ id, title, timeout, progress, message, icon }: ToastOptionsWith
<RawHtmlBlock className="toast-body" html={message} /> <RawHtmlBlock className="toast-body" html={message} />
{!title && <div class="toast-header">{closeButton}</div>} {!title && <div class="toast-header">{closeButton}</div>}
{buttons && (
<div class="toast-buttons">
{buttons.map(({ text, onClick }) => (
<Button text={text} onClick={() => onClick({ dismissToast })} />
))}
</div>
)}
<div <div
class="toast-progress" class="toast-progress"
style={{ width: `${(progress ?? 0) * 100}%` }} style={{ width: `${(progress ?? 0) * 100}%` }}