@zag-js/toast
Version:
Core logic for the toast widget implemented as a state machine
386 lines (383 loc) • 10.9 kB
text/typescript
import { PropTypes, CommonProperties, Direction, DirectionProperty, Required, RequiredBy } from '@zag-js/types';
import { Service, EventObject, Machine } from '@zag-js/core';
import { AnimationFrame } from '@zag-js/dom-query';
type Type = "success" | "error" | "loading" | "info" | "warning" | (string & {});
type ToastQueuePriority = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
type Placement = "top-start" | "top" | "top-end" | "bottom-start" | "bottom" | "bottom-end";
type Status = "visible" | "dismissing" | "unmounted";
interface StatusChangeDetails {
/**
* The status of the toast
*/
status: Status;
/**
* The reason for the status change
*/
src?: string | undefined;
}
interface ToastHeight {
/**
* The id of the toast
*/
id: string;
/**
* The height of the toast
*/
height: number;
}
interface ActionOptions {
/**
* The label of the action
*/
label: string;
/**
* The function to call when the action is clicked
*/
onClick: VoidFunction;
}
interface IntlTranslations {
closeTriggerLabel?: string | undefined;
}
interface Options<T = any> {
/**
* The title of the toast.
*/
title?: T | undefined;
/**
* The description of the toast.
*/
description?: T | undefined;
/**
* The duration the toast will be visible
*/
duration?: number | undefined;
/**
* The duration for the toast to kept alive before it is removed.
* Useful for exit transitions.
*/
removeDelay?: number | undefined;
/**
* The unique id of the toast
*/
id?: string | undefined;
/**
* The type of the toast
*/
type?: Type | undefined;
/**
* The priority of the toast (1 = highest, 8 = lowest)
*/
priority?: ToastQueuePriority | undefined;
/**
* Function called when the toast is visible
*/
onStatusChange?: ((details: StatusChangeDetails) => void) | undefined;
/**
* The action of the toast
*/
action?: ActionOptions | undefined;
/**
* Whether the toast is closable
*/
closable?: boolean | undefined;
/**
* The metadata of the toast
*/
meta?: Record<string, any> | undefined;
}
interface ToastProps<T = any> extends Omit<CommonProperties, "id">, Options<T> {
/**
* Specifies the localized strings that identifies the accessibility elements and their states
*/
translations?: IntlTranslations | undefined;
/**
* The direction of the toast
*/
dir?: Direction | undefined;
/**
* The index of the toast in the group
*/
index?: number | undefined;
/**
* The gap of the toast
*/
gap?: number | undefined;
/**
* The parent toast group service. Required when using toast as a child of a group.
*/
parent: Service<ToastGroupSchema>;
}
type ToastPropsWithDefault = "type" | "parent" | "duration" | "id" | "removeDelay";
type ToastSchema<O = any> = {
props: RequiredBy<ToastProps<O>, Extract<ToastPropsWithDefault, keyof ToastProps<O>>>;
context: {
mounted: boolean;
initialHeight: number;
remainingTime: number;
};
computed: {
height: number;
heightIndex: number;
heightBefore: number;
frontmost: boolean;
zIndex: number;
shouldPersist: boolean;
};
refs: {
closeTimerStartTime: number;
lastCloseStartTimerStartTime: number;
};
state: "visible" | "visible:updating" | "dismissing" | "unmounted" | "visible:persist";
tag: "visible" | "paused" | "updating";
guard: string;
action: string;
effect: string;
event: EventObject;
};
type ToastService = Service<ToastSchema>;
type ToastMachine = Machine<ToastSchema>;
interface ToastStoreProps {
/**
* The placement of the toast
* @default "bottom"
*/
placement?: Placement | undefined;
/**
* The maximum number of toasts. When the number of toasts exceeds this limit, the new toasts are queued.
* @default 24
*/
max?: number | undefined;
/**
* Whether to overlap the toasts
*/
overlap?: boolean | undefined;
/**
* The duration of the toast.
* By default, it is determined by the type of the toast.
*/
duration?: number | undefined;
/**
* The gap between the toasts
* @default 16
*/
gap?: number | undefined;
/**
* The offset from the safe environment edge of the viewport
* @default "1rem"
*/
offsets?: string | Record<"left" | "right" | "bottom" | "top", string> | undefined;
/**
* The hotkey that will move focus to the toast group
* @default '["altKey", "KeyT"]'
*/
hotkey?: string[] | undefined;
/**
* The duration for the toast to kept alive before it is removed.
* Useful for exit transitions.
*
* @default 200
*/
removeDelay?: number | undefined;
/**
* Whether to pause toast when the user leaves the browser tab
* @default false
*/
pauseOnPageIdle?: boolean | undefined;
}
interface ToastGroupProps extends DirectionProperty, CommonProperties {
/**
* The store of the toast
*/
store: ToastStore;
}
type ToastGroupSchema = {
state: "stack" | "overlap";
props: ToastGroupProps;
context: {
toasts: RequiredBy<ToastProps, Extract<ToastPropsWithDefault, keyof ToastProps>>[];
heights: ToastHeight[];
};
computed: {
count: number;
overlap: boolean;
placement: Placement;
};
refs: {
dismissableCleanup?: VoidFunction | undefined;
lastFocusedEl: HTMLElement | null;
isFocusWithin: boolean;
isPointerWithin: boolean;
ignoreMouseTimer: AnimationFrame;
};
guard: string;
effect: string;
action: string;
event: EventObject;
};
type ToastGroupService = Service<ToastGroupSchema>;
type ToastGroupMachine = Machine<ToastGroupSchema>;
interface ToastStore<V = any> {
/**
* The attributes of the toast store
*/
attrs: Required<ToastStoreProps>;
/**
* Subscribe to the toast store
*/
subscribe: (subscriber: (...args: any[]) => void) => VoidFunction;
/**
* Create a new toast with the given options
*/
create: (data: Options<V>) => string;
/**
* Update an existing toast with new properties
*/
update: (id: string, data: Partial<ToastProps<V>>) => string;
/**
* Remove a toast by its ID
*/
remove: (id?: string) => void;
/**
* Dismiss a toast by its ID. If no ID is provided, dismisses all toasts
*/
dismiss: (id?: string) => void;
/**
* Create an error toast with the given options
*/
error: (data: Options<V>) => void;
/**
* Create a success toast with the given options
*/
success: (data: Options<V>) => void;
/**
* Create an info toast with the given options
*/
info: (data: Options<V>) => void;
/**
* Create a warning toast with the given options
*/
warning: (data: Options<V>) => void;
/**
* Create a loading toast with the given options
*/
loading: (data: Options<V>) => void;
/**
* Get all currently visible toasts
*/
getVisibleToasts: () => Partial<ToastProps<V>>[];
/**
* Get the total number of toasts
*/
getCount: () => number;
/**
* Create a toast that tracks a promise's state
*/
promise: <T>(promise: Promise<T> | (() => Promise<T>), options: PromiseOptions<T, V>, shared?: Omit<Options<V>, "type">) => {
id: string | undefined;
unwrap: () => Promise<T>;
} | undefined;
/**
* Pause a toast's auto-dismiss timer. If no ID is provided, pauses all toasts
*/
pause: (id?: string) => void;
/**
* Resume a toast's auto-dismiss timer. If no ID is provided, resumes all toasts
*/
resume: (id?: string) => void;
/**
* Check if a toast with the given ID is currently visible
*/
isVisible: (id: string) => boolean;
/**
* Check if a toast with the given ID has been dismissed
*/
isDismissed: (id: string) => boolean;
/**
* Expand all toasts to show their full content (overlap mode)
*/
expand: VoidFunction;
/**
* Collapse all toasts to their compact state (overlap mode)
*/
collapse: VoidFunction;
}
type MaybeFunction<Value, Args> = Value | ((arg: Args) => Value);
interface PromiseOptions<V, O = any> {
loading: Omit<Options<O>, "type">;
success?: MaybeFunction<Omit<Options<O>, "type"> & {
type?: "success" | "warning";
}, V> | undefined;
error?: MaybeFunction<Omit<Options<O>, "type">, unknown> | undefined;
finally?: (() => void | Promise<void>) | undefined;
}
interface GroupProps {
/**
* The human-readable label for the toast region
*/
label?: string | undefined;
}
interface ToastGroupApi<T extends PropTypes = PropTypes, O = any> {
/**
* The total number of toasts
*/
getCount: () => number;
/**
* The toasts
*/
getToasts: () => ToastProps[];
/**
* Subscribe to the toast group
*/
subscribe: (callback: (toasts: Options<O>[]) => void) => VoidFunction;
getGroupProps: (options?: GroupProps) => T["element"];
}
interface ToastApi<T extends PropTypes = PropTypes, O = any> {
/**
* The title of the toast.
*/
title?: O | undefined;
/**
* The description of the toast.
*/
description?: O | undefined;
/**
* The type of the toast.
*/
type: Type;
/**
* The current placement of the toast.
*/
placement: Placement;
/**
* Whether the toast is visible.
*/
visible: boolean;
/**
* Whether the toast should render a close button
*/
closable: boolean;
/**
* Whether the toast is paused.
*/
paused: boolean;
/**
* Function to pause the toast (keeping it visible).
*/
pause: VoidFunction;
/**
* Function to resume the toast dismissing.
*/
resume: VoidFunction;
/**
* Function to instantly dismiss the toast.
*/
dismiss: VoidFunction;
getRootProps: () => T["element"];
getTitleProps: () => T["element"];
getGhostBeforeProps: () => T["element"];
getGhostAfterProps: () => T["element"];
getDescriptionProps: () => T["element"];
getCloseTriggerProps: () => T["button"];
getActionTriggerProps: () => T["button"];
}
export type { ActionOptions, GroupProps, IntlTranslations, Options, Placement, PromiseOptions, Status, StatusChangeDetails, ToastApi, ToastGroupApi, ToastGroupMachine, ToastGroupProps, ToastGroupSchema, ToastGroupService, ToastHeight, ToastMachine, ToastProps, ToastQueuePriority, ToastSchema, ToastService, ToastStore, ToastStoreProps, Type };