@ttt-productions/ui-core 0.11.18 → 0.11.20
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/react/components/date-time-picker.d.ts +43 -0
- package/dist/react/components/date-time-picker.d.ts.map +1 -0
- package/dist/react/components/date-time-picker.js +188 -0
- package/dist/react/components/date-time-picker.js.map +1 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/package.json +4 -6
- package/src/index.ts +0 -3
- package/src/lib/format-numbers.ts +0 -17
- package/src/lib/utils.ts +0 -9
- package/src/react/components/accordion.tsx +0 -53
- package/src/react/components/alert-dialog.tsx +0 -131
- package/src/react/components/alert.tsx +0 -59
- package/src/react/components/avatar.tsx +0 -51
- package/src/react/components/badge.tsx +0 -31
- package/src/react/components/button.tsx +0 -102
- package/src/react/components/card.tsx +0 -54
- package/src/react/components/checkbox.tsx +0 -28
- package/src/react/components/chunk-error-recovery.tsx +0 -57
- package/src/react/components/collapsible.tsx +0 -11
- package/src/react/components/consequence-dialog.tsx +0 -282
- package/src/react/components/date-picker.tsx +0 -567
- package/src/react/components/dialog.tsx +0 -114
- package/src/react/components/dropdown-menu.tsx +0 -200
- package/src/react/components/end-of-list-indicator.tsx +0 -27
- package/src/react/components/form.tsx +0 -174
- package/src/react/components/input.tsx +0 -23
- package/src/react/components/label.tsx +0 -21
- package/src/react/components/layout/screen-adaptive-view.tsx +0 -106
- package/src/react/components/list-pagination.tsx +0 -101
- package/src/react/components/menubar.tsx +0 -220
- package/src/react/components/popover.tsx +0 -31
- package/src/react/components/progress.tsx +0 -27
- package/src/react/components/radio-group.tsx +0 -36
- package/src/react/components/relative-time.tsx +0 -56
- package/src/react/components/scroll-area.tsx +0 -56
- package/src/react/components/scroll-to-top-button.tsx +0 -85
- package/src/react/components/search-dropdown.tsx +0 -233
- package/src/react/components/select.tsx +0 -165
- package/src/react/components/separator.tsx +0 -26
- package/src/react/components/sheet.tsx +0 -140
- package/src/react/components/skeleton.tsx +0 -16
- package/src/react/components/slider.tsx +0 -25
- package/src/react/components/spinner.tsx +0 -49
- package/src/react/components/switch.tsx +0 -41
- package/src/react/components/table.tsx +0 -121
- package/src/react/components/tabs.tsx +0 -59
- package/src/react/components/textarea.tsx +0 -22
- package/src/react/components/toast.tsx +0 -193
- package/src/react/components/toaster.tsx +0 -43
- package/src/react/components/tooltip.tsx +0 -28
- package/src/react/hooks/use-async-action.ts +0 -63
- package/src/react/hooks/use-cursor-page.ts +0 -139
- package/src/react/hooks/use-media-query.tsx +0 -26
- package/src/react/hooks/use-paged-list.ts +0 -106
- package/src/react/hooks/use-return-scroll.ts +0 -149
- package/src/react/hooks/use-toast.tsx +0 -176
- package/src/react/index.ts +0 -85
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils.js"
|
|
7
|
-
import { Spinner } from "./spinner.js"
|
|
8
|
-
|
|
9
|
-
const Switch = React.forwardRef<
|
|
10
|
-
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
11
|
-
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> & {
|
|
12
|
-
/**
|
|
13
|
-
* The change this switch started is in flight: the switch disables itself, sets
|
|
14
|
-
* `aria-busy`, and spins inside its thumb. It keeps showing the COMMITTED state —
|
|
15
|
-
* the caller flips `checked` only once the write lands.
|
|
16
|
-
*/
|
|
17
|
-
pending?: boolean
|
|
18
|
-
}
|
|
19
|
-
>(({ className, pending = false, disabled, ...props }, ref) => (
|
|
20
|
-
<SwitchPrimitives.Root
|
|
21
|
-
className={cn(
|
|
22
|
-
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
|
23
|
-
className
|
|
24
|
-
)}
|
|
25
|
-
{...props}
|
|
26
|
-
{...(pending ? { "aria-busy": true, "data-pending": "" } : {})}
|
|
27
|
-
disabled={disabled || pending || undefined}
|
|
28
|
-
ref={ref}
|
|
29
|
-
>
|
|
30
|
-
<SwitchPrimitives.Thumb
|
|
31
|
-
className={cn(
|
|
32
|
-
"pointer-events-none flex h-5 w-5 items-center justify-center rounded-full bg-background elevation-raised transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
|
33
|
-
)}
|
|
34
|
-
>
|
|
35
|
-
{pending ? <Spinner size="xs" /> : null}
|
|
36
|
-
</SwitchPrimitives.Thumb>
|
|
37
|
-
</SwitchPrimitives.Root>
|
|
38
|
-
))
|
|
39
|
-
Switch.displayName = SwitchPrimitives.Root.displayName
|
|
40
|
-
|
|
41
|
-
export { Switch }
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { cn } from "../../lib/utils.js"
|
|
5
|
-
|
|
6
|
-
const Table = React.forwardRef<
|
|
7
|
-
HTMLTableElement,
|
|
8
|
-
React.HTMLAttributes<HTMLTableElement>
|
|
9
|
-
>(({ className, ...props }, ref) => (
|
|
10
|
-
<div className="relative w-full overflow-auto">
|
|
11
|
-
<table
|
|
12
|
-
ref={ref}
|
|
13
|
-
className={cn("w-full caption-bottom text-sm", className)}
|
|
14
|
-
{...props}
|
|
15
|
-
/>
|
|
16
|
-
</div>
|
|
17
|
-
))
|
|
18
|
-
Table.displayName = "Table"
|
|
19
|
-
|
|
20
|
-
const TableHeader = React.forwardRef<
|
|
21
|
-
HTMLTableSectionElement,
|
|
22
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
23
|
-
>(({ className, ...props }, ref) => (
|
|
24
|
-
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
|
25
|
-
))
|
|
26
|
-
TableHeader.displayName = "TableHeader"
|
|
27
|
-
|
|
28
|
-
const TableBody = React.forwardRef<
|
|
29
|
-
HTMLTableSectionElement,
|
|
30
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
31
|
-
>(({ className, ...props }, ref) => (
|
|
32
|
-
<tbody
|
|
33
|
-
ref={ref}
|
|
34
|
-
className={cn("[&_tr:last-child]:border-0", className)}
|
|
35
|
-
{...props}
|
|
36
|
-
/>
|
|
37
|
-
))
|
|
38
|
-
TableBody.displayName = "TableBody"
|
|
39
|
-
|
|
40
|
-
const TableFooter = React.forwardRef<
|
|
41
|
-
HTMLTableSectionElement,
|
|
42
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
43
|
-
>(({ className, ...props }, ref) => (
|
|
44
|
-
<tfoot
|
|
45
|
-
ref={ref}
|
|
46
|
-
className={cn(
|
|
47
|
-
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
48
|
-
className
|
|
49
|
-
)}
|
|
50
|
-
{...props}
|
|
51
|
-
/>
|
|
52
|
-
))
|
|
53
|
-
TableFooter.displayName = "TableFooter"
|
|
54
|
-
|
|
55
|
-
const TableRow = React.forwardRef<
|
|
56
|
-
HTMLTableRowElement,
|
|
57
|
-
React.HTMLAttributes<HTMLTableRowElement>
|
|
58
|
-
>(({ className, ...props }, ref) => (
|
|
59
|
-
<tr
|
|
60
|
-
ref={ref}
|
|
61
|
-
className={cn(
|
|
62
|
-
"border-b transition-colors hover:bg-[color:var(--table-row-hover)] data-[state=selected]:bg-muted",
|
|
63
|
-
className
|
|
64
|
-
)}
|
|
65
|
-
{...props}
|
|
66
|
-
/>
|
|
67
|
-
))
|
|
68
|
-
TableRow.displayName = "TableRow"
|
|
69
|
-
|
|
70
|
-
const TableHead = React.forwardRef<
|
|
71
|
-
HTMLTableCellElement,
|
|
72
|
-
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
73
|
-
>(({ className, ...props }, ref) => (
|
|
74
|
-
<th
|
|
75
|
-
ref={ref}
|
|
76
|
-
className={cn(
|
|
77
|
-
"h-12 px-4 text-left align-middle font-bold text-label [&:has([role=checkbox])]:pr-0",
|
|
78
|
-
className
|
|
79
|
-
)}
|
|
80
|
-
{...props}
|
|
81
|
-
/>
|
|
82
|
-
))
|
|
83
|
-
TableHead.displayName = "TableHead"
|
|
84
|
-
|
|
85
|
-
const TableCell = React.forwardRef<
|
|
86
|
-
HTMLTableCellElement,
|
|
87
|
-
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
88
|
-
>(({ className, ...props }, ref) => (
|
|
89
|
-
<td
|
|
90
|
-
ref={ref}
|
|
91
|
-
className={cn(
|
|
92
|
-
"p-4 align-middle text-label font-bold [&:has([role=checkbox])]:pr-0",
|
|
93
|
-
className
|
|
94
|
-
)}
|
|
95
|
-
{...props}
|
|
96
|
-
/>
|
|
97
|
-
))
|
|
98
|
-
TableCell.displayName = "TableCell"
|
|
99
|
-
|
|
100
|
-
const TableCaption = React.forwardRef<
|
|
101
|
-
HTMLTableCaptionElement,
|
|
102
|
-
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
103
|
-
>(({ className, ...props }, ref) => (
|
|
104
|
-
<caption
|
|
105
|
-
ref={ref}
|
|
106
|
-
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
|
107
|
-
{...props}
|
|
108
|
-
/>
|
|
109
|
-
))
|
|
110
|
-
TableCaption.displayName = "TableCaption"
|
|
111
|
-
|
|
112
|
-
export {
|
|
113
|
-
Table,
|
|
114
|
-
TableHeader,
|
|
115
|
-
TableBody,
|
|
116
|
-
TableFooter,
|
|
117
|
-
TableHead,
|
|
118
|
-
TableRow,
|
|
119
|
-
TableCell,
|
|
120
|
-
TableCaption,
|
|
121
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils.js"
|
|
7
|
-
|
|
8
|
-
const Tabs = TabsPrimitive.Root
|
|
9
|
-
|
|
10
|
-
const TabsList = React.forwardRef<
|
|
11
|
-
React.ElementRef<typeof TabsPrimitive.List>,
|
|
12
|
-
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
|
13
|
-
>(({ className, ...props }, ref) => (
|
|
14
|
-
<TabsPrimitive.List
|
|
15
|
-
ref={ref}
|
|
16
|
-
className={cn(
|
|
17
|
-
"inline-flex h-10 items-center justify-center rounded-md p-1 text-muted-foreground",
|
|
18
|
-
className
|
|
19
|
-
)}
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
))
|
|
23
|
-
TabsList.displayName = TabsPrimitive.List.displayName
|
|
24
|
-
|
|
25
|
-
const TabsTrigger = React.forwardRef<
|
|
26
|
-
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
27
|
-
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
28
|
-
>(({ className, children, ...props }, ref) => (
|
|
29
|
-
<TabsPrimitive.Trigger
|
|
30
|
-
ref={ref}
|
|
31
|
-
className={cn(
|
|
32
|
-
"tabs-trigger inline-flex items-center justify-center whitespace-nowrap rounded-xs px-3 py-1.5 text-sm !font-bold ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
33
|
-
"data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:border-2 data-[state=active]:border-[hsl(var(--brand-primary-deep))]",
|
|
34
|
-
"data-[state=inactive]:border data-[state=inactive]:border-border data-[state=inactive]:bg-background data-[state=inactive]:hover:bg-accent data-[state=inactive]:hover:text-accent-foreground",
|
|
35
|
-
className
|
|
36
|
-
)}
|
|
37
|
-
{...props}
|
|
38
|
-
>
|
|
39
|
-
{children}
|
|
40
|
-
</TabsPrimitive.Trigger>
|
|
41
|
-
))
|
|
42
|
-
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
|
43
|
-
|
|
44
|
-
const TabsContent = React.forwardRef<
|
|
45
|
-
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
46
|
-
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
47
|
-
>(({ className, ...props }, ref) => (
|
|
48
|
-
<TabsPrimitive.Content
|
|
49
|
-
ref={ref}
|
|
50
|
-
className={cn(
|
|
51
|
-
"mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
52
|
-
className
|
|
53
|
-
)}
|
|
54
|
-
{...props}
|
|
55
|
-
/>
|
|
56
|
-
))
|
|
57
|
-
TabsContent.displayName = TabsPrimitive.Content.displayName
|
|
58
|
-
|
|
59
|
-
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { cn } from "../../lib/utils.js"
|
|
5
|
-
|
|
6
|
-
const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"textarea">>(
|
|
7
|
-
({ className, ...props }, ref) => {
|
|
8
|
-
return (
|
|
9
|
-
<textarea
|
|
10
|
-
className={cn(
|
|
11
|
-
"flex min-h-[80px] w-full rounded-md border-2 border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
-
className
|
|
13
|
-
)}
|
|
14
|
-
ref={ref}
|
|
15
|
-
{...props}
|
|
16
|
-
/>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
)
|
|
20
|
-
Textarea.displayName = "Textarea"
|
|
21
|
-
|
|
22
|
-
export { Textarea }
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import * as ToastPrimitives from "@radix-ui/react-toast";
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils.js";
|
|
7
|
-
|
|
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
|
-
dangerouslySetInnerHTML={{
|
|
19
|
-
__html: `@keyframes toast-progress{from{transform:scaleX(1)}to{transform:scaleX(0)}}`,
|
|
20
|
-
}}
|
|
21
|
-
/>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const toastVariants = cva(
|
|
26
|
-
cn(
|
|
27
|
-
// base
|
|
28
|
-
"pointer-events-auto relative flex w-full items-start justify-between gap-2 overflow-hidden rounded-md border bg-background p-4 text-foreground elevation-raised transition-all",
|
|
29
|
-
// radix state/animation
|
|
30
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
31
|
-
"data-[state=closed]:fade-out-80 data-[state=open]:fade-in-80",
|
|
32
|
-
"data-[state=closed]:slide-out-to-top-full data-[state=open]:slide-in-from-top-full",
|
|
33
|
-
"data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]",
|
|
34
|
-
"data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-[transform_200ms_ease-out]",
|
|
35
|
-
"data-[swipe=end]:animate-out data-[swipe=end]:slide-out-to-right-full"
|
|
36
|
-
),
|
|
37
|
-
{
|
|
38
|
-
variants: {
|
|
39
|
-
variant: {
|
|
40
|
-
default: "",
|
|
41
|
-
destructive: "border-[color:var(--toast-destructive-border)] bg-[color:var(--toast-destructive)] text-[color:var(--toast-destructive-foreground)]",
|
|
42
|
-
success: "border-[color:var(--toast-success-border)] bg-[color:var(--toast-success)] text-[color:var(--toast-success-foreground)]",
|
|
43
|
-
warning: "border-[color:var(--toast-warning-border)] bg-[color:var(--toast-warning)] text-[color:var(--toast-warning-foreground)]",
|
|
44
|
-
error: "border-[color:var(--toast-destructive-border)] bg-[color:var(--toast-destructive)] text-[color:var(--toast-destructive-foreground)]",
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
defaultVariants: {
|
|
48
|
-
variant: "default",
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const ToastProvider = ToastPrimitives.Provider;
|
|
54
|
-
|
|
55
|
-
const ToastViewport = React.forwardRef<
|
|
56
|
-
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
|
57
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
|
58
|
-
>(({ className, ...props }, ref) => (
|
|
59
|
-
<ToastPrimitives.Viewport
|
|
60
|
-
ref={ref}
|
|
61
|
-
className={cn(
|
|
62
|
-
"fixed top-0 left-1/2 -translate-x-1/2 z-[100] flex max-h-screen w-full flex-col gap-2 p-4",
|
|
63
|
-
"sm:max-w-[480px] md:max-w-[600px]",
|
|
64
|
-
className
|
|
65
|
-
)}
|
|
66
|
-
{...props}
|
|
67
|
-
/>
|
|
68
|
-
));
|
|
69
|
-
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
70
|
-
|
|
71
|
-
type ToastRootProps = React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
|
72
|
-
VariantProps<typeof toastVariants> & {
|
|
73
|
-
/** ms; ignored when `persistent` is true */
|
|
74
|
-
duration?: number;
|
|
75
|
-
/** show X button */
|
|
76
|
-
dismissible?: boolean;
|
|
77
|
-
/** When true: no auto-dismiss, no countdown bar. */
|
|
78
|
-
persistent?: boolean;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const Toast = React.forwardRef<React.ElementRef<typeof ToastPrimitives.Root>, ToastRootProps>(
|
|
82
|
-
({ className, variant, duration, dismissible = true, persistent = false, style, children, onOpenChange, ...props }, ref) => {
|
|
83
|
-
// Caller is the source of truth for duration. Persistent toasts disable
|
|
84
|
-
// the auto-dismiss entirely by passing `Infinity` to Radix.
|
|
85
|
-
const effectiveDuration = persistent ? Infinity : (duration ?? 5000);
|
|
86
|
-
// pass duration to Radix AND to CSS countdown bar
|
|
87
|
-
const mergedStyle: React.CSSProperties = {
|
|
88
|
-
...(style as React.CSSProperties),
|
|
89
|
-
["--toast-duration" as any]: `${effectiveDuration}ms`,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<ToastPrimitives.Root
|
|
94
|
-
ref={ref}
|
|
95
|
-
duration={effectiveDuration}
|
|
96
|
-
data-variant={variant}
|
|
97
|
-
data-persistent={persistent ? "" : undefined}
|
|
98
|
-
className={cn(toastVariants({ variant }), className)}
|
|
99
|
-
style={mergedStyle}
|
|
100
|
-
onOpenChange={onOpenChange}
|
|
101
|
-
{...props}
|
|
102
|
-
>
|
|
103
|
-
<ToastKeyframes />
|
|
104
|
-
{children}
|
|
105
|
-
|
|
106
|
-
{/* countdown bar — hidden entirely for persistent toasts */}
|
|
107
|
-
{!persistent ? (
|
|
108
|
-
<div
|
|
109
|
-
aria-hidden
|
|
110
|
-
className={cn("absolute bottom-0 left-0 h-1 w-full bg-foreground/30", "origin-left")}
|
|
111
|
-
style={{
|
|
112
|
-
animationName: "toast-progress",
|
|
113
|
-
animationDuration: `${effectiveDuration}ms`,
|
|
114
|
-
animationTimingFunction: "linear",
|
|
115
|
-
animationFillMode: "forwards",
|
|
116
|
-
}}
|
|
117
|
-
// Radix's own auto-close timer PAUSES on pointer-over the viewport,
|
|
118
|
-
// focus, and window-blur — but this CSS bar does not. When the bar
|
|
119
|
-
// empties we must dismiss regardless of Radix's paused timer, so the
|
|
120
|
-
// toast never lingers after the countdown visibly reaches zero.
|
|
121
|
-
// Radix's `duration` above remains a motion-independent backstop
|
|
122
|
-
// (e.g. under prefers-reduced-motion where this event may not fire).
|
|
123
|
-
onAnimationEnd={(e) => {
|
|
124
|
-
if (e.animationName === "toast-progress") onOpenChange?.(false);
|
|
125
|
-
}}
|
|
126
|
-
/>
|
|
127
|
-
) : null}
|
|
128
|
-
|
|
129
|
-
{/* dismiss button */}
|
|
130
|
-
{dismissible ? (
|
|
131
|
-
<ToastPrimitives.Close
|
|
132
|
-
className={cn(
|
|
133
|
-
"absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
134
|
-
"text-foreground/60 hover:text-foreground",
|
|
135
|
-
"focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
|
|
136
|
-
)}
|
|
137
|
-
aria-label="Dismiss toast"
|
|
138
|
-
>
|
|
139
|
-
<X className="h-4 w-4" />
|
|
140
|
-
</ToastPrimitives.Close>
|
|
141
|
-
) : null}
|
|
142
|
-
</ToastPrimitives.Root>
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
);
|
|
146
|
-
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
147
|
-
|
|
148
|
-
const ToastAction = React.forwardRef<
|
|
149
|
-
React.ElementRef<typeof ToastPrimitives.Action>,
|
|
150
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
|
151
|
-
>(({ className, ...props }, ref) => (
|
|
152
|
-
<ToastPrimitives.Action ref={ref} className={cn(className)} {...props} />
|
|
153
|
-
));
|
|
154
|
-
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
155
|
-
|
|
156
|
-
const ToastClose = React.forwardRef<
|
|
157
|
-
React.ElementRef<typeof ToastPrimitives.Close>,
|
|
158
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
|
159
|
-
>(({ className, ...props }, ref) => (
|
|
160
|
-
<ToastPrimitives.Close
|
|
161
|
-
ref={ref}
|
|
162
|
-
className={cn(
|
|
163
|
-
"absolute right-2 top-2 inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
164
|
-
"text-foreground/60 hover:text-foreground",
|
|
165
|
-
"focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background",
|
|
166
|
-
className
|
|
167
|
-
)}
|
|
168
|
-
{...props}
|
|
169
|
-
/>
|
|
170
|
-
));
|
|
171
|
-
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
172
|
-
|
|
173
|
-
const ToastTitle = React.forwardRef<
|
|
174
|
-
React.ElementRef<typeof ToastPrimitives.Title>,
|
|
175
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
|
176
|
-
>(({ className, ...props }, ref) => (
|
|
177
|
-
<ToastPrimitives.Title ref={ref} className={cn("break-words", className)} {...props} />
|
|
178
|
-
));
|
|
179
|
-
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
180
|
-
|
|
181
|
-
const ToastDescription = React.forwardRef<
|
|
182
|
-
React.ElementRef<typeof ToastPrimitives.Description>,
|
|
183
|
-
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
|
184
|
-
>(({ className, ...props }, ref) => (
|
|
185
|
-
<ToastPrimitives.Description ref={ref} className={cn("break-words", className)} {...props} />
|
|
186
|
-
));
|
|
187
|
-
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
188
|
-
|
|
189
|
-
/** Exported types for consumers */
|
|
190
|
-
export type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;
|
|
191
|
-
export type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
192
|
-
|
|
193
|
-
export { ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastClose, ToastAction };
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "./toast.js";
|
|
4
|
-
import { useToast } from "../hooks/use-toast.js";
|
|
5
|
-
import type { ToasterToast, ToastVariant } from "../hooks/use-toast.js";
|
|
6
|
-
|
|
7
|
-
export function Toaster() {
|
|
8
|
-
const { toasts } = useToast() as { toasts: ToasterToast[] };
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<ToastProvider swipeDirection="right" swipeThreshold={32}>
|
|
12
|
-
{toasts.map((t: ToasterToast) => {
|
|
13
|
-
const { id, title, description, action, variant, duration, dismissible, persistent, open, onOpenChange, ...props } = t;
|
|
14
|
-
|
|
15
|
-
const v: ToastVariant | "destructive" =
|
|
16
|
-
variant === "error" ? "destructive" : (variant ?? "default");
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Toast
|
|
20
|
-
key={id}
|
|
21
|
-
open={open}
|
|
22
|
-
onOpenChange={onOpenChange}
|
|
23
|
-
variant={v as any}
|
|
24
|
-
duration={duration}
|
|
25
|
-
dismissible={dismissible ?? true}
|
|
26
|
-
persistent={persistent ?? false}
|
|
27
|
-
{...(props as any)}
|
|
28
|
-
>
|
|
29
|
-
<div className="grid gap-1 pr-10">
|
|
30
|
-
{title ? <ToastTitle className="font-bold">{title}</ToastTitle> : null}
|
|
31
|
-
{description ? <ToastDescription className="font-bold">{description}</ToastDescription> : null}
|
|
32
|
-
</div>
|
|
33
|
-
{action}
|
|
34
|
-
<ToastClose />
|
|
35
|
-
</Toast>
|
|
36
|
-
);
|
|
37
|
-
})}
|
|
38
|
-
|
|
39
|
-
{/* debug: proves viewport is visible/on-screen */}
|
|
40
|
-
<ToastViewport />
|
|
41
|
-
</ToastProvider>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils.js"
|
|
7
|
-
|
|
8
|
-
const TooltipProvider = TooltipPrimitive.Provider
|
|
9
|
-
const Tooltip = TooltipPrimitive.Root
|
|
10
|
-
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
11
|
-
|
|
12
|
-
const TooltipContent = React.forwardRef<
|
|
13
|
-
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
14
|
-
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
15
|
-
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
16
|
-
<TooltipPrimitive.Content
|
|
17
|
-
ref={ref}
|
|
18
|
-
sideOffset={sideOffset}
|
|
19
|
-
className={cn(
|
|
20
|
-
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground elevation-popover animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
21
|
-
className
|
|
22
|
-
)}
|
|
23
|
-
{...props}
|
|
24
|
-
/>
|
|
25
|
-
))
|
|
26
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
27
|
-
|
|
28
|
-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useCallback, useRef, useState } from "react";
|
|
4
|
-
|
|
5
|
-
export interface UseAsyncActionOptions {
|
|
6
|
-
/**
|
|
7
|
-
* Receives anything the action throws. REQUIRED, so every call site decides what a
|
|
8
|
-
* failure means (surface it, capture it, or accept that another layer already did):
|
|
9
|
-
* `run` itself never rejects, so a fire-and-forget `onClick={() => void run()}` can
|
|
10
|
-
* never become an unhandled rejection.
|
|
11
|
-
*/
|
|
12
|
-
onError: (error: unknown) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AsyncAction<TArgs extends unknown[]> {
|
|
16
|
-
/**
|
|
17
|
-
* Starts the action unless one is already in flight — a repeat call while pending is
|
|
18
|
-
* ignored. Resolves once the action settles: `true` when it completed, `false` when it
|
|
19
|
-
* was ignored or threw. Never rejects.
|
|
20
|
-
*/
|
|
21
|
-
run: (...args: TArgs) => Promise<boolean>;
|
|
22
|
-
/** True from the moment `run` starts the action until it settles. */
|
|
23
|
-
pending: boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Pending state + repeat guard for async work that is NOT a React Query mutation
|
|
28
|
-
* (a local media step, a sign-out, an awaited navigation). Feed `pending` to the
|
|
29
|
-
* control's own `pending` prop. Server writes stay mutations — this hook does not
|
|
30
|
-
* replace the data layer's one mutation path.
|
|
31
|
-
*
|
|
32
|
-
* The guard is a ref, not the state, so two clicks inside one frame cannot both start
|
|
33
|
-
* the action.
|
|
34
|
-
*/
|
|
35
|
-
export function useAsyncAction<TArgs extends unknown[]>(
|
|
36
|
-
action: (...args: TArgs) => Promise<unknown>,
|
|
37
|
-
options: UseAsyncActionOptions,
|
|
38
|
-
): AsyncAction<TArgs> {
|
|
39
|
-
const [pending, setPending] = useState(false);
|
|
40
|
-
const inFlightRef = useRef(false);
|
|
41
|
-
const actionRef = useRef(action);
|
|
42
|
-
const onErrorRef = useRef(options.onError);
|
|
43
|
-
actionRef.current = action;
|
|
44
|
-
onErrorRef.current = options.onError;
|
|
45
|
-
|
|
46
|
-
const run = useCallback(async (...args: TArgs): Promise<boolean> => {
|
|
47
|
-
if (inFlightRef.current) return false;
|
|
48
|
-
inFlightRef.current = true;
|
|
49
|
-
setPending(true);
|
|
50
|
-
try {
|
|
51
|
-
await actionRef.current(...args);
|
|
52
|
-
return true;
|
|
53
|
-
} catch (error) {
|
|
54
|
-
onErrorRef.current(error);
|
|
55
|
-
return false;
|
|
56
|
-
} finally {
|
|
57
|
-
inFlightRef.current = false;
|
|
58
|
-
setPending(false);
|
|
59
|
-
}
|
|
60
|
-
}, []);
|
|
61
|
-
|
|
62
|
-
return { run, pending };
|
|
63
|
-
}
|