|
|
|
@@ -7,7 +7,6 @@
|
|
|
|
|
import * as React from "react";
|
|
|
|
|
import { Toast as BaseToast } from "@base-ui-components/react/toast";
|
|
|
|
|
import { AlertTriangle, CheckIcon, InfoIcon, XIcon } from "lucide-react";
|
|
|
|
|
import { CloseIcon } from "../icons/actions/close-icon";
|
|
|
|
|
// spinner
|
|
|
|
|
import { CircularBarSpinner } from "../spinners/circular-bar-spinner";
|
|
|
|
|
import { cn } from "../utils/classname";
|
|
|
|
@@ -54,6 +53,14 @@ export type ToastProps = {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const toastManager = BaseToast.createToastManager();
|
|
|
|
|
const DEFAULT_LOADING_TITLE = "Загрузка...";
|
|
|
|
|
|
|
|
|
|
const TOAST_SURFACE_CLASSNAME =
|
|
|
|
|
"!border-0 bg-[linear-gradient(180deg,rgba(255,255,255,0.07)_0%,rgba(255,255,255,0.025)_100%),rgba(55,55,56,0.78)] text-white shadow-[0_26px_64px_rgba(0,0,0,0.36),inset_0_1px_0_rgba(255,255,255,0.09)] !outline-none backdrop-blur-[34px]";
|
|
|
|
|
const TOAST_CLOSE_CLASSNAME =
|
|
|
|
|
"absolute top-1/2 left-4 grid size-12 -translate-y-1/2 cursor-pointer place-items-center rounded-full !border-0 bg-black/[0.68] p-0 text-white/[0.72] shadow-[inset_0_1px_0_rgba(255,255,255,0.06)] !outline-none transition-colors hover:bg-black/[0.8] hover:text-white focus:outline-none focus-visible:bg-black/[0.84] focus-visible:text-white";
|
|
|
|
|
const TOAST_STATUS_CLASSNAME =
|
|
|
|
|
"absolute top-1/2 right-5 grid size-12 -translate-y-1/2 place-items-center rounded-full bg-black/[0.24] text-white/[0.7] shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]";
|
|
|
|
|
|
|
|
|
|
export function Toast(props: ToastProps) {
|
|
|
|
|
return (
|
|
|
|
@@ -69,40 +76,28 @@ export function Toast(props: ToastProps) {
|
|
|
|
|
|
|
|
|
|
const TOAST_DATA = {
|
|
|
|
|
[TOAST_TYPE.SUCCESS]: {
|
|
|
|
|
icon: <CheckIcon width={12} height={12} className="text-on-color" />,
|
|
|
|
|
iconBgClassName: "bg-success-primary",
|
|
|
|
|
backgroundColorClassName: "!bg-surface-1",
|
|
|
|
|
borderColorClassName: "border-subtle",
|
|
|
|
|
icon: <CheckIcon width={20} height={20} className="text-current" />,
|
|
|
|
|
iconClassName: "text-[rgb(var(--nodedc-card-active-rgb,195_255_102))]",
|
|
|
|
|
},
|
|
|
|
|
[TOAST_TYPE.ERROR]: {
|
|
|
|
|
icon: <XIcon width={12} height={12} className="text-on-color" />,
|
|
|
|
|
iconBgClassName: "bg-danger-primary",
|
|
|
|
|
backgroundColorClassName: "bg-surface-1",
|
|
|
|
|
borderColorClassName: "border-subtle",
|
|
|
|
|
icon: <XIcon width={20} height={20} className="text-current" />,
|
|
|
|
|
iconClassName: "text-[#ff4452]",
|
|
|
|
|
},
|
|
|
|
|
[TOAST_TYPE.WARNING]: {
|
|
|
|
|
icon: <AlertTriangle width={12} height={12} className="text-on-color" />,
|
|
|
|
|
iconBgClassName: "bg-warning-primary",
|
|
|
|
|
backgroundColorClassName: "bg-surface-1",
|
|
|
|
|
borderColorClassName: "border-subtle",
|
|
|
|
|
icon: <AlertTriangle width={20} height={20} className="text-current" />,
|
|
|
|
|
iconClassName: "text-[#ff8830]",
|
|
|
|
|
},
|
|
|
|
|
[TOAST_TYPE.INFO]: {
|
|
|
|
|
icon: <InfoIcon width={12} height={12} className="text-on-color" />,
|
|
|
|
|
iconBgClassName: "bg-accent-primary",
|
|
|
|
|
backgroundColorClassName: "bg-surface-1",
|
|
|
|
|
borderColorClassName: "border-subtle",
|
|
|
|
|
icon: <InfoIcon width={20} height={20} className="text-current" />,
|
|
|
|
|
iconClassName: "text-[rgb(var(--nodedc-accent-rgb,51_163_255))]",
|
|
|
|
|
},
|
|
|
|
|
[TOAST_TYPE.LOADING]: {
|
|
|
|
|
icon: <CircularBarSpinner className="text-on-color" />,
|
|
|
|
|
iconBgClassName: "bg-layer-2",
|
|
|
|
|
backgroundColorClassName: "bg-surface-1",
|
|
|
|
|
borderColorClassName: "border-subtle",
|
|
|
|
|
icon: <CircularBarSpinner className="text-current" />,
|
|
|
|
|
iconClassName: "text-white",
|
|
|
|
|
},
|
|
|
|
|
[TOAST_TYPE.LOADING_TOAST]: {
|
|
|
|
|
icon: <CircularBarSpinner className="text-on-color" />,
|
|
|
|
|
iconBgClassName: "bg-layer-2",
|
|
|
|
|
backgroundColorClassName: "bg-surface-1",
|
|
|
|
|
borderColorClassName: "border-subtle",
|
|
|
|
|
icon: <CircularBarSpinner className="text-current" />,
|
|
|
|
|
iconClassName: "text-white",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -122,7 +117,7 @@ function ToastRender({ id, toast }: { id: React.Key; toast: BaseToast.Root.Toast
|
|
|
|
|
key={id}
|
|
|
|
|
className={cn(
|
|
|
|
|
// Base layout and positioning
|
|
|
|
|
"group flex w-[350px] items-center rounded-lg border shadow-raised-200",
|
|
|
|
|
"group flex min-h-[6.4rem] w-[min(430px,calc(100vw-2rem))] items-center rounded-[1.85rem]",
|
|
|
|
|
"absolute right-3 bottom-3 z-[calc(1000-var(--toast-index))]",
|
|
|
|
|
"ease-[cubic-bezier(0.22,1,0.36,1)] transition-[opacity,transform] duration-500 select-none",
|
|
|
|
|
|
|
|
|
@@ -150,8 +145,7 @@ function ToastRender({ id, toast }: { id: React.Key; toast: BaseToast.Root.Toast
|
|
|
|
|
// Default ending transform for non-limited toasts
|
|
|
|
|
"data-[ending-style]:[&:not([data-limited])]:[transform:translateY(150%)]",
|
|
|
|
|
|
|
|
|
|
data.backgroundColorClassName,
|
|
|
|
|
data.borderColorClassName
|
|
|
|
|
TOAST_SURFACE_CLASSNAME
|
|
|
|
|
)}
|
|
|
|
|
style={{
|
|
|
|
|
["--gap" as string]: "1rem",
|
|
|
|
@@ -163,30 +157,24 @@ function ToastRender({ id, toast }: { id: React.Key; toast: BaseToast.Root.Toast
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<BaseToast.Close className="absolute top-3 right-3 cursor-pointer text-icon-secondary hover:text-icon-tertiary">
|
|
|
|
|
<CloseIcon strokeWidth={1.5} width={16} height={16} />
|
|
|
|
|
<BaseToast.Close className={TOAST_CLOSE_CLASSNAME} aria-label="Закрыть уведомление">
|
|
|
|
|
<XIcon strokeWidth={1.75} width={16} height={16} />
|
|
|
|
|
</BaseToast.Close>
|
|
|
|
|
<div className="flex w-full items-start gap-2 p-4">
|
|
|
|
|
<div className="py-1">
|
|
|
|
|
{data.icon && (
|
|
|
|
|
<div
|
|
|
|
|
className={cn("flex size-4 flex-shrink-0 items-center justify-center rounded-full", data.iconBgClassName)}
|
|
|
|
|
>
|
|
|
|
|
{data.icon}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
|
|
|
<BaseToast.Title className="text-h6-medium text-primary">
|
|
|
|
|
{toastData.type === TOAST_TYPE.LOADING ? (toastData.title ?? "Loading...") : toastData.title}
|
|
|
|
|
{data.icon && <div className={cn(TOAST_STATUS_CLASSNAME, data.iconClassName)}>{data.icon}</div>}
|
|
|
|
|
<div className="flex min-h-[6.4rem] w-full min-w-0 flex-col justify-center py-5 pr-[5.4rem] pl-[5.35rem]">
|
|
|
|
|
<div className="flex min-w-0 flex-col gap-1.5">
|
|
|
|
|
<BaseToast.Title className="text-15 leading-5 font-semibold text-white">
|
|
|
|
|
{toastData.type === TOAST_TYPE.LOADING ? (toastData.title ?? DEFAULT_LOADING_TITLE) : toastData.title}
|
|
|
|
|
</BaseToast.Title>
|
|
|
|
|
{toastData.type !== TOAST_TYPE.LOADING && toastData.message && (
|
|
|
|
|
<BaseToast.Description className="text-body-xs-regular text-tertiary">
|
|
|
|
|
<BaseToast.Description className="text-13 leading-5 text-white/[0.66]">
|
|
|
|
|
{toastData.message}
|
|
|
|
|
</BaseToast.Description>
|
|
|
|
|
)}
|
|
|
|
|
{toastData.type !== TOAST_TYPE.LOADING && toastData.actionItems && (
|
|
|
|
|
<div className="flex items-center gap-2">{toastData.actionItems}</div>
|
|
|
|
|
<div className="flex items-center gap-2 text-12 font-semibold text-[rgb(var(--nodedc-card-active-rgb,195_255_102))]">
|
|
|
|
|
{toastData.actionItems}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@@ -211,36 +199,28 @@ export function ToastStatic({ type, title, message, actionItems, theme = "light"
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
// Base layout and positioning
|
|
|
|
|
"group flex w-[350px] items-start rounded-lg border border-subtle-1 shadow-overlay-100",
|
|
|
|
|
"group flex min-h-[6.4rem] w-[min(430px,calc(100vw-2rem))] items-center rounded-[1.85rem]",
|
|
|
|
|
"relative",
|
|
|
|
|
data.backgroundColorClassName,
|
|
|
|
|
data.borderColorClassName
|
|
|
|
|
TOAST_SURFACE_CLASSNAME
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<div className="absolute top-1 right-1 cursor-default text-icon-tertiary">
|
|
|
|
|
<CloseIcon strokeWidth={1.5} width={14} height={14} />
|
|
|
|
|
<div className={cn(TOAST_CLOSE_CLASSNAME, "pointer-events-none")}>
|
|
|
|
|
<XIcon strokeWidth={1.75} width={16} height={16} />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex w-full items-start gap-3 p-4">
|
|
|
|
|
<div className="py-1">
|
|
|
|
|
{data.icon && (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex size-4 flex-shrink-0 items-center justify-center rounded-full",
|
|
|
|
|
data.iconBgClassName
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{data.icon}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
|
|
|
<div className="text-h6-medium text-primary">
|
|
|
|
|
{type === TOAST_TYPE.LOADING ? (title ?? "Loading...") : title}
|
|
|
|
|
{data.icon && <div className={cn(TOAST_STATUS_CLASSNAME, data.iconClassName)}>{data.icon}</div>}
|
|
|
|
|
<div className="flex min-h-[6.4rem] w-full min-w-0 flex-col justify-center py-5 pr-[5.4rem] pl-[5.35rem]">
|
|
|
|
|
<div className="flex min-w-0 flex-col gap-1.5">
|
|
|
|
|
<div className="text-15 leading-5 font-semibold text-white">
|
|
|
|
|
{type === TOAST_TYPE.LOADING ? (title ?? DEFAULT_LOADING_TITLE) : title}
|
|
|
|
|
</div>
|
|
|
|
|
{type !== TOAST_TYPE.LOADING && message && (
|
|
|
|
|
<div className="text-body-xs-regular text-tertiary">{message}</div>
|
|
|
|
|
<div className="text-13 leading-5 text-white/[0.66]">{message}</div>
|
|
|
|
|
)}
|
|
|
|
|
{type !== TOAST_TYPE.LOADING && actionItems && (
|
|
|
|
|
<div className="flex items-center gap-2 text-12 font-semibold text-[rgb(var(--nodedc-card-active-rgb,195_255_102))]">
|
|
|
|
|
{actionItems}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{type !== TOAST_TYPE.LOADING && actionItems && <div className="flex items-center gap-2">{actionItems}</div>}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@@ -294,7 +274,7 @@ export const setPromiseToast = <ToastData,>(
|
|
|
|
|
toastManager.promise(promise, {
|
|
|
|
|
loading: {
|
|
|
|
|
data: {
|
|
|
|
|
title: options.loading ?? "Loading...",
|
|
|
|
|
title: options.loading ?? DEFAULT_LOADING_TITLE,
|
|
|
|
|
type: TOAST_TYPE.LOADING,
|
|
|
|
|
message: undefined,
|
|
|
|
|
actionItems: undefined,
|
|
|
|
|