@ttt-productions/ui-core 0.2.19 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/toast.d.ts +1 -1
- package/dist/components/toast.d.ts.map +1 -1
- package/dist/components/toast.jsx +34 -10
- package/dist/components/toast.jsx.map +1 -1
- package/dist/components/toaster.d.ts.map +1 -1
- package/dist/components/toaster.jsx +12 -9
- package/dist/components/toaster.jsx.map +1 -1
- package/dist/hooks/use-toast.d.ts +3 -0
- package/dist/hooks/use-toast.d.ts.map +1 -1
- package/dist/hooks/use-toast.jsx +20 -16
- package/dist/hooks/use-toast.jsx.map +1 -1
- package/package.json +1 -1
- package/src/components/toast.tsx +84 -26
- package/src/components/toaster.tsx +38 -18
- package/src/hooks/use-toast.tsx +30 -19
|
@@ -18,5 +18,5 @@ declare const ToastDescription: React.ForwardRefExoticComponent<Omit<ToastPrimit
|
|
|
18
18
|
/** Exported types for consumers */
|
|
19
19
|
export type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
|
20
20
|
export type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
21
|
-
export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction
|
|
21
|
+
export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction };
|
|
22
22
|
//# sourceMappingURL=toast.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/components/toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,eAAe,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../src/components/toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,eAAe,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAoDlE,QAAA,MAAM,aAAa,8CAA2B,CAAC;AAE/C,QAAA,MAAM,aAAa,kKAcjB,CAAC;AAWH,QAAA,MAAM,KAAK;;;IANP,SAAS;eACE,MAAM;IACjB,oBAAoB;kBACN,OAAO;uCAkDxB,CAAC;AAGF,QAAA,MAAM,WAAW,kKAKf,CAAC;AAGH,QAAA,MAAM,UAAU,iKAcd,CAAC;AAGH,QAAA,MAAM,UAAU,2JAKd,CAAC;AAGH,QAAA,MAAM,gBAAgB,iKAKpB,CAAC;AAGH,mCAAmC;AACnC,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,wBAAwB,CAAC,OAAO,KAAK,CAAC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAAC,OAAO,WAAW,CAAC,CAAC;AAExE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -3,14 +3,32 @@ import * as ToastPrimitives from "@radix-ui/react-toast";
|
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
5
|
import { cn } from "../lib/utils";
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* ui-core Toasts are consumed by multiple apps.
|
|
8
|
+
*
|
|
9
|
+
* Do NOT rely on app-level CSS (@apply) for critical positioning/visibility.
|
|
10
|
+
* Keep all required styling inline as Tailwind utility classes.
|
|
11
|
+
*/
|
|
12
|
+
function ToastKeyframes() {
|
|
13
|
+
// Inject a tiny global keyframe used by the optional countdown bar.
|
|
14
|
+
return (<style
|
|
15
|
+
// eslint-disable-next-line react/no-danger
|
|
16
|
+
dangerouslySetInnerHTML={{
|
|
17
|
+
__html: `@keyframes toast-progress{from{transform:scaleX(1)}to{transform:scaleX(0)}}`,
|
|
18
|
+
}}/>);
|
|
19
|
+
}
|
|
20
|
+
const toastVariants = cva(cn(
|
|
21
|
+
// base
|
|
22
|
+
"pointer-events-auto relative flex w-full items-start justify-between gap-2 overflow-hidden rounded-md border bg-background p-4 text-foreground shadow-lg transition-all",
|
|
23
|
+
// radix state/animation
|
|
24
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out", "data-[state=closed]:fade-out-80 data-[state=open]:fade-in-80", "data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full", "sm:data-[state=open]:slide-in-from-bottom-full", "data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]", "data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-[transform_200ms_ease-out]", "data-[swipe=end]:animate-out data-[swipe=end]:slide-out-to-right-full"), {
|
|
7
25
|
variants: {
|
|
8
26
|
variant: {
|
|
9
27
|
default: "",
|
|
10
|
-
destructive: "
|
|
11
|
-
success: "
|
|
12
|
-
warning: "
|
|
13
|
-
error: "
|
|
28
|
+
destructive: "border-destructive bg-destructive/10 text-destructive",
|
|
29
|
+
success: "border-emerald-600/40 bg-emerald-600/10",
|
|
30
|
+
warning: "border-amber-500/40 bg-amber-500/10",
|
|
31
|
+
error: "border-destructive bg-destructive/10",
|
|
14
32
|
},
|
|
15
33
|
},
|
|
16
34
|
defaultVariants: {
|
|
@@ -18,7 +36,7 @@ const toastVariants = cva("toast-root", {
|
|
|
18
36
|
},
|
|
19
37
|
});
|
|
20
38
|
const ToastProvider = ToastPrimitives.Provider;
|
|
21
|
-
const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Viewport ref={ref} className={cn("
|
|
39
|
+
const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Viewport ref={ref} className={cn("fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4", "sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col", "md:max-w-[420px]", className)} {...props}/>));
|
|
22
40
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
23
41
|
const Toast = React.forwardRef(({ className, variant, duration = 5000, dismissible = true, style, children, ...props }, ref) => {
|
|
24
42
|
// pass duration to Radix AND to CSS countdown bar
|
|
@@ -27,13 +45,19 @@ const Toast = React.forwardRef(({ className, variant, duration = 5000, dismissib
|
|
|
27
45
|
["--toast-duration"]: `${duration}ms`,
|
|
28
46
|
};
|
|
29
47
|
return (<ToastPrimitives.Root ref={ref} duration={duration} data-variant={variant} className={cn(toastVariants({ variant }), className)} style={mergedStyle} {...props}>
|
|
48
|
+
<ToastKeyframes />
|
|
30
49
|
{children}
|
|
31
50
|
|
|
32
51
|
{/* countdown bar */}
|
|
33
|
-
<div className="
|
|
52
|
+
<div className={cn("absolute bottom-0 left-0 h-1 w-full bg-foreground/10", "origin-left")} style={{
|
|
53
|
+
animationName: "toast-progress",
|
|
54
|
+
animationDuration: `${duration}ms`,
|
|
55
|
+
animationTimingFunction: "linear",
|
|
56
|
+
animationFillMode: "forwards",
|
|
57
|
+
}}/>
|
|
34
58
|
|
|
35
59
|
{/* dismiss button */}
|
|
36
|
-
{dismissible ? (<ToastPrimitives.Close className="
|
|
60
|
+
{dismissible ? (<ToastPrimitives.Close className={cn("absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md", "text-foreground/60 hover:text-foreground", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background")} aria-label="Dismiss toast">
|
|
37
61
|
<X className="h-4 w-4"/>
|
|
38
62
|
</ToastPrimitives.Close>) : null}
|
|
39
63
|
</ToastPrimitives.Root>);
|
|
@@ -41,11 +65,11 @@ const Toast = React.forwardRef(({ className, variant, duration = 5000, dismissib
|
|
|
41
65
|
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
42
66
|
const ToastAction = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Action ref={ref} className={cn(className)} {...props}/>));
|
|
43
67
|
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
44
|
-
const ToastClose = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Close ref={ref} className={cn("
|
|
68
|
+
const ToastClose = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Close ref={ref} className={cn("absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md", "text-foreground/60 hover:text-foreground", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background", className)} {...props}/>));
|
|
45
69
|
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
46
70
|
const ToastTitle = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Title ref={ref} className={cn(className)} {...props}/>));
|
|
47
71
|
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
48
72
|
const ToastDescription = React.forwardRef(({ className, ...props }, ref) => (<ToastPrimitives.Description ref={ref} className={cn(className)} {...props}/>));
|
|
49
73
|
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
50
|
-
export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction
|
|
74
|
+
export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction };
|
|
51
75
|
//# sourceMappingURL=toast.jsx.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.jsx","sourceRoot":"","sources":["../../src/components/toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,eAAe,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,GAAG,EAAqB,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AAEjC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAElC,MAAM,aAAa,GAAG,GAAG,
|
|
1
|
+
{"version":3,"file":"toast.jsx","sourceRoot":"","sources":["../../src/components/toast.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../../src/components/toaster.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../../src/components/toaster.tsx"],"names":[],"mappings":"AAMA,wBAAgB,OAAO,gCA4CtB"}
|
|
@@ -4,15 +4,18 @@ import { useToast } from "../hooks/use-toast";
|
|
|
4
4
|
export function Toaster() {
|
|
5
5
|
const { toasts } = useToast();
|
|
6
6
|
return (<ToastProvider swipeDirection="right" swipeThreshold={32}>
|
|
7
|
-
{toasts.map((
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
{toasts.map((t) => {
|
|
8
|
+
const { id, title, description, action, variant, duration, dismissible, open, onOpenChange, ...props } = t;
|
|
9
|
+
const v = variant === "error" ? "destructive" : (variant ?? "default");
|
|
10
|
+
return (<Toast key={id} open={open} onOpenChange={onOpenChange} variant={v} duration={duration ?? 5000} dismissible={dismissible ?? true} {...props}>
|
|
11
|
+
<div className="grid gap-1 pr-10">
|
|
12
|
+
{title ? <ToastTitle className="font-bold">{title}</ToastTitle> : null}
|
|
13
|
+
{description ? <ToastDescription className="font-bold">{description}</ToastDescription> : null}
|
|
14
|
+
</div>
|
|
15
|
+
{action}
|
|
16
|
+
<ToastClose />
|
|
17
|
+
</Toast>);
|
|
18
|
+
})}
|
|
16
19
|
<ToastViewport />
|
|
17
20
|
</ToastProvider>);
|
|
18
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toaster.jsx","sourceRoot":"","sources":["../../src/components/toaster.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"toaster.jsx","sourceRoot":"","sources":["../../src/components/toaster.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG9C,MAAM,UAAU,OAAO;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAgC,CAAC;IAE5D,OAAO,CACL,CAAC,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CACvD;MAAA,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE;YAC9B,MAAM,EACJ,EAAE,EACF,KAAK,EACL,WAAW,EACX,MAAM,EACN,OAAO,EACP,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,YAAY,EACZ,GAAG,KAAK,EACT,GAAG,CAAC,CAAC;YAEN,MAAM,CAAC,GACL,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,SAAS,CAAC,CAAC;YAE/D,OAAO,CACL,CAAC,KAAK,CACJ,GAAG,CAAC,CAAC,EAAE,CAAC,CACR,IAAI,CAAC,CAAC,IAAI,CAAC,CACX,YAAY,CAAC,CAAC,YAAY,CAAC,CAC3B,OAAO,CAAC,CAAC,CAAQ,CAAC,CAClB,QAAQ,CAAC,CAAC,QAAQ,IAAI,IAAI,CAAC,CAC3B,WAAW,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,CACjC,IAAK,KAAa,CAAC,CAEnB;YAAA,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB,CAC/B;cAAA,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CACtE;cAAA,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAChG;YAAA,EAAE,GAAG,CACL;YAAA,CAAC,MAAM,CACP;YAAA,CAAC,UAAU,CAAC,AAAD,EACb;UAAA,EAAE,KAAK,CAAC,CACT,CAAC;QACJ,CAAC,CAAC,CACF;MAAA,CAAC,aAAa,CAAC,AAAD,EAChB;IAAA,EAAE,aAAa,CAAC,CACjB,CAAC;AACJ,CAAC"}
|
|
@@ -6,6 +6,9 @@ export type ToasterToast = {
|
|
|
6
6
|
description?: React.ReactNode;
|
|
7
7
|
action?: React.ReactNode;
|
|
8
8
|
variant?: ToastVariant;
|
|
9
|
+
/** Radix controlled open state */
|
|
10
|
+
open?: boolean;
|
|
11
|
+
onOpenChange?: (open: boolean) => void;
|
|
9
12
|
/** ms */
|
|
10
13
|
duration?: number;
|
|
11
14
|
/** show X button */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-toast.d.ts","sourceRoot":"","sources":["../../src/hooks/use-toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,SAAS;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,UAAU,GAAG,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"use-toast.d.ts","sourceRoot":"","sources":["../../src/hooks/use-toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,kCAAkC;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAEvC,SAAS;IACT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,UAAU,GAAG,YAAY,CAAC;AAwE/B,wBAAgB,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC;;;oBAQ1B,OAAO,CAAC,UAAU,CAAC;EAkB3C;AAED,wBAAgB,QAAQ;;wBAcA,MAAM;YAzGpB,UAAU,EAAE;EA8GrB"}
|
package/dist/hooks/use-toast.jsx
CHANGED
|
@@ -2,23 +2,26 @@ import * as React from "react";
|
|
|
2
2
|
const TOAST_LIMIT = 1;
|
|
3
3
|
const TOAST_REMOVE_DELAY = 1000;
|
|
4
4
|
const toastTimeouts = new Map();
|
|
5
|
+
const listeners = [];
|
|
6
|
+
let memoryState = { toasts: [] };
|
|
5
7
|
const reducer = (state, action) => {
|
|
6
8
|
switch (action.type) {
|
|
7
|
-
case "ADD_TOAST":
|
|
9
|
+
case "ADD_TOAST":
|
|
8
10
|
return { ...state, toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT) };
|
|
9
|
-
|
|
10
|
-
case "UPDATE_TOAST": {
|
|
11
|
+
case "UPDATE_TOAST":
|
|
11
12
|
return {
|
|
12
13
|
...state,
|
|
13
14
|
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
|
|
14
15
|
};
|
|
15
|
-
}
|
|
16
16
|
case "DISMISS_TOAST": {
|
|
17
17
|
const { toastId } = action;
|
|
18
18
|
if (toastId) {
|
|
19
|
-
return {
|
|
19
|
+
return {
|
|
20
|
+
...state,
|
|
21
|
+
toasts: state.toasts.map((t) => (t.id === toastId ? { ...t, open: false } : t)),
|
|
22
|
+
};
|
|
20
23
|
}
|
|
21
|
-
return { ...state, toasts: state.toasts.map((t) => ({ ...t })) };
|
|
24
|
+
return { ...state, toasts: state.toasts.map((t) => ({ ...t, open: false })) };
|
|
22
25
|
}
|
|
23
26
|
case "REMOVE_TOAST": {
|
|
24
27
|
const { toastId } = action;
|
|
@@ -30,8 +33,6 @@ const reducer = (state, action) => {
|
|
|
30
33
|
return state;
|
|
31
34
|
}
|
|
32
35
|
};
|
|
33
|
-
const listeners = [];
|
|
34
|
-
let memoryState = { toasts: [] };
|
|
35
36
|
function dispatch(action) {
|
|
36
37
|
memoryState = reducer(memoryState, action);
|
|
37
38
|
listeners.forEach((listener) => listener(memoryState));
|
|
@@ -50,24 +51,27 @@ function addToRemoveQueue(toastId) {
|
|
|
50
51
|
}
|
|
51
52
|
export function toast(input) {
|
|
52
53
|
const id = genId();
|
|
54
|
+
const dismiss = () => {
|
|
55
|
+
dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
56
|
+
addToRemoveQueue(id);
|
|
57
|
+
};
|
|
58
|
+
const update = (props) => dispatch({ type: "UPDATE_TOAST", toast: { ...props, id } });
|
|
53
59
|
dispatch({
|
|
54
60
|
type: "ADD_TOAST",
|
|
55
61
|
toast: {
|
|
56
62
|
id,
|
|
63
|
+
open: true,
|
|
64
|
+
onOpenChange: (open) => {
|
|
65
|
+
if (!open)
|
|
66
|
+
dismiss();
|
|
67
|
+
},
|
|
57
68
|
dismissible: true,
|
|
58
69
|
duration: 5000,
|
|
59
70
|
variant: "default",
|
|
60
71
|
...input,
|
|
61
72
|
},
|
|
62
73
|
});
|
|
63
|
-
return {
|
|
64
|
-
id,
|
|
65
|
-
dismiss: () => {
|
|
66
|
-
dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
67
|
-
addToRemoveQueue(id);
|
|
68
|
-
},
|
|
69
|
-
update: (props) => dispatch({ type: "UPDATE_TOAST", toast: { ...props, id } }),
|
|
70
|
-
};
|
|
74
|
+
return { id, dismiss, update };
|
|
71
75
|
}
|
|
72
76
|
export function useToast() {
|
|
73
77
|
const [state, setState] = React.useState(memoryState);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-toast.jsx","sourceRoot":"","sources":["../../src/hooks/use-toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,kBAAkB,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"use-toast.jsx","sourceRoot":"","sources":["../../src/hooks/use-toast.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/components/toast.tsx
CHANGED
|
@@ -5,20 +5,52 @@ import { X } from "lucide-react";
|
|
|
5
5
|
|
|
6
6
|
import { cn } from "../lib/utils";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
/**
|
|
9
|
+
* ui-core Toasts are consumed by multiple apps.
|
|
10
|
+
*
|
|
11
|
+
* Do NOT rely on app-level CSS (@apply) for critical positioning/visibility.
|
|
12
|
+
* Keep all required styling inline as Tailwind utility classes.
|
|
13
|
+
*/
|
|
14
|
+
function ToastKeyframes() {
|
|
15
|
+
// Inject a tiny global keyframe used by the optional countdown bar.
|
|
16
|
+
return (
|
|
17
|
+
<style
|
|
18
|
+
// eslint-disable-next-line react/no-danger
|
|
19
|
+
dangerouslySetInnerHTML={{
|
|
20
|
+
__html: `@keyframes toast-progress{from{transform:scaleX(1)}to{transform:scaleX(0)}}`,
|
|
21
|
+
}}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const toastVariants = cva(
|
|
27
|
+
cn(
|
|
28
|
+
// base
|
|
29
|
+
"pointer-events-auto relative flex w-full items-start justify-between gap-2 overflow-hidden rounded-md border bg-background p-4 text-foreground shadow-lg transition-all",
|
|
30
|
+
// radix state/animation
|
|
31
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
32
|
+
"data-[state=closed]:fade-out-80 data-[state=open]:fade-in-80",
|
|
33
|
+
"data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full",
|
|
34
|
+
"sm:data-[state=open]:slide-in-from-bottom-full",
|
|
35
|
+
"data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]",
|
|
36
|
+
"data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-[transform_200ms_ease-out]",
|
|
37
|
+
"data-[swipe=end]:animate-out data-[swipe=end]:slide-out-to-right-full"
|
|
38
|
+
),
|
|
39
|
+
{
|
|
40
|
+
variants: {
|
|
41
|
+
variant: {
|
|
42
|
+
default: "",
|
|
43
|
+
destructive: "border-destructive bg-destructive/10 text-destructive",
|
|
44
|
+
success: "border-emerald-600/40 bg-emerald-600/10",
|
|
45
|
+
warning: "border-amber-500/40 bg-amber-500/10",
|
|
46
|
+
error: "border-destructive bg-destructive/10",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
variant: "default",
|
|
16
51
|
},
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
variant: "default",
|
|
20
|
-
},
|
|
21
|
-
});
|
|
52
|
+
}
|
|
53
|
+
);
|
|
22
54
|
|
|
23
55
|
const ToastProvider = ToastPrimitives.Provider;
|
|
24
56
|
|
|
@@ -26,7 +58,16 @@ const ToastViewport = React.forwardRef<
|
|
|
26
58
|
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
|
27
59
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
|
28
60
|
>(({ className, ...props }, ref) => (
|
|
29
|
-
<ToastPrimitives.Viewport
|
|
61
|
+
<ToastPrimitives.Viewport
|
|
62
|
+
ref={ref}
|
|
63
|
+
className={cn(
|
|
64
|
+
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4",
|
|
65
|
+
"sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col",
|
|
66
|
+
"md:max-w-[420px]",
|
|
67
|
+
className
|
|
68
|
+
)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
30
71
|
));
|
|
31
72
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
32
73
|
|
|
@@ -55,14 +96,30 @@ const Toast = React.forwardRef<React.ElementRef<typeof ToastPrimitives.Root>, To
|
|
|
55
96
|
style={mergedStyle}
|
|
56
97
|
{...props}
|
|
57
98
|
>
|
|
99
|
+
<ToastKeyframes />
|
|
58
100
|
{children}
|
|
59
101
|
|
|
60
102
|
{/* countdown bar */}
|
|
61
|
-
<div
|
|
103
|
+
<div
|
|
104
|
+
className={cn("absolute bottom-0 left-0 h-1 w-full bg-foreground/10", "origin-left")}
|
|
105
|
+
style={{
|
|
106
|
+
animationName: "toast-progress",
|
|
107
|
+
animationDuration: `${duration}ms`,
|
|
108
|
+
animationTimingFunction: "linear",
|
|
109
|
+
animationFillMode: "forwards",
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
62
112
|
|
|
63
113
|
{/* dismiss button */}
|
|
64
114
|
{dismissible ? (
|
|
65
|
-
<ToastPrimitives.Close
|
|
115
|
+
<ToastPrimitives.Close
|
|
116
|
+
className={cn(
|
|
117
|
+
"absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
118
|
+
"text-foreground/60 hover:text-foreground",
|
|
119
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
|
|
120
|
+
)}
|
|
121
|
+
aria-label="Dismiss toast"
|
|
122
|
+
>
|
|
66
123
|
<X className="h-4 w-4" />
|
|
67
124
|
</ToastPrimitives.Close>
|
|
68
125
|
) : null}
|
|
@@ -84,7 +141,16 @@ const ToastClose = React.forwardRef<
|
|
|
84
141
|
React.ElementRef<typeof ToastPrimitives.Close>,
|
|
85
142
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
|
86
143
|
>(({ className, ...props }, ref) => (
|
|
87
|
-
<ToastPrimitives.Close
|
|
144
|
+
<ToastPrimitives.Close
|
|
145
|
+
ref={ref}
|
|
146
|
+
className={cn(
|
|
147
|
+
"absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
148
|
+
"text-foreground/60 hover:text-foreground",
|
|
149
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background",
|
|
150
|
+
className
|
|
151
|
+
)}
|
|
152
|
+
{...props}
|
|
153
|
+
/>
|
|
88
154
|
));
|
|
89
155
|
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
90
156
|
|
|
@@ -108,12 +174,4 @@ ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
|
108
174
|
export type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
|
109
175
|
export type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
110
176
|
|
|
111
|
-
export {
|
|
112
|
-
ToastProvider,
|
|
113
|
-
ToastViewport,
|
|
114
|
-
Toast,
|
|
115
|
-
ToastTitle,
|
|
116
|
-
ToastDescription,
|
|
117
|
-
ToastClose,
|
|
118
|
-
ToastAction,
|
|
119
|
-
};
|
|
177
|
+
export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction };
|
|
@@ -2,29 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "./toast";
|
|
4
4
|
import { useToast } from "../hooks/use-toast";
|
|
5
|
+
import type { ToasterToast, ToastVariant } from "../hooks/use-toast";
|
|
5
6
|
|
|
6
7
|
export function Toaster() {
|
|
7
|
-
const { toasts } = useToast();
|
|
8
|
+
const { toasts } = useToast() as { toasts: ToasterToast[] };
|
|
8
9
|
|
|
9
10
|
return (
|
|
10
11
|
<ToastProvider swipeDirection="right" swipeThreshold={32}>
|
|
11
|
-
{toasts.map((
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
{toasts.map((t: ToasterToast) => {
|
|
13
|
+
const {
|
|
14
|
+
id,
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
action,
|
|
18
|
+
variant,
|
|
19
|
+
duration,
|
|
20
|
+
dismissible,
|
|
21
|
+
open,
|
|
22
|
+
onOpenChange,
|
|
23
|
+
...props
|
|
24
|
+
} = t;
|
|
25
|
+
|
|
26
|
+
const v: ToastVariant | "destructive" =
|
|
27
|
+
variant === "error" ? "destructive" : (variant ?? "default");
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Toast
|
|
31
|
+
key={id}
|
|
32
|
+
open={open}
|
|
33
|
+
onOpenChange={onOpenChange}
|
|
34
|
+
variant={v as any}
|
|
35
|
+
duration={duration ?? 5000}
|
|
36
|
+
dismissible={dismissible ?? true}
|
|
37
|
+
{...(props as any)}
|
|
38
|
+
>
|
|
39
|
+
<div className="grid gap-1 pr-10">
|
|
40
|
+
{title ? <ToastTitle className="font-bold">{title}</ToastTitle> : null}
|
|
41
|
+
{description ? <ToastDescription className="font-bold">{description}</ToastDescription> : null}
|
|
42
|
+
</div>
|
|
43
|
+
{action}
|
|
44
|
+
<ToastClose />
|
|
45
|
+
</Toast>
|
|
46
|
+
);
|
|
47
|
+
})}
|
|
28
48
|
<ToastViewport />
|
|
29
49
|
</ToastProvider>
|
|
30
50
|
);
|
package/src/hooks/use-toast.tsx
CHANGED
|
@@ -12,6 +12,10 @@ export type ToasterToast = {
|
|
|
12
12
|
action?: React.ReactNode;
|
|
13
13
|
variant?: ToastVariant;
|
|
14
14
|
|
|
15
|
+
/** Radix controlled open state */
|
|
16
|
+
open?: boolean;
|
|
17
|
+
onOpenChange?: (open: boolean) => void;
|
|
18
|
+
|
|
15
19
|
/** ms */
|
|
16
20
|
duration?: number;
|
|
17
21
|
|
|
@@ -32,41 +36,45 @@ interface State {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
39
|
+
const listeners: Array<(state: State) => void> = [];
|
|
40
|
+
|
|
41
|
+
let memoryState: State = { toasts: [] };
|
|
35
42
|
|
|
36
43
|
const reducer = (state: State, action: Action): State => {
|
|
37
44
|
switch (action.type) {
|
|
38
|
-
case "ADD_TOAST":
|
|
45
|
+
case "ADD_TOAST":
|
|
39
46
|
return { ...state, toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT) };
|
|
40
|
-
|
|
41
|
-
case "UPDATE_TOAST":
|
|
47
|
+
|
|
48
|
+
case "UPDATE_TOAST":
|
|
42
49
|
return {
|
|
43
50
|
...state,
|
|
44
51
|
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
|
|
45
52
|
};
|
|
46
|
-
|
|
53
|
+
|
|
47
54
|
case "DISMISS_TOAST": {
|
|
48
55
|
const { toastId } = action;
|
|
49
56
|
|
|
50
57
|
if (toastId) {
|
|
51
|
-
return {
|
|
58
|
+
return {
|
|
59
|
+
...state,
|
|
60
|
+
toasts: state.toasts.map((t) => (t.id === toastId ? { ...t, open: false } : t)),
|
|
61
|
+
};
|
|
52
62
|
}
|
|
53
63
|
|
|
54
|
-
return { ...state, toasts: state.toasts.map((t) => ({ ...t })) };
|
|
64
|
+
return { ...state, toasts: state.toasts.map((t) => ({ ...t, open: false })) };
|
|
55
65
|
}
|
|
66
|
+
|
|
56
67
|
case "REMOVE_TOAST": {
|
|
57
68
|
const { toastId } = action;
|
|
58
69
|
if (!toastId) return { ...state, toasts: [] };
|
|
59
70
|
return { ...state, toasts: state.toasts.filter((t) => t.id !== toastId) };
|
|
60
71
|
}
|
|
72
|
+
|
|
61
73
|
default:
|
|
62
74
|
return state;
|
|
63
75
|
}
|
|
64
76
|
};
|
|
65
77
|
|
|
66
|
-
const listeners: Array<(state: State) => void> = [];
|
|
67
|
-
|
|
68
|
-
let memoryState: State = { toasts: [] };
|
|
69
|
-
|
|
70
78
|
function dispatch(action: Action) {
|
|
71
79
|
memoryState = reducer(memoryState, action);
|
|
72
80
|
listeners.forEach((listener) => listener(memoryState));
|
|
@@ -90,10 +98,21 @@ function addToRemoveQueue(toastId: string) {
|
|
|
90
98
|
export function toast(input: Omit<ToastProps, "id">) {
|
|
91
99
|
const id = genId();
|
|
92
100
|
|
|
101
|
+
const dismiss = () => {
|
|
102
|
+
dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
103
|
+
addToRemoveQueue(id);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const update = (props: Partial<ToastProps>) => dispatch({ type: "UPDATE_TOAST", toast: { ...props, id } });
|
|
107
|
+
|
|
93
108
|
dispatch({
|
|
94
109
|
type: "ADD_TOAST",
|
|
95
110
|
toast: {
|
|
96
111
|
id,
|
|
112
|
+
open: true,
|
|
113
|
+
onOpenChange: (open) => {
|
|
114
|
+
if (!open) dismiss();
|
|
115
|
+
},
|
|
97
116
|
dismissible: true,
|
|
98
117
|
duration: 5000,
|
|
99
118
|
variant: "default",
|
|
@@ -101,15 +120,7 @@ export function toast(input: Omit<ToastProps, "id">) {
|
|
|
101
120
|
},
|
|
102
121
|
});
|
|
103
122
|
|
|
104
|
-
return {
|
|
105
|
-
id,
|
|
106
|
-
dismiss: () => {
|
|
107
|
-
dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
108
|
-
addToRemoveQueue(id);
|
|
109
|
-
},
|
|
110
|
-
update: (props: Partial<ToastProps>) =>
|
|
111
|
-
dispatch({ type: "UPDATE_TOAST", toast: { ...props, id } }),
|
|
112
|
-
};
|
|
123
|
+
return { id, dismiss, update };
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
export function useToast() {
|