@srcroot/ui 0.0.54 → 0.0.56
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/README.md +151 -151
- package/dist/index.d.ts +0 -0
- package/dist/index.js +55 -1
- package/package.json +7 -2
- package/src/registry/analytics/google-analytics.tsx +36 -39
- package/src/registry/analytics/google-tag-manager.tsx +62 -65
- package/src/registry/analytics/meta-pixel.tsx +44 -47
- package/src/registry/analytics/microsoft-clarity.tsx +31 -34
- package/src/registry/analytics/tiktok-pixel.tsx +34 -37
- package/src/registry/lib/utils.ts +0 -0
- package/src/registry/themes/v3/blue.css +157 -157
- package/src/registry/themes/v3/glass.css +153 -153
- package/src/registry/themes/v3/gray.css +157 -157
- package/src/registry/themes/v3/green.css +157 -157
- package/src/registry/themes/v3/neutral.css +157 -157
- package/src/registry/themes/v3/orange.css +157 -157
- package/src/registry/themes/v3/rose.css +157 -157
- package/src/registry/themes/v3/slate.css +157 -157
- package/src/registry/themes/v3/stone.css +157 -157
- package/src/registry/themes/v3/violet.css +186 -186
- package/src/registry/themes/v3/zinc.css +157 -157
- package/src/registry/themes/v4/blue.css +184 -184
- package/src/registry/themes/v4/glass.css +180 -180
- package/src/registry/themes/v4/gray.css +184 -184
- package/src/registry/themes/v4/green.css +184 -184
- package/src/registry/themes/v4/neutral.css +184 -184
- package/src/registry/themes/v4/orange.css +184 -184
- package/src/registry/themes/v4/rose.css +184 -184
- package/src/registry/themes/v4/slate.css +184 -184
- package/src/registry/themes/v4/stone.css +184 -184
- package/src/registry/themes/v4/violet.css +184 -184
- package/src/registry/themes/v4/zinc.css +184 -184
- package/src/registry/ui/accordion.tsx +164 -165
- package/src/registry/ui/alert-dialog.tsx +213 -214
- package/src/registry/ui/alert.tsx +73 -76
- package/src/registry/ui/aspect-ratio.tsx +44 -47
- package/src/registry/ui/avatar.tsx +96 -97
- package/src/registry/ui/badge.tsx +52 -55
- package/src/registry/ui/breadcrumb.tsx +147 -150
- package/src/registry/ui/button-group.tsx +64 -67
- package/src/registry/ui/button.tsx +71 -72
- package/src/registry/ui/calendar.tsx +514 -515
- package/src/registry/ui/card.tsx +88 -91
- package/src/registry/ui/carousel.tsx +214 -214
- package/src/registry/ui/chart.tsx +373 -373
- package/src/registry/ui/chatbot.tsx +86 -13
- package/src/registry/ui/checkbox.tsx +93 -94
- package/src/registry/ui/collapsible.tsx +107 -108
- package/src/registry/ui/combobox.tsx +171 -171
- package/src/registry/ui/command.tsx +300 -300
- package/src/registry/ui/container.tsx +44 -47
- package/src/registry/ui/context-menu.tsx +221 -221
- package/src/registry/ui/date-picker.tsx +228 -228
- package/src/registry/ui/dialog.tsx +269 -270
- package/src/registry/ui/drawer.tsx +10 -4
- package/src/registry/ui/dropdown-menu.tsx +529 -530
- package/src/registry/ui/empty-state.tsx +0 -2
- package/src/registry/ui/file-upload.tsx +0 -0
- package/src/registry/ui/floating-dock.tsx +0 -0
- package/src/registry/ui/form-field.tsx +91 -94
- package/src/registry/ui/google-analytics.tsx +38 -0
- package/src/registry/ui/google-tag-manager.tsx +64 -0
- package/src/registry/ui/hover-card.tsx +223 -223
- package/src/registry/ui/image.tsx +144 -147
- package/src/registry/ui/input-group.tsx +82 -85
- package/src/registry/ui/input.tsx +125 -125
- package/src/registry/ui/kbd.tsx +60 -63
- package/src/registry/ui/label.tsx +36 -37
- package/src/registry/ui/loading-spinner.tsx +108 -111
- package/src/registry/ui/map.tsx +0 -0
- package/src/registry/ui/marquee.tsx +2 -0
- package/src/registry/ui/menubar.tsx +246 -246
- package/src/registry/ui/meta-pixel.tsx +46 -0
- package/src/registry/ui/microsoft-clarity.tsx +33 -0
- package/src/registry/ui/native-select.tsx +49 -52
- package/src/registry/ui/otp-input.tsx +152 -155
- package/src/registry/ui/pagination.tsx +149 -152
- package/src/registry/ui/patterns.tsx +28 -0
- package/src/registry/ui/popover.tsx +226 -227
- package/src/registry/ui/progress.tsx +51 -52
- package/src/registry/ui/radio.tsx +99 -102
- package/src/registry/ui/resizable.tsx +314 -314
- package/src/registry/ui/scroll-animation.tsx +45 -0
- package/src/registry/ui/scroll-area.tsx +121 -122
- package/src/registry/ui/scroll-to-top.tsx +0 -0
- package/src/registry/ui/search.tsx +147 -150
- package/src/registry/ui/select.tsx +292 -293
- package/src/registry/ui/separator.tsx +46 -47
- package/src/registry/ui/sheet.tsx +6 -3
- package/src/registry/ui/sidebar.tsx +628 -628
- package/src/registry/ui/skeleton.tsx +26 -29
- package/src/registry/ui/slider.tsx +196 -197
- package/src/registry/ui/slot.tsx +69 -72
- package/src/registry/ui/star-rating.tsx +131 -134
- package/src/registry/ui/switch.tsx +72 -73
- package/src/registry/ui/table-of-contents.tsx +96 -96
- package/src/registry/ui/table.tsx +138 -139
- package/src/registry/ui/tabs.tsx +124 -125
- package/src/registry/ui/text.tsx +61 -64
- package/src/registry/ui/textarea.tsx +41 -42
- package/src/registry/ui/theme-switcher.tsx +66 -66
- package/src/registry/ui/tiktok-pixel.tsx +36 -0
- package/src/registry/ui/toast.tsx +97 -98
- package/src/registry/ui/toggle-group.tsx +129 -129
- package/src/registry/ui/toggle.tsx +72 -72
- package/src/registry/ui/tooltip.tsx +143 -144
- package/src/registry/ui/whatsapp.tsx +0 -0
|
@@ -1,42 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
export interface TextareaProps
|
|
7
|
-
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
8
|
-
/**
|
|
9
|
-
* Whether the textarea is in an error state
|
|
10
|
-
*/
|
|
11
|
-
error?: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Textarea component for multi-line text input
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* <Textarea placeholder="Enter your message" />
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* <Textarea error placeholder="Required field" />
|
|
22
|
-
*/
|
|
23
|
-
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
24
|
-
({ className, error, ...props }, ref) => {
|
|
25
|
-
return (
|
|
26
|
-
<textarea
|
|
27
|
-
className={cn(
|
|
28
|
-
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
29
|
-
error && "border-destructive focus-visible:ring-destructive",
|
|
30
|
-
className
|
|
31
|
-
)}
|
|
32
|
-
ref={ref}
|
|
33
|
-
aria-invalid={error ? "true" : undefined}
|
|
34
|
-
{...props}
|
|
35
|
-
/>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
)
|
|
39
|
-
Textarea.displayName = "Textarea"
|
|
40
|
-
|
|
41
|
-
export { Textarea }
|
|
42
|
-
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
export interface TextareaProps
|
|
7
|
+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
8
|
+
/**
|
|
9
|
+
* Whether the textarea is in an error state
|
|
10
|
+
*/
|
|
11
|
+
error?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Textarea component for multi-line text input
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* <Textarea placeholder="Enter your message" />
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <Textarea error placeholder="Required field" />
|
|
22
|
+
*/
|
|
23
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
24
|
+
({ className, error, ...props }, ref) => {
|
|
25
|
+
return (
|
|
26
|
+
<textarea
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
29
|
+
error && "border-destructive focus-visible:ring-destructive",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
ref={ref}
|
|
33
|
+
aria-invalid={error ? "true" : undefined}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
Textarea.displayName = "Textarea"
|
|
40
|
+
|
|
41
|
+
export { Textarea }
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { useTheme } from "next-themes"
|
|
5
|
-
import { FiSun, FiMoon, FiMonitor } from "react-icons/fi"
|
|
6
|
-
|
|
7
|
-
import { DropdownMenuItem } from "@/components/ui/dropdown-menu"
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* ThemeSwitcher component for use within a DropdownMenu
|
|
11
|
-
* Toggles between light, dark, and system themes
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* <DropdownMenu>
|
|
15
|
-
* <DropdownMenuContent>
|
|
16
|
-
* <ThemeSwitcher />
|
|
17
|
-
* </DropdownMenuContent>
|
|
18
|
-
* </DropdownMenu>
|
|
19
|
-
*/
|
|
20
|
-
export function ThemeSwitcher() {
|
|
21
|
-
const { setTheme, theme, resolvedTheme } = useTheme()
|
|
22
|
-
const [mounted, setMounted] = React.useState(false)
|
|
23
|
-
|
|
24
|
-
// Avoid hydration mismatch
|
|
25
|
-
React.useEffect(() => {
|
|
26
|
-
setMounted(true)
|
|
27
|
-
}, [])
|
|
28
|
-
|
|
29
|
-
const toggleTheme = () => {
|
|
30
|
-
// Cycle: light -> dark -> system -> light
|
|
31
|
-
if (theme === "light") {
|
|
32
|
-
setTheme("dark")
|
|
33
|
-
} else if (theme === "dark") {
|
|
34
|
-
setTheme("system")
|
|
35
|
-
} else {
|
|
36
|
-
setTheme("light")
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const getIcon = () => {
|
|
41
|
-
if (!mounted) {
|
|
42
|
-
return <FiSun className="mr-2 h-4 w-4" />
|
|
43
|
-
}
|
|
44
|
-
if (theme === "system") {
|
|
45
|
-
return <FiMonitor className="mr-2 h-4 w-4" />
|
|
46
|
-
}
|
|
47
|
-
if (resolvedTheme === "dark") {
|
|
48
|
-
return <FiMoon className="mr-2 h-4 w-4" />
|
|
49
|
-
}
|
|
50
|
-
return <FiSun className="mr-2 h-4 w-4" />
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const getLabel = () => {
|
|
54
|
-
if (!mounted) return "Theme"
|
|
55
|
-
if (theme === "system") return "System"
|
|
56
|
-
if (theme === "dark") return "Dark"
|
|
57
|
-
return "Light"
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<DropdownMenuItem onClick={toggleTheme} closeOnSelect={false}>
|
|
62
|
-
{getIcon()}
|
|
63
|
-
<span>Theme: {getLabel()}</span>
|
|
64
|
-
</DropdownMenuItem>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { useTheme } from "next-themes"
|
|
5
|
+
import { FiSun, FiMoon, FiMonitor } from "react-icons/fi"
|
|
6
|
+
|
|
7
|
+
import { DropdownMenuItem } from "@/components/ui/dropdown-menu"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ThemeSwitcher component for use within a DropdownMenu
|
|
11
|
+
* Toggles between light, dark, and system themes
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <DropdownMenu>
|
|
15
|
+
* <DropdownMenuContent>
|
|
16
|
+
* <ThemeSwitcher />
|
|
17
|
+
* </DropdownMenuContent>
|
|
18
|
+
* </DropdownMenu>
|
|
19
|
+
*/
|
|
20
|
+
export function ThemeSwitcher() {
|
|
21
|
+
const { setTheme, theme, resolvedTheme } = useTheme()
|
|
22
|
+
const [mounted, setMounted] = React.useState(false)
|
|
23
|
+
|
|
24
|
+
// Avoid hydration mismatch
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
setMounted(true)
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
const toggleTheme = () => {
|
|
30
|
+
// Cycle: light -> dark -> system -> light
|
|
31
|
+
if (theme === "light") {
|
|
32
|
+
setTheme("dark")
|
|
33
|
+
} else if (theme === "dark") {
|
|
34
|
+
setTheme("system")
|
|
35
|
+
} else {
|
|
36
|
+
setTheme("light")
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getIcon = () => {
|
|
41
|
+
if (!mounted) {
|
|
42
|
+
return <FiSun className="mr-2 h-4 w-4" />
|
|
43
|
+
}
|
|
44
|
+
if (theme === "system") {
|
|
45
|
+
return <FiMonitor className="mr-2 h-4 w-4" />
|
|
46
|
+
}
|
|
47
|
+
if (resolvedTheme === "dark") {
|
|
48
|
+
return <FiMoon className="mr-2 h-4 w-4" />
|
|
49
|
+
}
|
|
50
|
+
return <FiSun className="mr-2 h-4 w-4" />
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const getLabel = () => {
|
|
54
|
+
if (!mounted) return "Theme"
|
|
55
|
+
if (theme === "system") return "System"
|
|
56
|
+
if (theme === "dark") return "Dark"
|
|
57
|
+
return "Light"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<DropdownMenuItem onClick={toggleTheme} closeOnSelect={false}>
|
|
62
|
+
{getIcon()}
|
|
63
|
+
<span>Theme: {getLabel()}</span>
|
|
64
|
+
</DropdownMenuItem>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import Script from 'next/script';
|
|
4
|
+
import type { FC } from 'react';
|
|
5
|
+
|
|
6
|
+
interface TikTokPixelProps {
|
|
7
|
+
pixelIds: string[]; // ← array
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const TikTokPixel: FC<TikTokPixelProps> = ({ pixelIds }) => {
|
|
11
|
+
return (
|
|
12
|
+
<Script
|
|
13
|
+
id="tiktok-script-multi"
|
|
14
|
+
strategy="afterInteractive"
|
|
15
|
+
dangerouslySetInnerHTML={{
|
|
16
|
+
__html: `
|
|
17
|
+
!function (w, d, t) {
|
|
18
|
+
w.TiktokAnalyticsObject=t;var ttq=w[t]=w[t]||[];
|
|
19
|
+
ttq.methods=["page","track","identify","instances","debug","on","off","once","ready","alias","group","enableCookie","disableCookie","holdConsent","revokeConsent","grantConsent"],
|
|
20
|
+
ttq.setAndDefer=function(t,e){t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}};
|
|
21
|
+
for(var i=0;i<ttq.methods.length;i++)ttq.setAndDefer(ttq,ttq.methods[i]);
|
|
22
|
+
ttq.instance=function(t){for(var e=ttq._i[t]||[],n=0;n<ttq.methods.length;n++)ttq.setAndDefer(e,ttq.methods[n]);return e},
|
|
23
|
+
ttq.load=function(e,n){var r="https://analytics.tiktok.com/i18n/pixel/events.js";
|
|
24
|
+
ttq._i=ttq._i||{},ttq._i[e]=[],ttq._i[e]._u=r,ttq._t=ttq._t||{},ttq._t[e]=+new Date,ttq._o=ttq._o||{},ttq._o[e]=n||{};
|
|
25
|
+
var s=document.createElement("script");s.type="text/javascript",s.async=!0,s.src=r+"?sdkid="+e+"&lib="+t;
|
|
26
|
+
var p=document.getElementsByTagName("script")[0];p.parentNode.insertBefore(s,p)};
|
|
27
|
+
${pixelIds.map(id => `ttq.load('${id}');`).join('\n')}
|
|
28
|
+
ttq.page();
|
|
29
|
+
}(window, document, 'ttq');
|
|
30
|
+
`,
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default TikTokPixel;
|
|
@@ -1,98 +1,97 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
|
|
7
|
-
const toastVariants = cva(
|
|
8
|
-
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "border bg-background text-foreground",
|
|
13
|
-
destructive:
|
|
14
|
-
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
defaultVariants: {
|
|
18
|
-
variant: "default",
|
|
19
|
-
},
|
|
20
|
-
}
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
interface ToastProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof toastVariants> {
|
|
24
|
-
onClose?: () => void
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Toast notification component
|
|
29
|
-
*
|
|
30
|
-
* For a complete toast system, use with a ToastProvider context.
|
|
31
|
-
* This is the individual toast component.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* <Toast variant="default">
|
|
35
|
-
* <ToastTitle>Success</ToastTitle>
|
|
36
|
-
* <ToastDescription>Your action was completed.</ToastDescription>
|
|
37
|
-
* </Toast>
|
|
38
|
-
*/
|
|
39
|
-
const Toast = React.forwardRef<HTMLDivElement, ToastProps>(
|
|
40
|
-
({ className, variant, onClose, children, ...props }, ref) => {
|
|
41
|
-
return (
|
|
42
|
-
<div
|
|
43
|
-
ref={ref}
|
|
44
|
-
role={variant === "destructive" ? "alert" : "status"}
|
|
45
|
-
aria-live={variant === "destructive" ? "assertive" : "polite"}
|
|
46
|
-
className={cn(toastVariants({ variant }), className)}
|
|
47
|
-
{...props}
|
|
48
|
-
>
|
|
49
|
-
<div className="grid gap-1">{children}</div>
|
|
50
|
-
{onClose && (
|
|
51
|
-
<button
|
|
52
|
-
className="absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100"
|
|
53
|
-
onClick={onClose}
|
|
54
|
-
>
|
|
55
|
-
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
56
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
57
|
-
</svg>
|
|
58
|
-
</button>
|
|
59
|
-
)}
|
|
60
|
-
</div>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
)
|
|
64
|
-
Toast.displayName = "Toast"
|
|
65
|
-
|
|
66
|
-
const ToastTitle = React.forwardRef<
|
|
67
|
-
HTMLDivElement,
|
|
68
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
69
|
-
>(({ className, ...props }, ref) => (
|
|
70
|
-
<div ref={ref} className={cn("text-sm font-semibold [&+div]:text-xs", className)} {...props} />
|
|
71
|
-
))
|
|
72
|
-
ToastTitle.displayName = "ToastTitle"
|
|
73
|
-
|
|
74
|
-
const ToastDescription = React.forwardRef<
|
|
75
|
-
HTMLDivElement,
|
|
76
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
77
|
-
>(({ className, ...props }, ref) => (
|
|
78
|
-
<div ref={ref} className={cn("text-sm opacity-90", className)} {...props} />
|
|
79
|
-
))
|
|
80
|
-
ToastDescription.displayName = "ToastDescription"
|
|
81
|
-
|
|
82
|
-
const ToastAction = React.forwardRef<
|
|
83
|
-
HTMLButtonElement,
|
|
84
|
-
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
85
|
-
>(({ className, ...props }, ref) => (
|
|
86
|
-
<button
|
|
87
|
-
ref={ref}
|
|
88
|
-
className={cn(
|
|
89
|
-
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
90
|
-
className
|
|
91
|
-
)}
|
|
92
|
-
{...props}
|
|
93
|
-
/>
|
|
94
|
-
))
|
|
95
|
-
ToastAction.displayName = "ToastAction"
|
|
96
|
-
|
|
97
|
-
export { Toast, ToastTitle, ToastDescription, ToastAction, toastVariants }
|
|
98
|
-
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const toastVariants = cva(
|
|
8
|
+
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "border bg-background text-foreground",
|
|
13
|
+
destructive:
|
|
14
|
+
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: "default",
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
interface ToastProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof toastVariants> {
|
|
24
|
+
onClose?: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Toast notification component
|
|
29
|
+
*
|
|
30
|
+
* For a complete toast system, use with a ToastProvider context.
|
|
31
|
+
* This is the individual toast component.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* <Toast variant="default">
|
|
35
|
+
* <ToastTitle>Success</ToastTitle>
|
|
36
|
+
* <ToastDescription>Your action was completed.</ToastDescription>
|
|
37
|
+
* </Toast>
|
|
38
|
+
*/
|
|
39
|
+
const Toast = React.forwardRef<HTMLDivElement, ToastProps>(
|
|
40
|
+
({ className, variant, onClose, children, ...props }, ref) => {
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
ref={ref}
|
|
44
|
+
role={variant === "destructive" ? "alert" : "status"}
|
|
45
|
+
aria-live={variant === "destructive" ? "assertive" : "polite"}
|
|
46
|
+
className={cn(toastVariants({ variant }), className)}
|
|
47
|
+
{...props}
|
|
48
|
+
>
|
|
49
|
+
<div className="grid gap-1">{children}</div>
|
|
50
|
+
{onClose && (
|
|
51
|
+
<button
|
|
52
|
+
className="absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100"
|
|
53
|
+
onClick={onClose}
|
|
54
|
+
>
|
|
55
|
+
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
56
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
57
|
+
</svg>
|
|
58
|
+
</button>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
Toast.displayName = "Toast"
|
|
65
|
+
|
|
66
|
+
const ToastTitle = React.forwardRef<
|
|
67
|
+
HTMLDivElement,
|
|
68
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
69
|
+
>(({ className, ...props }, ref) => (
|
|
70
|
+
<div ref={ref} className={cn("text-sm font-semibold [&+div]:text-xs", className)} {...props} />
|
|
71
|
+
))
|
|
72
|
+
ToastTitle.displayName = "ToastTitle"
|
|
73
|
+
|
|
74
|
+
const ToastDescription = React.forwardRef<
|
|
75
|
+
HTMLDivElement,
|
|
76
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
77
|
+
>(({ className, ...props }, ref) => (
|
|
78
|
+
<div ref={ref} className={cn("text-sm opacity-90", className)} {...props} />
|
|
79
|
+
))
|
|
80
|
+
ToastDescription.displayName = "ToastDescription"
|
|
81
|
+
|
|
82
|
+
const ToastAction = React.forwardRef<
|
|
83
|
+
HTMLButtonElement,
|
|
84
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
85
|
+
>(({ className, ...props }, ref) => (
|
|
86
|
+
<button
|
|
87
|
+
ref={ref}
|
|
88
|
+
className={cn(
|
|
89
|
+
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
90
|
+
className
|
|
91
|
+
)}
|
|
92
|
+
{...props}
|
|
93
|
+
/>
|
|
94
|
+
))
|
|
95
|
+
ToastAction.displayName = "ToastAction"
|
|
96
|
+
|
|
97
|
+
export { Toast, ToastTitle, ToastDescription, ToastAction, toastVariants }
|