mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat: toast api improved
This commit is contained in:
parent
fcd6c1552f
commit
bc0112f3b4
6
.changeset/brave-masks-rescue.md
Normal file
6
.changeset/brave-masks-rescue.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@heroui/toast": patch
|
||||
"@heroui/theme": patch
|
||||
---
|
||||
|
||||
Toast API changes, pleacements changed and severity property added
|
||||
@ -303,9 +303,15 @@ Toast has the following slots:
|
||||
},
|
||||
{
|
||||
attribute: "placement",
|
||||
type: "right-bottom" | "left-bottom" | "center-bottom" | "right-top" | "left-top" | "center-top",
|
||||
type: "bottom-right" | "bottom-left" | "bottom-center" | "top-right" | "top-left" | "top-center",
|
||||
description: "The placement of the toast.",
|
||||
default: "right-bottom"
|
||||
default: "bottom-right"
|
||||
},
|
||||
{
|
||||
attribute: "severity",
|
||||
type: "default | primary | secondary | success | warning | danger",
|
||||
description: "The severity of the toast. This changes the icon of the toast without having to change the color.",
|
||||
default: "default"
|
||||
},
|
||||
{
|
||||
attribute: "disableAnimation",
|
||||
|
||||
@ -2,19 +2,13 @@ import {ToastOptions, ToastQueue, useToastQueue} from "@react-stately/toast";
|
||||
import {useProviderContext} from "@heroui/system";
|
||||
|
||||
import {ToastRegion} from "./toast-region";
|
||||
import {ToastProps} from "./use-toast";
|
||||
import {ToastProps, ToastPlacement} from "./use-toast";
|
||||
|
||||
let globalToastQueue: ToastQueue<ToastProps> | null = null;
|
||||
|
||||
interface ToastProviderProps {
|
||||
maxVisibleToasts?: number;
|
||||
placement?:
|
||||
| "right-bottom"
|
||||
| "left-bottom"
|
||||
| "center-bottom"
|
||||
| "right-top"
|
||||
| "left-top"
|
||||
| "center-top";
|
||||
placement?: ToastPlacement;
|
||||
disableAnimation?: boolean;
|
||||
toastProps?: ToastProps;
|
||||
toastOffset?: number;
|
||||
@ -32,7 +26,7 @@ export const getToastQueue = () => {
|
||||
};
|
||||
|
||||
export const ToastProvider = ({
|
||||
placement = "right-bottom",
|
||||
placement = "bottom-right",
|
||||
disableAnimation: disableAnimationProp = false,
|
||||
maxVisibleToasts = 3,
|
||||
toastOffset = 0,
|
||||
|
||||
@ -6,17 +6,11 @@ import {mergeProps} from "@react-aria/utils";
|
||||
import {toastRegion, ToastRegionVariantProps} from "@heroui/theme";
|
||||
|
||||
import Toast from "./toast";
|
||||
import {ToastProps} from "./use-toast";
|
||||
import {ToastProps, ToastPlacement} from "./use-toast";
|
||||
|
||||
interface ToastRegionProps<T> extends AriaToastRegionProps, ToastRegionVariantProps {
|
||||
toastQueue: ToastState<T>;
|
||||
placement?:
|
||||
| "right-bottom"
|
||||
| "left-bottom"
|
||||
| "center-bottom"
|
||||
| "right-top"
|
||||
| "left-top"
|
||||
| "center-top";
|
||||
placement?: ToastPlacement;
|
||||
maxVisibleToasts: number;
|
||||
toastOffset?: number;
|
||||
toastProps?: ToastProps;
|
||||
|
||||
@ -18,6 +18,7 @@ const loadFeatures = () => import("framer-motion").then((res) => res.domMax);
|
||||
export interface ToastProps extends UseToastProps {}
|
||||
|
||||
const iconMap = {
|
||||
default: InfoFilledIcon,
|
||||
primary: InfoFilledIcon,
|
||||
secondary: InfoFilledIcon,
|
||||
success: SuccessIcon,
|
||||
@ -27,6 +28,7 @@ const iconMap = {
|
||||
|
||||
const Toast = forwardRef<"div", ToastProps>((props, ref) => {
|
||||
const {
|
||||
severity,
|
||||
Component,
|
||||
icon,
|
||||
loadingIcon,
|
||||
@ -56,7 +58,7 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
|
||||
});
|
||||
|
||||
const customIcon = icon && isValidElement(icon) ? cloneElement(icon, getIconProps()) : null;
|
||||
const IconComponent = iconMap[color] || iconMap.primary;
|
||||
const IconComponent = severity ? iconMap[severity] : iconMap[color] || iconMap.default;
|
||||
const customLoadingIcon =
|
||||
loadingIcon && isValidElement(loadingIcon)
|
||||
? cloneElement(loadingIcon, getLoadingIconProps())
|
||||
|
||||
@ -12,6 +12,14 @@ import {MotionProps} from "framer-motion";
|
||||
import {useHover} from "@react-aria/interactions";
|
||||
import {useIsMobile} from "@heroui/use-is-mobile";
|
||||
|
||||
export type ToastPlacement =
|
||||
| "bottom-right"
|
||||
| "bottom-left"
|
||||
| "bottom-center"
|
||||
| "top-right"
|
||||
| "top-left"
|
||||
| "top-center";
|
||||
|
||||
export interface ToastProps extends ToastVariantProps {
|
||||
/**
|
||||
* Ref to the DOM node.
|
||||
@ -93,6 +101,11 @@ export interface ToastProps extends ToastVariantProps {
|
||||
* should apply styles to indicate timeout progress
|
||||
*/
|
||||
shouldShowTimeoutProgess?: boolean;
|
||||
/**
|
||||
* The severity of the toast. This changes the icon without having to change the color.
|
||||
* @default "default"
|
||||
*/
|
||||
severity?: "default" | "primary" | "secondary" | "success" | "warning" | "danger";
|
||||
}
|
||||
|
||||
interface Props<T> extends Omit<HTMLHeroUIProps<"div">, "title">, ToastProps {
|
||||
@ -104,13 +117,7 @@ interface Props<T> extends Omit<HTMLHeroUIProps<"div">, "title">, ToastProps {
|
||||
setHeights: (val: number[]) => void;
|
||||
disableAnimation?: boolean;
|
||||
isRegionExpanded: boolean;
|
||||
placement?:
|
||||
| "right-bottom"
|
||||
| "left-bottom"
|
||||
| "center-bottom"
|
||||
| "right-top"
|
||||
| "left-top"
|
||||
| "center-top";
|
||||
placement?: ToastPlacement;
|
||||
toastOffset?: number;
|
||||
}
|
||||
|
||||
@ -135,7 +142,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
endContent,
|
||||
closeIcon,
|
||||
hideIcon = false,
|
||||
placement: placementProp = "right-bottom",
|
||||
placement: placementProp = "bottom-right",
|
||||
isRegionExpanded,
|
||||
hideCloseButton = false,
|
||||
state,
|
||||
@ -150,6 +157,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
shouldShowTimeoutProgess = false,
|
||||
icon,
|
||||
onClose,
|
||||
severity,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
@ -166,9 +174,9 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
|
||||
if (isMobile) {
|
||||
if (placementProp.includes("top")) {
|
||||
placement = "center-top";
|
||||
placement = "top-center";
|
||||
} else {
|
||||
placement = "center-bottom";
|
||||
placement = "bottom-center";
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,8 +325,8 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
const shouldCloseToast = (offsetX: number, offsetY: number) => {
|
||||
const isRight = placement.includes("right");
|
||||
const isLeft = placement.includes("left");
|
||||
const isCenterTop = placement === "center-top";
|
||||
const isCenterBottom = placement === "center-bottom";
|
||||
const isCenterTop = placement === "top-center";
|
||||
const isCenterBottom = placement === "bottom-center";
|
||||
|
||||
if (
|
||||
(isRight && offsetX >= SWIPE_THRESHOLD_X) ||
|
||||
@ -362,7 +370,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
|
||||
let opacityValue: undefined | number = undefined;
|
||||
|
||||
if ((drag && placement === "center-bottom") || placement === "center-top") {
|
||||
if ((drag && placement === "bottom-center") || placement === "top-center") {
|
||||
opacityValue = Math.max(0, 1 - dragValue / (SWIPE_THRESHOLD_Y + 5));
|
||||
} else if (drag) {
|
||||
opacityValue = Math.max(0, 1 - dragValue / (SWIPE_THRESHOLD_X + 20));
|
||||
@ -471,7 +479,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
className: string;
|
||||
} => {
|
||||
const isCloseToEnd = total - index - 1 <= 2;
|
||||
const dragDirection = placement === "center-bottom" || placement === "center-top" ? "y" : "x";
|
||||
const dragDirection = placement === "bottom-center" || placement === "top-center" ? "y" : "x";
|
||||
const dragConstraints = {left: 0, right: 0, top: 0, bottom: 0};
|
||||
const dragElastic = getDragElasticConstraints(placement);
|
||||
|
||||
@ -534,9 +542,9 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
onDrag: (_, info) => {
|
||||
let updatedDragValue = 0;
|
||||
|
||||
if (placement === "center-top") {
|
||||
if (placement === "top-center") {
|
||||
updatedDragValue = -info.offset.y;
|
||||
} else if (placement === "center-bottom") {
|
||||
} else if (placement === "bottom-center") {
|
||||
updatedDragValue = info.offset.y;
|
||||
} else if (placement.includes("right")) {
|
||||
updatedDragValue = info.offset.x;
|
||||
@ -587,6 +595,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
|
||||
icon,
|
||||
loadingIcon,
|
||||
domRef,
|
||||
severity,
|
||||
closeIcon,
|
||||
classNames,
|
||||
color: variantProps["color"],
|
||||
|
||||
@ -13,6 +13,10 @@ export default {
|
||||
control: {type: "select"},
|
||||
options: ["flat", "bordered", "solid"],
|
||||
},
|
||||
severity: {
|
||||
control: {type: "select"},
|
||||
options: ["default", "primary", "secondary", "success", "warning", "danger"],
|
||||
},
|
||||
color: {
|
||||
control: {type: "select"},
|
||||
options: ["default", "foreground", "primary", "secondary", "success", "warning", "danger"],
|
||||
@ -37,12 +41,12 @@ export default {
|
||||
placement: {
|
||||
control: {type: "select"},
|
||||
options: [
|
||||
"right-bottom",
|
||||
"left-bottom",
|
||||
"center-bottom",
|
||||
"right-top",
|
||||
"left-top",
|
||||
"center-top",
|
||||
"bottom-right",
|
||||
"bottom-left",
|
||||
"bottom-center",
|
||||
"top-right",
|
||||
"top-left",
|
||||
"top-center",
|
||||
],
|
||||
},
|
||||
hideCloseButton: {
|
||||
|
||||
@ -14,12 +14,12 @@ const toastRegion = tv({
|
||||
},
|
||||
true: {
|
||||
base: [
|
||||
"data-[placement=right-bottom]:bottom-0 data-[placement=right-bottom]:right-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=right-bottom]:fixed data-[placement=right-bottom]:flex data-[placement=right-bottom]:flex-col",
|
||||
"data-[placement=left-bottom]:bottom-0 data-[placement=left-bottom]:left-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=left-bottom]:fixed data-[placement=left-bottom]:flex data-[placement=left-bottom]:flex-col",
|
||||
"data-[placement=center-bottom]:bottom-0 data-[placement=center-bottom]:fixed w-full px-2 sm:w-auto sm:px-0 data-[placement=center-bottom]:flex data-[placement=center-bottom]:flex-col data-[placement=center-bottom]:left-1/2 data-[placement=center-bottom]:-translate-x-1/2",
|
||||
"data-[placement=right-top]:top-0 data-[placement=right-top]:right-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=right-top]:fixed data-[placement=right-top]:flex data-[placement=right-top]:flex-col",
|
||||
"data-[placement=left-top]:top-0 data-[placement=left-top]:left-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=left-top]:fixed data-[placement=left-top]:flex data-[placement=left-top]:flex-col",
|
||||
"data-[placement=center-top]:top-0 data-[placement=center-top]:fixed w-full px-2 sm:w-auto sm:px-0 data-[placement=center-top]:flex data-[placement=center-top]:flex-col data-[placement=center-top]:left-1/2 data-[placement=center-top]:-translate-x-1/2",
|
||||
"data-[placement=bottom-right]:bottom-0 data-[placement=bottom-right]:right-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=bottom-right]:fixed data-[placement=bottom-right]:flex data-[placement=bottom-right]:flex-col",
|
||||
"data-[placement=bottom-left]:bottom-0 data-[placement=bottom-left]:left-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=bottom-left]:fixed data-[placement=bottom-left]:flex data-[placement=bottom-left]:flex-col",
|
||||
"data-[placement=bottom-center]:bottom-0 data-[placement=bottom-center]:fixed w-full px-2 sm:w-auto sm:px-0 data-[placement=bottom-center]:flex data-[placement=bottom-center]:flex-col data-[placement=bottom-center]:left-1/2 data-[placement=bottom-center]:-translate-x-1/2",
|
||||
"data-[placement=top-right]:top-0 data-[placement=top-right]:right-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=top-right]:fixed data-[placement=top-right]:flex data-[placement=top-right]:flex-col",
|
||||
"data-[placement=top-left]:top-0 data-[placement=top-left]:left-0 w-full px-2 sm:w-auto sm:px-0 data-[placement=top-left]:fixed data-[placement=top-left]:flex data-[placement=top-left]:flex-col",
|
||||
"data-[placement=top-center]:top-0 data-[placement=top-center]:fixed w-full px-2 sm:w-auto sm:px-0 data-[placement=top-center]:flex data-[placement=top-center]:flex-col data-[placement=top-center]:left-1/2 data-[placement=top-center]:-translate-x-1/2",
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -54,12 +54,12 @@ const toast = tv({
|
||||
motionDiv: [
|
||||
"fixed",
|
||||
"px-4 sm:px-0",
|
||||
"data-[placement=right-bottom]:bottom-0 data-[placement=right-bottom]:right-0 data-[placement=right-bottom]:mx-auto w-full sm:data-[placement=right-bottom]:w-max mb-1 sm:data-[placement=right-bottom]:mr-2",
|
||||
"data-[placement=left-bottom]:bottom-0 data-[placement=left-bottom]:left-0 data-[placement=left-bottom]:mx-auto w-full sm:data-[placement=left-bottom]:w-max mb-1 sm:data-[placement=left-bottom]:ml-2",
|
||||
"data-[placement=center-bottom]:bottom-0 data-[placement=center-bottom]:left-0 data-[placement=center-bottom]:right-0 w-full sm:data-[placement=center-bottom]:w-max sm:data-[placement=center-bottom]:mx-auto",
|
||||
"data-[placement=right-top]:top-0 data-[placement=right-top]:right-0 data-[placement=right-top]:mx-auto w-full sm:data-[placement=right-top]:w-max sm:data-[placement=right-top]:mr-2",
|
||||
"data-[placement=left-top]:top-0 data-[placement=left-top]:left-0 data-[placement=left-top]:mx-auto w-full sm:data-[placement=left-top]:w-max sm:data-[placement=left-top]:ml-2",
|
||||
"data-[placement=center-top]:top-0 data-[placement=center-top]:left-0 data-[placement=center-top]:right-0 w-full sm:data-[placement=center-top]:w-max sm:data-[placement=center-top]:mx-auto",
|
||||
"data-[placement=bottom-right]:bottom-0 data-[placement=bottom-right]:right-0 data-[placement=bottom-right]:mx-auto w-full sm:data-[placement=bottom-right]:w-max mb-1 sm:data-[placement=bottom-right]:mr-2",
|
||||
"data-[placement=bottom-left]:bottom-0 data-[placement=bottom-left]:left-0 data-[placement=bottom-left]:mx-auto w-full sm:data-[placement=bottom-left]:w-max mb-1 sm:data-[placement=bottom-left]:ml-2",
|
||||
"data-[placement=bottom-center]:bottom-0 data-[placement=bottom-center]:left-0 data-[placement=bottom-center]:right-0 w-full sm:data-[placement=bottom-center]:w-max sm:data-[placement=bottom-center]:mx-auto",
|
||||
"data-[placement=top-right]:top-0 data-[placement=top-right]:right-0 data-[placement=top-right]:mx-auto w-full sm:data-[placement=top-right]:w-max sm:data-[placement=top-right]:mr-2",
|
||||
"data-[placement=top-left]:top-0 data-[placement=top-left]:left-0 data-[placement=top-left]:mx-auto w-full sm:data-[placement=top-left]:w-max sm:data-[placement=top-left]:ml-2",
|
||||
"data-[placement=top-center]:top-0 data-[placement=top-center]:left-0 data-[placement=top-center]:right-0 w-full sm:data-[placement=top-center]:w-max sm:data-[placement=top-center]:mx-auto",
|
||||
],
|
||||
closeButton: [
|
||||
"opacity-0 pointer-events-none group-hover:pointer-events-auto p-0 group-hover:opacity-100 w-6 h-6 min-w-4 absolute -right-2 -top-2 items-center justify-center bg-transparent text-default-400 hover:text-default-600 border border-3 border-transparent",
|
||||
@ -143,12 +143,12 @@ const toast = tv({
|
||||
base: [
|
||||
"data-[animation=exiting]:transform",
|
||||
"data-[animation=exiting]:delay-100",
|
||||
"data-[animation=exiting]:data-[placement=right-bottom]:translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=left-bottom]:-translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=center-bottom]:translate-y-28",
|
||||
"data-[animation=exiting]:data-[placement=right-top]:translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=left-top]:-translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=center-top]:-translate-y-28",
|
||||
"data-[animation=exiting]:data-[placement=bottom-right]:translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=bottom-left]:-translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=bottom-center]:translate-y-28",
|
||||
"data-[animation=exiting]:data-[placement=top-right]:translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=top-left]:-translate-x-28",
|
||||
"data-[animation=exiting]:data-[placement=top-center]:-translate-y-28",
|
||||
"data-[animation=exiting]:opacity-0",
|
||||
"data-[animation=exiting]:duration-200",
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user