@turtleclub/ui 0.0.1
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/.turbo/turbo-build.log +15 -0
- package/.turbo/turbo-type-check.log +360 -0
- package/README.md +3 -0
- package/components.json +21 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1672 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +66 -0
- package/src/components/molecules/index.ts +7 -0
- package/src/components/molecules/opportunity-details.tsx +145 -0
- package/src/components/molecules/opportunity-item.tsx +63 -0
- package/src/components/molecules/route-details.tsx +87 -0
- package/src/components/molecules/swap-details.tsx +95 -0
- package/src/components/molecules/swap-input.tsx +115 -0
- package/src/components/molecules/token-selector.tsx +72 -0
- package/src/components/molecules/tx-status.tsx +254 -0
- package/src/components/ui/button.tsx +65 -0
- package/src/components/ui/card.tsx +101 -0
- package/src/components/ui/chip.tsx +48 -0
- package/src/components/ui/icon-animation.tsx +82 -0
- package/src/components/ui/index.ts +18 -0
- package/src/components/ui/info-card.tsx +128 -0
- package/src/components/ui/input.tsx +78 -0
- package/src/components/ui/label-with-icon.tsx +112 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/navigation-bar.tsx +135 -0
- package/src/components/ui/opportunity-details-v1.tsx +90 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +180 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sonner.tsx +23 -0
- package/src/components/ui/switch.tsx +29 -0
- package/src/components/ui/toggle-group.tsx +71 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/index.ts +9 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/globals.css +75 -0
- package/src/styles/themes/index.css +9 -0
- package/src/styles/themes/semantic.css +107 -0
- package/src/styles/tokens/colors.css +77 -0
- package/src/styles/tokens/index.css +15 -0
- package/src/styles/tokens/radius.css +46 -0
- package/src/styles/tokens/spacing.css +52 -0
- package/src/styles/tokens/typography.css +86 -0
- package/src/tokens/index.ts +108 -0
- package/tsconfig.json +21 -0
- package/vite.config.js +65 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Card } from "@/components/ui/card";
|
|
4
|
+
import { IconAnimation } from "@/components/ui/icon-animation";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { CheckIcon, TurtleIcon, XIcon } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
interface TxStatusProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
txHash?: string;
|
|
12
|
+
explorerUrl?: string;
|
|
13
|
+
estimatedTime?: string;
|
|
14
|
+
amount?: string;
|
|
15
|
+
token?: string;
|
|
16
|
+
protocol?: string;
|
|
17
|
+
steps?: Array<{
|
|
18
|
+
label: string;
|
|
19
|
+
completed: boolean;
|
|
20
|
+
current?: boolean;
|
|
21
|
+
txHash?: string;
|
|
22
|
+
}>;
|
|
23
|
+
variant?: "default" | "compact";
|
|
24
|
+
completed?: boolean;
|
|
25
|
+
cancelled?: boolean;
|
|
26
|
+
onViewDetails?: () => void;
|
|
27
|
+
onClose?: () => void;
|
|
28
|
+
showActions?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const TxStatus = React.forwardRef<HTMLDivElement, TxStatusProps>(
|
|
32
|
+
(
|
|
33
|
+
{
|
|
34
|
+
className,
|
|
35
|
+
title,
|
|
36
|
+
description,
|
|
37
|
+
txHash,
|
|
38
|
+
explorerUrl,
|
|
39
|
+
estimatedTime,
|
|
40
|
+
amount,
|
|
41
|
+
token,
|
|
42
|
+
protocol,
|
|
43
|
+
steps = [],
|
|
44
|
+
variant = "default",
|
|
45
|
+
completed = false,
|
|
46
|
+
cancelled = false,
|
|
47
|
+
onViewDetails,
|
|
48
|
+
onClose,
|
|
49
|
+
showActions = true,
|
|
50
|
+
...props
|
|
51
|
+
},
|
|
52
|
+
ref
|
|
53
|
+
) => {
|
|
54
|
+
const defaultTitle = cancelled
|
|
55
|
+
? "Transaction Cancelled"
|
|
56
|
+
: completed
|
|
57
|
+
? "Transaction Completed"
|
|
58
|
+
: "Processing Transaction";
|
|
59
|
+
const defaultDescription = cancelled
|
|
60
|
+
? "Transaction was cancelled by user"
|
|
61
|
+
: completed
|
|
62
|
+
? "Your transaction has been successfully processed!"
|
|
63
|
+
: "Please wait while your transaction is being processed...";
|
|
64
|
+
|
|
65
|
+
const ExternalLinkIcon = ({ size = "w-4 h-4" }) => (
|
|
66
|
+
<svg className={size} fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
67
|
+
<path
|
|
68
|
+
strokeLinecap="round"
|
|
69
|
+
strokeLinejoin="round"
|
|
70
|
+
strokeWidth={2}
|
|
71
|
+
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
72
|
+
/>
|
|
73
|
+
</svg>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const StepCheckIcon = () => (
|
|
77
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
78
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
79
|
+
</svg>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const formatTxHash = (hash: string) => {
|
|
83
|
+
if (hash.length <= 12) return hash;
|
|
84
|
+
return `${hash.slice(0, 6)}...${hash.slice(-6)}`;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<Card
|
|
89
|
+
ref={ref}
|
|
90
|
+
className={cn(
|
|
91
|
+
"p-6 text-center space-y-4",
|
|
92
|
+
variant === "compact" && "p-4 space-y-3",
|
|
93
|
+
className
|
|
94
|
+
)}
|
|
95
|
+
{...props}
|
|
96
|
+
>
|
|
97
|
+
{/* Animation */}
|
|
98
|
+
<div className="flex justify-center">
|
|
99
|
+
<IconAnimation spinning={!completed && !cancelled} size={variant === "compact" ? "default" : "lg"}>
|
|
100
|
+
{cancelled ? (
|
|
101
|
+
<XIcon className="w-8 h-8 text-destructive" />
|
|
102
|
+
) : completed ? (
|
|
103
|
+
<CheckIcon className="w-8 h-8 text-primary" />
|
|
104
|
+
) : (
|
|
105
|
+
<TurtleIcon className="w-8 h-8 text-primary" />
|
|
106
|
+
)}
|
|
107
|
+
</IconAnimation>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{/* Title and description */}
|
|
111
|
+
<div className="space-y-2">
|
|
112
|
+
<h3
|
|
113
|
+
className={cn(
|
|
114
|
+
"font-medium text-foreground",
|
|
115
|
+
variant === "compact" ? "text-sm" : "text-base"
|
|
116
|
+
)}
|
|
117
|
+
>
|
|
118
|
+
{title || defaultTitle}
|
|
119
|
+
</h3>
|
|
120
|
+
<p className={cn("text-muted-foreground", variant === "compact" ? "text-xs" : "text-sm")}>
|
|
121
|
+
{description || defaultDescription}
|
|
122
|
+
</p>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
{/* Transaction summary (completed state) */}
|
|
126
|
+
{completed && !cancelled && (amount || token || protocol) && (
|
|
127
|
+
<div className="p-3 bg-muted/50 rounded-lg space-y-1">
|
|
128
|
+
{amount && token && (
|
|
129
|
+
<div className="text-sm font-medium">
|
|
130
|
+
{amount} {token}
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
{protocol && <div className="text-xs text-muted-foreground">via {protocol}</div>}
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{/* Transaction hash link */}
|
|
138
|
+
{txHash && !cancelled && (
|
|
139
|
+
<div className="space-y-1">
|
|
140
|
+
<div className="text-xs text-muted-foreground">Transaction Hash</div>
|
|
141
|
+
{explorerUrl ? (
|
|
142
|
+
<a
|
|
143
|
+
href={`${explorerUrl}/tx/${txHash}`}
|
|
144
|
+
target="_blank"
|
|
145
|
+
rel="noopener noreferrer"
|
|
146
|
+
className="inline-flex items-center gap-2 text-sm text-primary hover:text-primary/80 transition-colors"
|
|
147
|
+
>
|
|
148
|
+
{formatTxHash(txHash)}
|
|
149
|
+
<ExternalLinkIcon />
|
|
150
|
+
</a>
|
|
151
|
+
) : (
|
|
152
|
+
<div className="text-sm font-mono text-foreground">{formatTxHash(txHash)}</div>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
{/* Estimated time (pending state) */}
|
|
158
|
+
{!completed && !cancelled && estimatedTime && (
|
|
159
|
+
<div className="text-xs text-muted-foreground">Estimated time: {estimatedTime}</div>
|
|
160
|
+
)}
|
|
161
|
+
|
|
162
|
+
{/* Progress steps (pending state) */}
|
|
163
|
+
{!completed && !cancelled && steps.length > 0 && (
|
|
164
|
+
<div className="space-y-2 pt-2 border-t border-border">
|
|
165
|
+
<div className="text-xs font-medium text-muted-foreground text-left">Progress</div>
|
|
166
|
+
<div className="space-y-2">
|
|
167
|
+
{steps.map((step, index) => (
|
|
168
|
+
<div key={index} className="flex items-center gap-3 text-left">
|
|
169
|
+
<div
|
|
170
|
+
className={cn(
|
|
171
|
+
"flex items-center justify-center w-5 h-5 rounded-full border-2 flex-shrink-0",
|
|
172
|
+
step.completed && "bg-primary border-primary text-primary-foreground",
|
|
173
|
+
step.current &&
|
|
174
|
+
!step.completed &&
|
|
175
|
+
"border-primary text-primary animate-pulse",
|
|
176
|
+
!step.completed &&
|
|
177
|
+
!step.current &&
|
|
178
|
+
"border-muted-foreground/30 text-muted-foreground"
|
|
179
|
+
)}
|
|
180
|
+
>
|
|
181
|
+
{step.completed ? (
|
|
182
|
+
<StepCheckIcon />
|
|
183
|
+
) : step.current ? (
|
|
184
|
+
<div className="w-2 h-2 rounded-full bg-primary animate-pulse" />
|
|
185
|
+
) : (
|
|
186
|
+
<div className="w-2 h-2 rounded-full bg-muted-foreground/30" />
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
<div className="flex-1 flex items-center justify-between">
|
|
190
|
+
<span
|
|
191
|
+
className={cn(
|
|
192
|
+
"text-sm",
|
|
193
|
+
step.completed && "text-foreground",
|
|
194
|
+
step.current && !step.completed && "text-foreground font-medium",
|
|
195
|
+
!step.completed && !step.current && "text-muted-foreground"
|
|
196
|
+
)}
|
|
197
|
+
>
|
|
198
|
+
{step.label}
|
|
199
|
+
</span>
|
|
200
|
+
{step.txHash && explorerUrl && (
|
|
201
|
+
<a
|
|
202
|
+
href={`${explorerUrl}/tx/${step.txHash}`}
|
|
203
|
+
target="_blank"
|
|
204
|
+
rel="noopener noreferrer"
|
|
205
|
+
className="inline-flex items-center gap-1 text-xs text-primary hover:text-primary/80 transition-colors ml-2"
|
|
206
|
+
title={`View transaction: ${step.txHash}`}
|
|
207
|
+
>
|
|
208
|
+
<span className="font-mono">{step.txHash.slice(0, 6)}...{step.txHash.slice(-4)}</span>
|
|
209
|
+
<ExternalLinkIcon size="w-3 h-3" />
|
|
210
|
+
</a>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
))}
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
)}
|
|
218
|
+
|
|
219
|
+
{/* Action buttons (completed or cancelled state) */}
|
|
220
|
+
{(completed || cancelled) && showActions && (onViewDetails || onClose) && (
|
|
221
|
+
<div
|
|
222
|
+
className={cn(
|
|
223
|
+
"flex gap-2 pt-2",
|
|
224
|
+
variant === "compact" ? "flex-col" : "flex-row justify-center"
|
|
225
|
+
)}
|
|
226
|
+
>
|
|
227
|
+
{!cancelled && onViewDetails && (
|
|
228
|
+
<Button
|
|
229
|
+
variant="transparentWhite"
|
|
230
|
+
size={variant === "compact" ? "sm" : "default"}
|
|
231
|
+
onClick={onViewDetails}
|
|
232
|
+
>
|
|
233
|
+
View Details
|
|
234
|
+
</Button>
|
|
235
|
+
)}
|
|
236
|
+
{onClose && (
|
|
237
|
+
<Button
|
|
238
|
+
variant="default"
|
|
239
|
+
size={variant === "compact" ? "sm" : "default"}
|
|
240
|
+
onClick={onClose}
|
|
241
|
+
>
|
|
242
|
+
{cancelled ? "Try Again" : "Done"}
|
|
243
|
+
</Button>
|
|
244
|
+
)}
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
</Card>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
TxStatus.displayName = "TxStatus";
|
|
253
|
+
|
|
254
|
+
export { TxStatus };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none border",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
// Turtle Design System variants using semantic classes
|
|
13
|
+
default:
|
|
14
|
+
"bg-background text-primary border-primary shadow-[0_0_4px_0_hsl(var(--primary))] hover:bg-background/90 hover:shadow-[0_0_6px_0_hsl(var(--primary))]",
|
|
15
|
+
green:
|
|
16
|
+
"bg-primary text-primary-foreground border-primary shadow-[0_0_4px_0_hsl(var(--primary))] hover:bg-primary/90 hover:shadow-[0_0_6px_0_hsl(var(--primary))]",
|
|
17
|
+
transparentWhite: "bg-secondary text-secondary-foreground border-secondary hover:bg-secondary/80",
|
|
18
|
+
transparentGreen: "bg-secondary text-primary border-secondary hover:bg-secondary/80",
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
22
|
+
sm: "h-8 gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
23
|
+
lg: "h-10 px-6 has-[>svg]:px-4",
|
|
24
|
+
icon: "size-9",
|
|
25
|
+
},
|
|
26
|
+
rounded: {
|
|
27
|
+
default: "rounded-full",
|
|
28
|
+
none: "rounded-none",
|
|
29
|
+
sm: "rounded-sm",
|
|
30
|
+
md: "rounded-md",
|
|
31
|
+
lg: "rounded-lg",
|
|
32
|
+
full: "rounded-full",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
variant: "default",
|
|
37
|
+
size: "default",
|
|
38
|
+
rounded: "default",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
function Button({
|
|
44
|
+
className,
|
|
45
|
+
variant,
|
|
46
|
+
size,
|
|
47
|
+
rounded,
|
|
48
|
+
asChild = false,
|
|
49
|
+
...props
|
|
50
|
+
}: React.ComponentProps<"button"> &
|
|
51
|
+
VariantProps<typeof buttonVariants> & {
|
|
52
|
+
asChild?: boolean;
|
|
53
|
+
}) {
|
|
54
|
+
const Comp = asChild ? Slot : "button";
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Comp
|
|
58
|
+
data-slot="button"
|
|
59
|
+
className={cn(buttonVariants({ variant, size, rounded, className }))}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const cardVariants = cva("transition-all", {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
// Turtle Design System variants
|
|
10
|
+
container:
|
|
11
|
+
"relative bg-card shadow-[0_0_20px_0_rgba(0,0,0,0.25)] before:absolute before:inset-0 before:rounded-[inherit] before:p-px before:bg-gradient-to-b before:from-[#F9F9F9] before:from-40% before:via-white before:via-40.01% before:to-white before:to-50% before:content-[''] before:pointer-events-none before:-z-10",
|
|
12
|
+
simple: "bg-muted",
|
|
13
|
+
item: "bg-muted shadow-[0_4px_4px_0_rgba(0,0,0,0.25)]",
|
|
14
|
+
},
|
|
15
|
+
padding: {
|
|
16
|
+
none: "p-0",
|
|
17
|
+
sm: "p-3",
|
|
18
|
+
default: "p-4",
|
|
19
|
+
md: "p-6",
|
|
20
|
+
lg: "p-8",
|
|
21
|
+
},
|
|
22
|
+
rounded: {
|
|
23
|
+
default: "rounded-lg",
|
|
24
|
+
none: "rounded-none",
|
|
25
|
+
sm: "rounded-sm",
|
|
26
|
+
md: "rounded-md",
|
|
27
|
+
lg: "rounded-lg",
|
|
28
|
+
xl: "rounded-xl",
|
|
29
|
+
full: "rounded-full",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
variant: "container",
|
|
34
|
+
padding: "default",
|
|
35
|
+
rounded: "default",
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function Card({
|
|
40
|
+
className,
|
|
41
|
+
variant,
|
|
42
|
+
padding,
|
|
43
|
+
rounded,
|
|
44
|
+
...props
|
|
45
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof cardVariants>) {
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
data-slot="card"
|
|
49
|
+
className={cn(cardVariants({ variant, padding, rounded, className }))}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
data-slot="card-header"
|
|
59
|
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function CardTitle({ className, children, ...props }: React.ComponentProps<"h3">) {
|
|
66
|
+
return (
|
|
67
|
+
<h3
|
|
68
|
+
data-slot="card-title"
|
|
69
|
+
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</h3>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
78
|
+
return (
|
|
79
|
+
<p
|
|
80
|
+
data-slot="card-description"
|
|
81
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
88
|
+
return <div data-slot="card-content" className={cn("p-6 pt-0", className)} {...props} />;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
data-slot="card-footer"
|
|
95
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, cardVariants };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const chipVariants = cva(
|
|
7
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 cursor-pointer border-none rounded-full",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
12
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
13
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
xs: "h-6 px-2 py-1 text-xs",
|
|
17
|
+
default: "h-8 px-4 py-1",
|
|
18
|
+
sm: "h-7 px-3 py-1 text-xs",
|
|
19
|
+
lg: "h-9 px-6 py-2",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export interface ChipProps extends React.ComponentProps<"div">, VariantProps<typeof chipVariants> {
|
|
30
|
+
asChild?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Chip = React.forwardRef<HTMLDivElement, ChipProps>(
|
|
34
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
data-slot="chip"
|
|
38
|
+
className={cn(chipVariants({ variant, size, className }))}
|
|
39
|
+
ref={ref}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
Chip.displayName = "Chip";
|
|
47
|
+
|
|
48
|
+
export { Chip, chipVariants };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const iconAnimationVariants = cva(
|
|
7
|
+
"relative inline-flex items-center justify-center rounded-full",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
size: {
|
|
11
|
+
default: "w-12 h-12",
|
|
12
|
+
sm: "w-8 h-8",
|
|
13
|
+
lg: "w-16 h-16",
|
|
14
|
+
xl: "w-20 h-20",
|
|
15
|
+
},
|
|
16
|
+
variant: {
|
|
17
|
+
default: "bg-background",
|
|
18
|
+
transparent: "bg-transparent",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
size: "default",
|
|
23
|
+
variant: "default",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export interface IconAnimationProps
|
|
29
|
+
extends React.ComponentProps<"div">,
|
|
30
|
+
VariantProps<typeof iconAnimationVariants> {
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
spinning?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const IconAnimation = React.forwardRef<HTMLDivElement, IconAnimationProps>(
|
|
36
|
+
({ className, size, variant, children, spinning = true, ...props }, ref) => {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn(iconAnimationVariants({ size, variant }), className)}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
{/* Animated border */}
|
|
44
|
+
<div
|
|
45
|
+
className={cn(
|
|
46
|
+
"absolute inset-0 rounded-full",
|
|
47
|
+
"bg-gradient-to-r from-transparent via-primary to-transparent",
|
|
48
|
+
"animate-spin",
|
|
49
|
+
{
|
|
50
|
+
"animate-spin": spinning,
|
|
51
|
+
"animate-none": !spinning,
|
|
52
|
+
}
|
|
53
|
+
)}
|
|
54
|
+
style={{
|
|
55
|
+
background: spinning
|
|
56
|
+
? "conic-gradient(from 0deg, transparent 0deg, var(--primary) 180deg, transparent 360deg)"
|
|
57
|
+
: "conic-gradient(from 0deg, var(--primary) 0deg, var(--primary) 360deg)",
|
|
58
|
+
padding: "1px",
|
|
59
|
+
borderRadius: "9999px",
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
{/* Inner circle to create the border effect */}
|
|
63
|
+
<div
|
|
64
|
+
className={cn(
|
|
65
|
+
"w-full h-full rounded-full",
|
|
66
|
+
variant === "transparent" ? "bg-transparent" : "bg-background"
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{/* Icon container - stays fixed */}
|
|
72
|
+
<div className="relative z-10 flex items-center justify-center">
|
|
73
|
+
{children}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
IconAnimation.displayName = "IconAnimation";
|
|
81
|
+
|
|
82
|
+
export { IconAnimation, iconAnimationVariants };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./input";
|
|
2
|
+
export * from "./label";
|
|
3
|
+
export * from "./scroll-area";
|
|
4
|
+
export * from "./select";
|
|
5
|
+
export * from "./separator";
|
|
6
|
+
export * from "./sonner";
|
|
7
|
+
export * from "./switch";
|
|
8
|
+
export * from "./toggle-group";
|
|
9
|
+
export * from "./toggle";
|
|
10
|
+
export * from "./tooltip";
|
|
11
|
+
export * from "./button";
|
|
12
|
+
export * from "./card";
|
|
13
|
+
export * from "./chip";
|
|
14
|
+
export * from "./icon-animation";
|
|
15
|
+
export * from "./label-with-icon";
|
|
16
|
+
export * from "./navigation-bar";
|
|
17
|
+
export * from "./info-card";
|
|
18
|
+
export * from "./opportunity-details-v1";
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Card } from "./card";
|
|
4
|
+
import type { VariantProps } from "class-variance-authority";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
|
|
7
|
+
const infoCardVariants = cva("space-y-1", {
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
sm: "p-2",
|
|
11
|
+
default: "p-3",
|
|
12
|
+
lg: "p-4",
|
|
13
|
+
},
|
|
14
|
+
align: {
|
|
15
|
+
left: "text-left",
|
|
16
|
+
center: "text-center",
|
|
17
|
+
right: "text-right",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
size: "default",
|
|
22
|
+
align: "center",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const valueVariants = cva("font-bold", {
|
|
27
|
+
variants: {
|
|
28
|
+
color: {
|
|
29
|
+
primary: "text-foreground",
|
|
30
|
+
secondary: "text-muted-foreground",
|
|
31
|
+
accent: "text-primary",
|
|
32
|
+
success: "text-green-600",
|
|
33
|
+
warning: "text-yellow-600",
|
|
34
|
+
error: "text-red-600",
|
|
35
|
+
},
|
|
36
|
+
size: {
|
|
37
|
+
sm: "text-sm",
|
|
38
|
+
default: "text-lg",
|
|
39
|
+
lg: "text-xl",
|
|
40
|
+
xl: "text-2xl",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
color: "primary",
|
|
45
|
+
size: "default",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const titleVariants = cva("font-medium text-muted-foreground", {
|
|
50
|
+
variants: {
|
|
51
|
+
size: {
|
|
52
|
+
sm: "text-xs",
|
|
53
|
+
default: "text-xs",
|
|
54
|
+
lg: "text-sm",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
defaultVariants: {
|
|
58
|
+
size: "default",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
interface InfoCardProps
|
|
63
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "color">,
|
|
64
|
+
VariantProps<typeof infoCardVariants> {
|
|
65
|
+
title: string;
|
|
66
|
+
value: string;
|
|
67
|
+
color?: VariantProps<typeof valueVariants>["color"];
|
|
68
|
+
valueSize?: VariantProps<typeof valueVariants>["size"];
|
|
69
|
+
titleSize?: VariantProps<typeof titleVariants>["size"];
|
|
70
|
+
align?: VariantProps<typeof infoCardVariants>["align"];
|
|
71
|
+
icon?: React.ReactNode;
|
|
72
|
+
subtitle?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const InfoCard = React.forwardRef<HTMLDivElement, InfoCardProps>(
|
|
76
|
+
(
|
|
77
|
+
{
|
|
78
|
+
title,
|
|
79
|
+
value,
|
|
80
|
+
color = "primary",
|
|
81
|
+
valueSize = "default",
|
|
82
|
+
titleSize = "default",
|
|
83
|
+
size = "default",
|
|
84
|
+
align = "center",
|
|
85
|
+
icon,
|
|
86
|
+
subtitle,
|
|
87
|
+
className,
|
|
88
|
+
...props
|
|
89
|
+
},
|
|
90
|
+
ref
|
|
91
|
+
) => {
|
|
92
|
+
return (
|
|
93
|
+
<Card
|
|
94
|
+
ref={ref}
|
|
95
|
+
variant="item"
|
|
96
|
+
className={cn(infoCardVariants({ size, align }), className)}
|
|
97
|
+
{...props}
|
|
98
|
+
>
|
|
99
|
+
{/* Icon */}
|
|
100
|
+
{icon && (
|
|
101
|
+
<div
|
|
102
|
+
className={cn(
|
|
103
|
+
"flex mb-2",
|
|
104
|
+
align === "left" && "justify-start",
|
|
105
|
+
align === "center" && "justify-center",
|
|
106
|
+
align === "right" && "justify-end"
|
|
107
|
+
)}
|
|
108
|
+
>
|
|
109
|
+
{icon}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{/* Title */}
|
|
114
|
+
<p className={titleVariants({ size: titleSize })}>{title}</p>
|
|
115
|
+
|
|
116
|
+
{/* Value */}
|
|
117
|
+
<p className={valueVariants({ color, size: valueSize })}>{value}</p>
|
|
118
|
+
|
|
119
|
+
{/* Subtitle */}
|
|
120
|
+
{subtitle && <p className="text-xs text-muted-foreground/70">{subtitle}</p>}
|
|
121
|
+
</Card>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
InfoCard.displayName = "InfoCard";
|
|
127
|
+
|
|
128
|
+
export { InfoCard };
|