@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.
Files changed (60) hide show
  1. package/dist/react/components/date-time-picker.d.ts +43 -0
  2. package/dist/react/components/date-time-picker.d.ts.map +1 -0
  3. package/dist/react/components/date-time-picker.js +188 -0
  4. package/dist/react/components/date-time-picker.js.map +1 -0
  5. package/dist/react/index.d.ts +2 -0
  6. package/dist/react/index.d.ts.map +1 -1
  7. package/dist/react/index.js +1 -0
  8. package/dist/react/index.js.map +1 -1
  9. package/package.json +4 -6
  10. package/src/index.ts +0 -3
  11. package/src/lib/format-numbers.ts +0 -17
  12. package/src/lib/utils.ts +0 -9
  13. package/src/react/components/accordion.tsx +0 -53
  14. package/src/react/components/alert-dialog.tsx +0 -131
  15. package/src/react/components/alert.tsx +0 -59
  16. package/src/react/components/avatar.tsx +0 -51
  17. package/src/react/components/badge.tsx +0 -31
  18. package/src/react/components/button.tsx +0 -102
  19. package/src/react/components/card.tsx +0 -54
  20. package/src/react/components/checkbox.tsx +0 -28
  21. package/src/react/components/chunk-error-recovery.tsx +0 -57
  22. package/src/react/components/collapsible.tsx +0 -11
  23. package/src/react/components/consequence-dialog.tsx +0 -282
  24. package/src/react/components/date-picker.tsx +0 -567
  25. package/src/react/components/dialog.tsx +0 -114
  26. package/src/react/components/dropdown-menu.tsx +0 -200
  27. package/src/react/components/end-of-list-indicator.tsx +0 -27
  28. package/src/react/components/form.tsx +0 -174
  29. package/src/react/components/input.tsx +0 -23
  30. package/src/react/components/label.tsx +0 -21
  31. package/src/react/components/layout/screen-adaptive-view.tsx +0 -106
  32. package/src/react/components/list-pagination.tsx +0 -101
  33. package/src/react/components/menubar.tsx +0 -220
  34. package/src/react/components/popover.tsx +0 -31
  35. package/src/react/components/progress.tsx +0 -27
  36. package/src/react/components/radio-group.tsx +0 -36
  37. package/src/react/components/relative-time.tsx +0 -56
  38. package/src/react/components/scroll-area.tsx +0 -56
  39. package/src/react/components/scroll-to-top-button.tsx +0 -85
  40. package/src/react/components/search-dropdown.tsx +0 -233
  41. package/src/react/components/select.tsx +0 -165
  42. package/src/react/components/separator.tsx +0 -26
  43. package/src/react/components/sheet.tsx +0 -140
  44. package/src/react/components/skeleton.tsx +0 -16
  45. package/src/react/components/slider.tsx +0 -25
  46. package/src/react/components/spinner.tsx +0 -49
  47. package/src/react/components/switch.tsx +0 -41
  48. package/src/react/components/table.tsx +0 -121
  49. package/src/react/components/tabs.tsx +0 -59
  50. package/src/react/components/textarea.tsx +0 -22
  51. package/src/react/components/toast.tsx +0 -193
  52. package/src/react/components/toaster.tsx +0 -43
  53. package/src/react/components/tooltip.tsx +0 -28
  54. package/src/react/hooks/use-async-action.ts +0 -63
  55. package/src/react/hooks/use-cursor-page.ts +0 -139
  56. package/src/react/hooks/use-media-query.tsx +0 -26
  57. package/src/react/hooks/use-paged-list.ts +0 -106
  58. package/src/react/hooks/use-return-scroll.ts +0 -149
  59. package/src/react/hooks/use-toast.tsx +0 -176
  60. 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
- }