@moontra/moonui 2.0.7 → 2.0.9
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/package.json +26 -19
- package/src/__tests__/use-intersection-observer.test.tsx +216 -0
- package/src/__tests__/use-local-storage.test.tsx +174 -0
- package/src/__tests__/use-pro-access.test.tsx +183 -0
- package/src/components/ui/__tests__/accordion.test.tsx +571 -0
- package/src/components/ui/__tests__/alert.test.tsx +484 -0
- package/src/components/ui/__tests__/aspect-ratio.test.tsx +492 -0
- package/src/components/ui/__tests__/avatar.test.tsx +382 -0
- package/src/components/ui/__tests__/badge.test.tsx +364 -0
- package/src/components/ui/__tests__/breadcrumb.test.tsx +687 -0
- package/src/components/ui/__tests__/button.test.tsx +91 -0
- package/src/components/ui/__tests__/card.test.tsx +104 -0
- package/src/components/ui/__tests__/checkbox.test.tsx +591 -0
- package/src/components/ui/__tests__/collapsible.test.tsx +592 -0
- package/src/components/ui/__tests__/color-picker.test.tsx +365 -0
- package/src/components/ui/__tests__/command.test.tsx +677 -0
- package/src/components/ui/__tests__/data-table.test.tsx +256 -0
- package/src/components/ui/__tests__/date-picker.test.tsx +320 -0
- package/src/components/ui/__tests__/dialog.test.tsx +764 -0
- package/src/components/ui/__tests__/input.test.tsx +318 -0
- package/src/components/ui/__tests__/label.test.tsx +103 -0
- package/src/components/ui/__tests__/popover.test.tsx +637 -0
- package/src/components/ui/__tests__/progress.test.tsx +382 -0
- package/src/components/ui/__tests__/radio-group.test.tsx +555 -0
- package/src/components/ui/__tests__/select.test.tsx +705 -0
- package/src/components/ui/__tests__/skeleton.test.tsx +253 -0
- package/src/components/ui/__tests__/slider.test.tsx +493 -0
- package/src/components/ui/__tests__/switch.test.tsx +372 -0
- package/src/components/ui/__tests__/table.test.tsx +824 -0
- package/src/components/ui/__tests__/tabs.test.tsx +887 -0
- package/src/components/ui/__tests__/toast.test.tsx +458 -0
- package/src/components/ui/__tests__/tooltip.test.tsx +583 -0
- package/src/components/ui/accordion.tsx +63 -0
- package/src/components/ui/alert.tsx +130 -0
- package/src/components/ui/aspect-ratio.tsx +72 -0
- package/src/components/ui/avatar.tsx +135 -0
- package/src/components/ui/badge.tsx +225 -0
- package/src/components/ui/breadcrumb.tsx +207 -0
- package/src/components/ui/button.stories.tsx +162 -0
- package/src/components/ui/button.tsx +221 -0
- package/src/components/ui/calendar.tsx +262 -0
- package/src/components/ui/card.stories.tsx +208 -0
- package/src/components/ui/card.tsx +141 -0
- package/src/components/ui/checkbox.tsx +256 -0
- package/src/components/ui/collapsible.tsx +129 -0
- package/src/components/ui/color-picker.tsx +664 -0
- package/src/components/ui/command.tsx +218 -0
- package/src/components/ui/data-table.stories.tsx +509 -0
- package/src/components/ui/data-table.tsx +623 -0
- package/src/components/ui/date-picker.stories.tsx +321 -0
- package/src/components/ui/date-picker.tsx +603 -0
- package/src/components/ui/dialog.tsx +332 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/file-upload.tsx +400 -0
- package/src/components/ui/github-stars.tsx +201 -0
- package/src/components/ui/index.ts +12 -0
- package/src/components/ui/input.stories.tsx +255 -0
- package/src/components/ui/input.tsx +219 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/locked-component.tsx +215 -0
- package/src/components/ui/moon-logo.tsx +97 -0
- package/src/components/ui/pagination.tsx +116 -0
- package/src/components/ui/popover.tsx +183 -0
- package/src/components/ui/progress.tsx +182 -0
- package/src/components/ui/radio-group.tsx +243 -0
- package/src/components/ui/select.tsx +273 -0
- package/src/components/ui/separator.tsx +140 -0
- package/src/components/ui/simple-editor.tsx +652 -0
- package/src/components/ui/skeleton.tsx +351 -0
- package/src/components/ui/slider.tsx +351 -0
- package/src/components/ui/switch.tsx +83 -0
- package/src/components/ui/table.tsx +322 -0
- package/src/components/ui/tabs.tsx +195 -0
- package/src/components/ui/textarea.tsx +46 -0
- package/src/components/ui/toast.tsx +313 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +152 -0
- package/src/docs/theme-system.md +1194 -0
- package/src/index.tsx +39 -0
- package/src/lib/micro-interactions.ts +255 -0
- package/src/lib/theme.tsx +398 -0
- package/src/lib/utils.ts +69 -0
- package/src/styles/design-system.css +365 -0
- package/src/styles/index.css +4 -0
- package/src/styles/tailwind.css +6 -0
- package/src/styles/tokens.css +453 -0
- package/src/use-intersection-observer.tsx +154 -0
- package/src/use-local-storage.tsx +71 -0
- package/src/use-paddle.ts +138 -0
- package/src/use-performance-optimizer.ts +379 -0
- package/src/use-pro-access.ts +141 -0
- package/src/use-scroll-animation.ts +221 -0
- package/src/use-subscription.ts +37 -0
- package/src/use-toast.ts +32 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { AlertCircle, AlertTriangle, Info, Check, X } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../lib/utils";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alert Component
|
|
11
|
+
*
|
|
12
|
+
* Yüksek kaliteli, özelleştirilebilir ve erişilebilir alert bileşeni.
|
|
13
|
+
* Bildirim, uyarı ve dikkat çekmek gereken içerikler için kullanılır.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const alertVariants = cva(
|
|
17
|
+
"relative w-full flex items-center gap-3 p-4 border text-foreground [&>svg]:shrink-0",
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default: "bg-background text-foreground border-border",
|
|
22
|
+
primary: "bg-primary/10 text-primary border-primary/30",
|
|
23
|
+
success: "bg-success/10 text-success border-success/30",
|
|
24
|
+
warning: "bg-warning/10 text-warning border-warning/30",
|
|
25
|
+
error: "bg-destructive/10 text-destructive border-destructive/30",
|
|
26
|
+
info: "bg-blue-500/10 text-blue-500 border-blue-500/30",
|
|
27
|
+
},
|
|
28
|
+
size: {
|
|
29
|
+
sm: "py-2 text-xs",
|
|
30
|
+
default: "py-3 text-sm",
|
|
31
|
+
lg: "py-4 text-base",
|
|
32
|
+
},
|
|
33
|
+
radius: {
|
|
34
|
+
none: "rounded-none",
|
|
35
|
+
sm: "rounded-sm",
|
|
36
|
+
default: "rounded-md",
|
|
37
|
+
lg: "rounded-lg",
|
|
38
|
+
full: "rounded-full",
|
|
39
|
+
},
|
|
40
|
+
withClose: {
|
|
41
|
+
true: "pr-10",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
defaultVariants: {
|
|
45
|
+
variant: "default",
|
|
46
|
+
size: "default",
|
|
47
|
+
radius: "default",
|
|
48
|
+
withClose: false,
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
export interface AlertProps
|
|
54
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
55
|
+
VariantProps<typeof alertVariants> {
|
|
56
|
+
/** Alert ikonunu gizler */
|
|
57
|
+
hideIcon?: boolean;
|
|
58
|
+
/** Kapatma butonu ekler */
|
|
59
|
+
closable?: boolean;
|
|
60
|
+
/** Kapatma butonu tıklandığında çalışacak fonksiyon */
|
|
61
|
+
onClose?: () => void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
|
65
|
+
({ className, variant = "default", size, radius, hideIcon = false, closable = false, onClose, children, ...props }, ref) => {
|
|
66
|
+
// Alert türüne göre ikon belirleme
|
|
67
|
+
const Icon = React.useMemo(() => {
|
|
68
|
+
switch (variant) {
|
|
69
|
+
case "success":
|
|
70
|
+
return Check;
|
|
71
|
+
case "warning":
|
|
72
|
+
return AlertTriangle;
|
|
73
|
+
case "error":
|
|
74
|
+
return AlertCircle;
|
|
75
|
+
case "info":
|
|
76
|
+
return Info;
|
|
77
|
+
default:
|
|
78
|
+
return Info;
|
|
79
|
+
}
|
|
80
|
+
}, [variant]);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
ref={ref}
|
|
85
|
+
role="alert"
|
|
86
|
+
className={cn(alertVariants({ variant, size, radius, withClose: closable }), className)}
|
|
87
|
+
{...props}
|
|
88
|
+
>
|
|
89
|
+
{!hideIcon && <Icon className="h-5 w-5" />}
|
|
90
|
+
<div className="flex-1">{children}</div>
|
|
91
|
+
{closable && onClose && (
|
|
92
|
+
<button
|
|
93
|
+
onClick={onClose}
|
|
94
|
+
className="absolute right-3 top-3 inline-flex h-6 w-6 items-center justify-center rounded-full opacity-70 transition-opacity hover:opacity-100"
|
|
95
|
+
aria-label="Kapat"
|
|
96
|
+
>
|
|
97
|
+
<X className="h-4 w-4" />
|
|
98
|
+
</button>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
Alert.displayName = "Alert";
|
|
105
|
+
|
|
106
|
+
const AlertTitle = React.forwardRef<
|
|
107
|
+
HTMLParagraphElement,
|
|
108
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
109
|
+
>(({ className, ...props }, ref) => (
|
|
110
|
+
<h5
|
|
111
|
+
ref={ref}
|
|
112
|
+
className={cn("font-semibold leading-tight tracking-tight mb-1", className)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
));
|
|
116
|
+
AlertTitle.displayName = "AlertTitle";
|
|
117
|
+
|
|
118
|
+
const AlertDescription = React.forwardRef<
|
|
119
|
+
HTMLParagraphElement,
|
|
120
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
121
|
+
>(({ className, ...props }, ref) => (
|
|
122
|
+
<div
|
|
123
|
+
ref={ref}
|
|
124
|
+
className={cn("text-sm leading-5 text-muted-foreground", className)}
|
|
125
|
+
{...props}
|
|
126
|
+
/>
|
|
127
|
+
));
|
|
128
|
+
AlertDescription.displayName = "AlertDescription";
|
|
129
|
+
|
|
130
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Aspect Ratio Component
|
|
10
|
+
*
|
|
11
|
+
* A component that maintains a specific aspect ratio for content.
|
|
12
|
+
* Particularly useful for images, videos, and media content in responsive designs.
|
|
13
|
+
* Fully integrated with the theme system, accessible and customizable.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const aspectRatioVariants = cva(
|
|
17
|
+
"relative overflow-hidden",
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default: "rounded-md bg-muted/10",
|
|
22
|
+
ghost: "bg-transparent",
|
|
23
|
+
outline: "rounded-md border border-border",
|
|
24
|
+
card: "rounded-md bg-card shadow-sm",
|
|
25
|
+
},
|
|
26
|
+
radius: {
|
|
27
|
+
none: "rounded-none",
|
|
28
|
+
sm: "rounded-sm",
|
|
29
|
+
md: "rounded-md",
|
|
30
|
+
lg: "rounded-lg",
|
|
31
|
+
full: "rounded-full",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
variant: "default",
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
interface AspectRatioProps
|
|
41
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
42
|
+
VariantProps<typeof aspectRatioVariants> {
|
|
43
|
+
/**
|
|
44
|
+
* Aspect ratio (width/height). For example 16/9, 4/3, 1/1 etc.
|
|
45
|
+
* @default 16/9
|
|
46
|
+
*/
|
|
47
|
+
ratio?: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const AspectRatio = React.forwardRef<
|
|
51
|
+
HTMLDivElement,
|
|
52
|
+
AspectRatioProps
|
|
53
|
+
>(({ className, variant, radius, ratio = 16 / 9, style, children, ...props }, ref) => (
|
|
54
|
+
<div
|
|
55
|
+
ref={ref}
|
|
56
|
+
className={cn(aspectRatioVariants({ variant, radius }), className)}
|
|
57
|
+
style={{
|
|
58
|
+
position: "relative",
|
|
59
|
+
paddingBottom: `${(1 / ratio) * 100}%`,
|
|
60
|
+
...style
|
|
61
|
+
}}
|
|
62
|
+
{...props}
|
|
63
|
+
>
|
|
64
|
+
<div className="absolute inset-0">
|
|
65
|
+
{children}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
))
|
|
69
|
+
AspectRatio.displayName = "AspectRatio"
|
|
70
|
+
|
|
71
|
+
export { AspectRatio, aspectRatioVariants }
|
|
72
|
+
export type { AspectRatioProps }
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
const avatarVariants = cva(
|
|
8
|
+
"relative flex shrink-0 overflow-hidden",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
default: "h-10 w-10",
|
|
13
|
+
xs: "h-6 w-6",
|
|
14
|
+
sm: "h-8 w-8",
|
|
15
|
+
md: "h-10 w-10",
|
|
16
|
+
lg: "h-12 w-12",
|
|
17
|
+
xl: "h-16 w-16",
|
|
18
|
+
"2xl": "h-20 w-20",
|
|
19
|
+
},
|
|
20
|
+
radius: {
|
|
21
|
+
default: "rounded-full",
|
|
22
|
+
sm: "rounded-md",
|
|
23
|
+
lg: "rounded-xl",
|
|
24
|
+
full: "rounded-full",
|
|
25
|
+
none: "rounded-none",
|
|
26
|
+
},
|
|
27
|
+
variant: {
|
|
28
|
+
default: "",
|
|
29
|
+
ring: "ring-2 ring-gray-300 dark:ring-gray-600",
|
|
30
|
+
ringOffset: "ring-2 ring-gray-300 dark:ring-gray-600 ring-offset-2 ring-offset-background dark:ring-offset-gray-950",
|
|
31
|
+
border: "border-2 border-gray-200 dark:border-gray-800"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
size: "default",
|
|
36
|
+
radius: "default",
|
|
37
|
+
variant: "default"
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export interface AvatarProps
|
|
43
|
+
extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,
|
|
44
|
+
VariantProps<typeof avatarVariants> {}
|
|
45
|
+
|
|
46
|
+
const Avatar = React.forwardRef<
|
|
47
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
48
|
+
AvatarProps
|
|
49
|
+
>(({ className, size, radius, variant, ...props }, ref) => (
|
|
50
|
+
<AvatarPrimitive.Root
|
|
51
|
+
ref={ref}
|
|
52
|
+
className={cn(avatarVariants({ size, radius, variant }), className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
));
|
|
56
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
57
|
+
|
|
58
|
+
const AvatarImage = React.forwardRef<
|
|
59
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
60
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
61
|
+
>(({ className, ...props }, ref) => (
|
|
62
|
+
<AvatarPrimitive.Image
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={cn("aspect-square h-full w-full", className)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
));
|
|
68
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
69
|
+
|
|
70
|
+
const AvatarFallback = React.forwardRef<
|
|
71
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
72
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
73
|
+
>(({ className, ...props }, ref) => (
|
|
74
|
+
<AvatarPrimitive.Fallback
|
|
75
|
+
ref={ref}
|
|
76
|
+
className={cn(
|
|
77
|
+
"flex h-full w-full items-center justify-center bg-muted",
|
|
78
|
+
className
|
|
79
|
+
)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
));
|
|
83
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
84
|
+
|
|
85
|
+
// Avatar Group Component for displaying multiple avatars
|
|
86
|
+
interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
87
|
+
limit?: number;
|
|
88
|
+
avatars: React.ReactNode[];
|
|
89
|
+
overlapOffset?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
|
|
93
|
+
({ className, limit, avatars, overlapOffset = -8, ...props }, ref) => {
|
|
94
|
+
const visibleAvatars = limit ? avatars.slice(0, limit) : avatars;
|
|
95
|
+
const remainingCount = limit ? Math.max(0, avatars.length - limit) : 0;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
ref={ref}
|
|
100
|
+
className={cn("flex items-center", className)}
|
|
101
|
+
{...props}
|
|
102
|
+
>
|
|
103
|
+
<div className="flex">
|
|
104
|
+
{visibleAvatars.map((avatar, index) => (
|
|
105
|
+
<div
|
|
106
|
+
key={index}
|
|
107
|
+
className="relative"
|
|
108
|
+
style={{
|
|
109
|
+
marginLeft: index === 0 ? 0 : `${overlapOffset}px`,
|
|
110
|
+
zIndex: visibleAvatars.length - index
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{avatar}
|
|
114
|
+
</div>
|
|
115
|
+
))}
|
|
116
|
+
{remainingCount > 0 && (
|
|
117
|
+
<div
|
|
118
|
+
className="relative z-0"
|
|
119
|
+
style={{ marginLeft: `${overlapOffset}px` }}
|
|
120
|
+
>
|
|
121
|
+
<Avatar variant="border">
|
|
122
|
+
<AvatarFallback>
|
|
123
|
+
+{remainingCount}
|
|
124
|
+
</AvatarFallback>
|
|
125
|
+
</Avatar>
|
|
126
|
+
</div>
|
|
127
|
+
)}
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
AvatarGroup.displayName = "AvatarGroup";
|
|
134
|
+
|
|
135
|
+
export { Avatar, AvatarImage, AvatarFallback, AvatarGroup };
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Premium Badge Component
|
|
7
|
+
*
|
|
8
|
+
* Durum, kategori ve etiketleme için yüksek kaliteli badge bileşeni.
|
|
9
|
+
* Dark ve light modda uyumlu, erişilebilir ve çeşitli varyantlar sunar.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const badgeVariants = cva(
|
|
13
|
+
["inline-flex items-center gap-1.5",
|
|
14
|
+
"font-medium transition-colors duration-200",
|
|
15
|
+
"border focus:outline-none",
|
|
16
|
+
"focus-visible:ring-2 focus-visible:ring-offset-1"],
|
|
17
|
+
{
|
|
18
|
+
variants: {
|
|
19
|
+
variant: {
|
|
20
|
+
primary: [
|
|
21
|
+
"border-transparent bg-primary text-primary-foreground",
|
|
22
|
+
"hover:bg-primary-hover dark:hover:bg-primary/90",
|
|
23
|
+
"focus-visible:ring-primary/30 dark:focus-visible:ring-primary/40",
|
|
24
|
+
"dark:bg-primary/90 dark:text-white dark:shadow-inner dark:shadow-primary/10",
|
|
25
|
+
],
|
|
26
|
+
secondary: [
|
|
27
|
+
"border-transparent bg-secondary text-secondary-foreground",
|
|
28
|
+
"hover:bg-gray-200 dark:hover:bg-gray-700/90",
|
|
29
|
+
"focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
|
|
30
|
+
"dark:bg-gray-800 dark:text-gray-100 dark:shadow-inner dark:shadow-gray-950/10",
|
|
31
|
+
],
|
|
32
|
+
outline: [
|
|
33
|
+
"border-gray-200 dark:border-gray-700",
|
|
34
|
+
"bg-transparent text-foreground",
|
|
35
|
+
"hover:border-gray-300 dark:hover:border-gray-600",
|
|
36
|
+
"focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
|
|
37
|
+
"dark:text-gray-200 dark:bg-transparent dark:backdrop-blur-sm",
|
|
38
|
+
],
|
|
39
|
+
destructive: [
|
|
40
|
+
"border-transparent bg-error text-white",
|
|
41
|
+
"hover:bg-error/90 dark:hover:bg-error/80",
|
|
42
|
+
"focus-visible:ring-error/30 dark:focus-visible:ring-error/25",
|
|
43
|
+
"dark:bg-error/90 dark:text-white dark:shadow-inner dark:shadow-error/10",
|
|
44
|
+
],
|
|
45
|
+
success: [
|
|
46
|
+
"border-transparent bg-success text-white",
|
|
47
|
+
"hover:bg-success/90 dark:hover:bg-success/80",
|
|
48
|
+
"focus-visible:ring-success/30 dark:focus-visible:ring-success/25",
|
|
49
|
+
"dark:bg-success/90 dark:text-white dark:shadow-inner dark:shadow-success/10",
|
|
50
|
+
],
|
|
51
|
+
warning: [
|
|
52
|
+
"border-transparent bg-warning text-white",
|
|
53
|
+
"hover:bg-warning/90 dark:hover:bg-warning/80",
|
|
54
|
+
"focus-visible:ring-warning/30 dark:focus-visible:ring-warning/25",
|
|
55
|
+
"dark:bg-warning/90 dark:text-gray-900 dark:shadow-inner dark:shadow-warning/10",
|
|
56
|
+
],
|
|
57
|
+
ghost: [
|
|
58
|
+
"border-transparent bg-transparent text-foreground",
|
|
59
|
+
"hover:bg-gray-100 dark:hover:bg-gray-800/80",
|
|
60
|
+
"focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
|
|
61
|
+
"dark:text-gray-200 dark:backdrop-blur-sm",
|
|
62
|
+
"transition-colors duration-200",
|
|
63
|
+
],
|
|
64
|
+
pro: [
|
|
65
|
+
"border-transparent bg-gradient-to-r from-purple-600 to-pink-600 text-white",
|
|
66
|
+
"hover:from-purple-700 hover:to-pink-700",
|
|
67
|
+
"focus-visible:ring-purple-400/30 dark:focus-visible:ring-purple-500/40",
|
|
68
|
+
"shadow-md dark:shadow-lg dark:shadow-purple-500/10",
|
|
69
|
+
],
|
|
70
|
+
admin: [
|
|
71
|
+
"border-transparent bg-gradient-to-r from-purple-600 to-pink-600 text-white",
|
|
72
|
+
"hover:from-purple-700 hover:to-pink-700",
|
|
73
|
+
"focus-visible:ring-purple-400/30 dark:focus-visible:ring-purple-500/40",
|
|
74
|
+
"shadow-md dark:shadow-lg dark:shadow-purple-500/10",
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
size: {
|
|
78
|
+
sm: "h-5 px-2 text-xs",
|
|
79
|
+
md: "h-6 px-3 text-sm",
|
|
80
|
+
lg: "h-8 px-4 text-base",
|
|
81
|
+
},
|
|
82
|
+
radius: {
|
|
83
|
+
default: "rounded-full",
|
|
84
|
+
sm: "rounded-md",
|
|
85
|
+
lg: "rounded-xl",
|
|
86
|
+
none: "rounded-none",
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
defaultVariants: {
|
|
90
|
+
variant: "primary",
|
|
91
|
+
size: "md",
|
|
92
|
+
radius: "default",
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
export interface BadgeProps
|
|
98
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
99
|
+
Omit<VariantProps<typeof badgeVariants>, 'variant'> {
|
|
100
|
+
withDot?: boolean;
|
|
101
|
+
dotColor?: string;
|
|
102
|
+
removable?: boolean;
|
|
103
|
+
onRemove?: () => void;
|
|
104
|
+
leftIcon?: React.ReactNode;
|
|
105
|
+
rightIcon?: React.ReactNode;
|
|
106
|
+
variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' | 'ghost' | 'pro' | 'admin';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Premium Badge Component
|
|
111
|
+
*
|
|
112
|
+
* @param props - Badge bileşeni özellikleri
|
|
113
|
+
* @param props.variant - Badgenin görsel varyantı
|
|
114
|
+
* @param props.size - Badge boyutu
|
|
115
|
+
* @param props.radius - Köşe yuvarlama stili
|
|
116
|
+
* @param props.withDot - Başında nokta gösterimi
|
|
117
|
+
* @param props.dotColor - Noktanın rengi (özel)
|
|
118
|
+
* @param props.removable - Kaldırılabilir badge (X simgesi ile)
|
|
119
|
+
* @param props.onRemove - Kaldırma işlevi
|
|
120
|
+
* @param props.leftIcon - Badge'in solunda görüntülenecek ikon
|
|
121
|
+
* @param props.rightIcon - Badge'in sağında görüntülenecek ikon
|
|
122
|
+
*/
|
|
123
|
+
function Badge({
|
|
124
|
+
className,
|
|
125
|
+
variant,
|
|
126
|
+
size,
|
|
127
|
+
radius,
|
|
128
|
+
withDot,
|
|
129
|
+
dotColor,
|
|
130
|
+
removable,
|
|
131
|
+
onRemove,
|
|
132
|
+
leftIcon,
|
|
133
|
+
rightIcon,
|
|
134
|
+
children,
|
|
135
|
+
...props
|
|
136
|
+
}: BadgeProps) {
|
|
137
|
+
// Auto-assign icons and content for special variants
|
|
138
|
+
let autoLeftIcon = leftIcon;
|
|
139
|
+
let autoChildren = children;
|
|
140
|
+
|
|
141
|
+
if (variant === 'pro') {
|
|
142
|
+
// Auto Sparkles icon for pro
|
|
143
|
+
autoLeftIcon = leftIcon || (
|
|
144
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-3 h-3">
|
|
145
|
+
<path d="M12 0L13.09 8.26L22 9L13.09 9.74L12 18L10.91 9.74L2 9L10.91 8.26L12 0Z" fill="currentColor"/>
|
|
146
|
+
<path d="M19 5L19.5 7L21 7.5L19.5 8L19 10L18.5 8L17 7.5L18.5 7L19 5Z" fill="currentColor"/>
|
|
147
|
+
<path d="M19 15L19.5 17L21 17.5L19.5 18L19 20L18.5 18L17 17.5L18.5 17L19 15Z" fill="currentColor"/>
|
|
148
|
+
</svg>
|
|
149
|
+
);
|
|
150
|
+
autoChildren = children || 'Pro';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (variant === 'admin') {
|
|
154
|
+
// Auto CheckCircle icon for admin
|
|
155
|
+
autoLeftIcon = leftIcon || (
|
|
156
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-3 h-3">
|
|
157
|
+
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/>
|
|
158
|
+
</svg>
|
|
159
|
+
);
|
|
160
|
+
autoChildren = children || 'Admin Pro';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<div
|
|
165
|
+
className={cn(badgeVariants({ variant, size, radius }), className)}
|
|
166
|
+
data-removable={removable ? "" : undefined}
|
|
167
|
+
{...props}
|
|
168
|
+
>
|
|
169
|
+
{withDot && (
|
|
170
|
+
<span
|
|
171
|
+
className={cn(
|
|
172
|
+
"h-2 w-2 rounded-full",
|
|
173
|
+
dotColor ||
|
|
174
|
+
(variant === "destructive" ? "bg-white" :
|
|
175
|
+
variant === "success" ? "bg-white" :
|
|
176
|
+
variant === "warning" ? "bg-white" :
|
|
177
|
+
variant === "secondary" ? "bg-secondary-foreground" :
|
|
178
|
+
variant === "outline" ? "bg-foreground" :
|
|
179
|
+
variant === "ghost" ? "bg-foreground" :
|
|
180
|
+
variant === "pro" ? "bg-white" :
|
|
181
|
+
variant === "admin" ? "bg-white" :
|
|
182
|
+
"bg-primary-foreground")
|
|
183
|
+
)}
|
|
184
|
+
aria-hidden="true"
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
{autoLeftIcon && (
|
|
188
|
+
<span className="inline-flex shrink-0">
|
|
189
|
+
{autoLeftIcon}
|
|
190
|
+
</span>
|
|
191
|
+
)}
|
|
192
|
+
<span className="truncate">{autoChildren}</span>
|
|
193
|
+
{rightIcon && (
|
|
194
|
+
<span className="inline-flex shrink-0">
|
|
195
|
+
{rightIcon}
|
|
196
|
+
</span>
|
|
197
|
+
)}
|
|
198
|
+
{removable && onRemove && (
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
className="ml-1 -mr-1 h-3.5 w-3.5 rounded-full inline-flex items-center justify-center hover:bg-black/10 dark:hover:bg-white/10"
|
|
202
|
+
onClick={(e) => {
|
|
203
|
+
e.stopPropagation();
|
|
204
|
+
onRemove();
|
|
205
|
+
}}
|
|
206
|
+
aria-label="Remove badge"
|
|
207
|
+
>
|
|
208
|
+
<svg
|
|
209
|
+
width="8"
|
|
210
|
+
height="8"
|
|
211
|
+
viewBox="0 0 8 8"
|
|
212
|
+
fill="none"
|
|
213
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
214
|
+
className="opacity-70"
|
|
215
|
+
aria-hidden="true"
|
|
216
|
+
>
|
|
217
|
+
<path d="M0.799988 7.19999L7.19999 0.799988M0.799988 0.799988L7.19999 7.19999" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
|
218
|
+
</svg>
|
|
219
|
+
</button>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { Badge, badgeVariants };
|