@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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +15 -0
  2. package/.turbo/turbo-type-check.log +360 -0
  3. package/README.md +3 -0
  4. package/components.json +21 -0
  5. package/dist/index.cjs +2 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.js +1672 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/styles.css +1 -0
  10. package/package.json +66 -0
  11. package/src/components/molecules/index.ts +7 -0
  12. package/src/components/molecules/opportunity-details.tsx +145 -0
  13. package/src/components/molecules/opportunity-item.tsx +63 -0
  14. package/src/components/molecules/route-details.tsx +87 -0
  15. package/src/components/molecules/swap-details.tsx +95 -0
  16. package/src/components/molecules/swap-input.tsx +115 -0
  17. package/src/components/molecules/token-selector.tsx +72 -0
  18. package/src/components/molecules/tx-status.tsx +254 -0
  19. package/src/components/ui/button.tsx +65 -0
  20. package/src/components/ui/card.tsx +101 -0
  21. package/src/components/ui/chip.tsx +48 -0
  22. package/src/components/ui/icon-animation.tsx +82 -0
  23. package/src/components/ui/index.ts +18 -0
  24. package/src/components/ui/info-card.tsx +128 -0
  25. package/src/components/ui/input.tsx +78 -0
  26. package/src/components/ui/label-with-icon.tsx +112 -0
  27. package/src/components/ui/label.tsx +22 -0
  28. package/src/components/ui/navigation-bar.tsx +135 -0
  29. package/src/components/ui/opportunity-details-v1.tsx +90 -0
  30. package/src/components/ui/scroll-area.tsx +56 -0
  31. package/src/components/ui/select.tsx +180 -0
  32. package/src/components/ui/separator.tsx +26 -0
  33. package/src/components/ui/sonner.tsx +23 -0
  34. package/src/components/ui/switch.tsx +29 -0
  35. package/src/components/ui/toggle-group.tsx +71 -0
  36. package/src/components/ui/toggle.tsx +47 -0
  37. package/src/components/ui/tooltip.tsx +59 -0
  38. package/src/index.ts +9 -0
  39. package/src/lib/utils.ts +6 -0
  40. package/src/styles/globals.css +75 -0
  41. package/src/styles/themes/index.css +9 -0
  42. package/src/styles/themes/semantic.css +107 -0
  43. package/src/styles/tokens/colors.css +77 -0
  44. package/src/styles/tokens/index.css +15 -0
  45. package/src/styles/tokens/radius.css +46 -0
  46. package/src/styles/tokens/spacing.css +52 -0
  47. package/src/styles/tokens/typography.css +86 -0
  48. package/src/tokens/index.ts +108 -0
  49. package/tsconfig.json +21 -0
  50. package/vite.config.js +65 -0
@@ -0,0 +1,145 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Card } from "@/components/ui/card";
4
+ import { LabelWithIcon } from "@/components/ui/label-with-icon";
5
+
6
+ interface DetailRow {
7
+ label: string;
8
+ value: string;
9
+ highlight?: boolean;
10
+ icon?: React.ReactNode;
11
+ }
12
+
13
+ interface OpportunityDetailsProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ title?: string;
15
+ titleIcon?: React.ReactNode;
16
+ yield?: string;
17
+ details?: DetailRow[];
18
+ badges?: Array<{
19
+ text: string;
20
+ icon?: React.ReactNode;
21
+ variant?: "default" | "secondary" | "outline";
22
+ }>;
23
+ externalLink?: {
24
+ text: string;
25
+ href: string;
26
+ icon?: React.ReactNode;
27
+ };
28
+ }
29
+
30
+ const OpportunityDetails = React.forwardRef<HTMLDivElement, OpportunityDetailsProps>(
31
+ (
32
+ {
33
+ className,
34
+ title,
35
+ titleIcon,
36
+ yield: yieldValue,
37
+ details = [],
38
+ badges = [],
39
+ externalLink,
40
+ ...props
41
+ },
42
+ ref
43
+ ) => {
44
+ const defaultTitleIcon = (
45
+ <div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center">
46
+ <svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
47
+ <path d="M12 2L13.09 8.26L20 7L18.74 13.09L22 14L16.74 19.26L17 21L10.91 19.74L10 22L8.09 15.74L2 17L3.26 10.91L0 10L5.26 4.74L5 3L11.09 4.26L12 2Z" />
48
+ </svg>
49
+ </div>
50
+ );
51
+
52
+ const ExternalLinkIcon = () => (
53
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
54
+ <path
55
+ strokeLinecap="round"
56
+ strokeLinejoin="round"
57
+ strokeWidth={2}
58
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
59
+ />
60
+ </svg>
61
+ );
62
+
63
+ return (
64
+ <Card ref={ref} className={cn("p-4 space-y-4", className)} {...props}>
65
+ {/* Header with title and yield */}
66
+ {(title || titleIcon) && (
67
+ <div className="flex items-center justify-between">
68
+ <div className="flex items-center gap-3">
69
+ <LabelWithIcon icon={titleIcon || defaultTitleIcon} textSize="lg" iconSize="lg">
70
+ {title}
71
+ </LabelWithIcon>
72
+ </div>
73
+ {yieldValue && (
74
+ <div className="text-right">
75
+ <div className="text-2xl font-bold text-primary">{yieldValue}</div>
76
+ <div className="text-xs text-muted-foreground">APY</div>
77
+ </div>
78
+ )}
79
+ </div>
80
+ )}
81
+
82
+ {/* Detail rows */}
83
+ {details.length > 0 && (
84
+ <div className="space-y-3">
85
+ {details.map((detail, index) => (
86
+ <div key={index} className="space-y-2">
87
+ <Card className="p-3">
88
+ <div className="flex items-center justify-between">
89
+ <div className="flex items-center gap-2">
90
+ {detail.icon}
91
+ <span className="text-sm text-muted-foreground">{detail.label}</span>
92
+ </div>
93
+ <span className={cn("text-sm font-medium", detail.highlight && "text-primary")}>
94
+ {detail.value}
95
+ </span>
96
+ </div>
97
+ </Card>
98
+ </div>
99
+ ))}
100
+ </div>
101
+ )}
102
+
103
+ {/* Badges section */}
104
+ {badges.length > 0 && (
105
+ <div className="flex flex-wrap gap-2">
106
+ {badges.map((badge, index) => (
107
+ <div
108
+ key={index}
109
+ className={cn(
110
+ "inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-medium",
111
+ badge.variant === "secondary" && "bg-secondary text-secondary-foreground",
112
+ badge.variant === "outline" && "border border-border bg-background",
113
+ (!badge.variant || badge.variant === "default") && "bg-primary/10 text-primary"
114
+ )}
115
+ >
116
+ {badge.icon}
117
+ {badge.text}
118
+ </div>
119
+ ))}
120
+ </div>
121
+ )}
122
+
123
+ {/* External link */}
124
+ {externalLink && (
125
+ <div className="pt-2 border-t border-border">
126
+ <a
127
+ href={externalLink.href}
128
+ target="_blank"
129
+ rel="noopener noreferrer"
130
+ className="inline-flex items-center gap-2 text-sm text-primary hover:text-primary/80 transition-colors"
131
+ >
132
+ {externalLink.icon}
133
+ {externalLink.text}
134
+ <ExternalLinkIcon />
135
+ </a>
136
+ </div>
137
+ )}
138
+ </Card>
139
+ );
140
+ }
141
+ );
142
+
143
+ OpportunityDetails.displayName = "OpportunityDetails";
144
+
145
+ export { OpportunityDetails, type DetailRow };
@@ -0,0 +1,63 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Card } from "@/components/ui/card";
4
+ import { LabelWithIcon } from "@/components/ui/label-with-icon";
5
+
6
+ interface OpportunityItemProps extends React.ComponentProps<"div"> {
7
+ icon?: React.ReactNode | string;
8
+ name: string;
9
+ tvl?: string;
10
+ yield?: string;
11
+ onSelect?: () => void;
12
+ selected?: boolean;
13
+ }
14
+
15
+ const OpportunityItem = React.forwardRef<HTMLDivElement, OpportunityItemProps>(
16
+ (
17
+ { className, icon, name, tvl, yield: yieldValue, onSelect, selected = false, ...props },
18
+ ref
19
+ ) => {
20
+ const defaultIcon = (
21
+ <div className="w-6 h-6 rounded-full bg-primary/20 flex items-center justify-center">
22
+ <svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
23
+ <path d="M12 2L13.09 8.26L20 7L18.74 13.09L22 14L16.74 19.26L17 21L10.91 19.74L10 22L8.09 15.74L2 17L3.26 10.91L0 10L5.26 4.74L5 3L11.09 4.26L12 2Z" />
24
+ </svg>
25
+ </div>
26
+ );
27
+
28
+ return (
29
+ <Card
30
+ ref={ref}
31
+ className={cn(
32
+ "cursor-pointer transition-all duration-200 hover:shadow-md w-full",
33
+ selected && "ring-2 ring-primary ring-offset-2",
34
+ className
35
+ )}
36
+ onClick={onSelect}
37
+ {...props}
38
+ >
39
+ <div className="flex gap-12 items-start justify-between">
40
+ {/* Left side: Name and TVL */}
41
+ <div className="flex flex-col min-w-0 flex-1 items-start justify-start">
42
+ <span className="text-sm font-medium text-foreground truncate">
43
+ <LabelWithIcon icon={icon || defaultIcon} textSize="2xl">
44
+ {name}
45
+ </LabelWithIcon>
46
+ </span>
47
+ {tvl && <span className="text-xs text-muted-foreground">TVL: {tvl}</span>}
48
+ </div>
49
+
50
+ {/* Right side: Yield */}
51
+ <div className="flex-shrink-0">
52
+ {yieldValue && <span className="text-lg font-bold text-primary">{yieldValue}</span>}
53
+ </div>
54
+ </div>
55
+ </Card>
56
+ );
57
+ }
58
+ );
59
+
60
+ OpportunityItem.displayName = "OpportunityItem";
61
+
62
+ export { OpportunityItem };
63
+ export type { OpportunityItemProps };
@@ -0,0 +1,87 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Card } from "@/components/ui/card";
4
+ import { ChevronDownIcon, ChevronsRightIcon } from "lucide-react";
5
+ import { LabelWithIcon, ScrollArea, ScrollBar } from "../ui";
6
+
7
+ interface TokenStep {
8
+ icon?: React.ReactNode | string;
9
+ symbol: string;
10
+ amount?: string;
11
+ }
12
+
13
+ interface RouteDetailsProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ tokens: TokenStep[];
15
+ variant?: "default" | "compact";
16
+ defaultOpen?: boolean;
17
+ }
18
+
19
+ const RouteDetails = React.forwardRef<HTMLDivElement, RouteDetailsProps>(
20
+ ({ className, tokens = [], variant = "default", defaultOpen = false, ...props }, ref) => {
21
+ const [isOpen, setIsOpen] = React.useState(defaultOpen);
22
+
23
+ const defaultIcon = (symbol: string) => (
24
+ <div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center">
25
+ <span className="text-xs font-bold text-primary">{symbol.charAt(0)}</span>
26
+ </div>
27
+ );
28
+
29
+ if (tokens.length === 0) return null;
30
+
31
+ return (
32
+ <Card ref={ref} className={cn("overflow-hidden", className)} {...props}>
33
+ {/* Collapsible header */}
34
+ <button
35
+ onClick={() => setIsOpen(!isOpen)}
36
+ className="w-full flex items-center justify-between p-2 hover:bg-muted/50 transition-colors"
37
+ >
38
+ <span className="text-sm font-medium">Swap Route</span>
39
+ <div className="flex items-center justify-between gap-2">
40
+ <span className="text-xs text-muted-foreground">
41
+ {tokens.length} token{tokens.length > 1 ? "s" : ""}
42
+ </span>
43
+ <ChevronDownIcon className="w-4 h-4 text-primary" />
44
+ </div>
45
+ </button>
46
+
47
+ {/* Collapsible content */}
48
+ {isOpen && (
49
+ <div className="px-4 pb-4 border-t border-border">
50
+ {/* Route visualization */}
51
+ <ScrollArea className="rounded-md whitespace-nowrap">
52
+ <div className="flex items-center justify-between gap-2 overflow-x-auto py-3">
53
+ {tokens.map((token, index) => (
54
+ <>
55
+ <Card variant="item" className="p-2 rounded-full" key={index}>
56
+ <LabelWithIcon
57
+ icon={token.icon || defaultIcon(token.symbol)}
58
+ textSize="xs"
59
+ iconSize="sm"
60
+ >
61
+ <span className="gap-2">
62
+ {token.amount} {token.symbol}
63
+ </span>
64
+ </LabelWithIcon>
65
+
66
+ {/* Arrow between tokens */}
67
+ </Card>
68
+ {index < tokens.length - 1 && (
69
+ <div className="flex-shrink-0 mx-1">
70
+ <ChevronsRightIcon className="w-4 h-4 text-primary" />
71
+ </div>
72
+ )}
73
+ </>
74
+ ))}
75
+ </div>
76
+ <ScrollBar orientation="horizontal" className="h-2" />
77
+ </ScrollArea>
78
+ </div>
79
+ )}
80
+ </Card>
81
+ );
82
+ }
83
+ );
84
+
85
+ RouteDetails.displayName = "RouteDetails";
86
+
87
+ export { RouteDetails, type TokenStep };
@@ -0,0 +1,95 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Card } from "@/components/ui/card";
4
+ import { ChevronDownIcon } from "lucide-react";
5
+
6
+ interface SwapDetailsProps extends React.HTMLAttributes<HTMLDivElement> {
7
+ deposit?: string;
8
+ receive?: string;
9
+ equivalentInUsd?: string;
10
+ slippageInUsd?: string;
11
+ networkFee?: string;
12
+ networkFeeInUsd?: string;
13
+ swapFee?: string;
14
+ showFree?: boolean;
15
+ defaultOpen?: boolean;
16
+ }
17
+
18
+ const SwapDetails = React.forwardRef<HTMLDivElement, SwapDetailsProps>(
19
+ (
20
+ {
21
+ className,
22
+ swapFee,
23
+ slippageInUsd,
24
+ deposit,
25
+ receive,
26
+ equivalentInUsd,
27
+ networkFee,
28
+ networkFeeInUsd,
29
+ showFree = false,
30
+ defaultOpen = false,
31
+ ...props
32
+ },
33
+ ref
34
+ ) => {
35
+ const [isOpen, setIsOpen] = React.useState(defaultOpen);
36
+
37
+ return (
38
+ <Card ref={ref} className={cn("overflow-hidden", className)} {...props}>
39
+ {/* Collapsible header */}
40
+ <button
41
+ onClick={() => setIsOpen(!isOpen)}
42
+ className="w-full flex items-center justify-between p-1 hover:bg-muted/50 transition-colors"
43
+ >
44
+ <div className="flex items-center gap-2">
45
+ <span className="text-sm font-medium">
46
+ {deposit} = {receive}
47
+ </span>
48
+ <span className="text-xs text-muted-foreground">(≈ ${equivalentInUsd})</span>
49
+ </div>
50
+ <div className="flex items-center gap-1">
51
+ <span className="text-xs text-muted-foreground">≈ ${slippageInUsd}</span>
52
+ <ChevronDownIcon className="w-4 h-4" />
53
+ </div>
54
+ </button>
55
+
56
+ {/* Collapsible content */}
57
+ {isOpen && (
58
+ <div className="transition-all animate-in fade-in-0 duration-300">
59
+ {/* Swap Fee */}
60
+
61
+ <div className="flex items-center justify-between text-sm">
62
+ <span className="text-muted-foreground text-xs">Swap Fee</span>
63
+ {swapFee ? (
64
+ <span className="font-medium">{swapFee}%</span>
65
+ ) : (
66
+ <span className="text-xs px-2 py-1 bg-primary/10 text-primary rounded-full font-medium">
67
+ FREE
68
+ </span>
69
+ )}
70
+ </div>
71
+
72
+ {/* Network Fee */}
73
+ <div className="flex items-center justify-between text-sm">
74
+ <span className="text-muted-foreground text-xs">Network Fee</span>
75
+ <span className="font-medium">
76
+ <span className="text-xs text-foreground">{networkFee}</span>
77
+ {networkFeeInUsd ? (
78
+ <span className="text-xs text-muted-foreground">(≈ ${networkFeeInUsd})</span>
79
+ ) : (
80
+ <span className="text-xs px-2 py-1 bg-primary/10 text-primary rounded-full font-medium">
81
+ FREE
82
+ </span>
83
+ )}
84
+ </span>
85
+ </div>
86
+ </div>
87
+ )}
88
+ </Card>
89
+ );
90
+ }
91
+ );
92
+
93
+ SwapDetails.displayName = "SwapDetails";
94
+
95
+ export { SwapDetails };
@@ -0,0 +1,115 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Card } from "@/components/ui/card";
4
+ import { Input } from "@/components/ui/input";
5
+ import { Chip } from "@/components/ui/chip";
6
+ import { TokenSelector, type Token } from "./token-selector";
7
+
8
+ interface SwapInputProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
9
+ value?: string;
10
+ onChange?: (value: string) => void;
11
+ tokens: Token[];
12
+ selectedToken?: string;
13
+ onTokenChange?: (tokenAddress: string) => void;
14
+ usdValue?: string;
15
+ balance?: string;
16
+ onMaxClick?: () => void;
17
+ placeholder?: string;
18
+ disabled?: boolean;
19
+ hideTokenSelector?: boolean;
20
+ }
21
+
22
+ const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
23
+ (
24
+ {
25
+ className,
26
+ value = "",
27
+ onChange,
28
+ tokens,
29
+ selectedToken,
30
+ onTokenChange,
31
+ usdValue,
32
+ balance,
33
+ onMaxClick,
34
+ placeholder = "0",
35
+ disabled = false,
36
+ hideTokenSelector = false,
37
+ ...props
38
+ },
39
+ ref
40
+ ) => {
41
+ // Find the selected token for balance display
42
+ const currentToken = tokens.find((token) => token.address === selectedToken);
43
+
44
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
45
+ const newValue = e.target.value;
46
+ // Allow only numbers and decimal point
47
+ if (/^\d*\.?\d*$/.test(newValue)) {
48
+ onChange?.(newValue);
49
+ }
50
+ };
51
+
52
+ return (
53
+ <Card
54
+ ref={ref}
55
+ className={cn("space-y-3", disabled && "opacity-50", className)}
56
+ {...props}
57
+ >
58
+ {/* Main input row */}
59
+ <div className="flex items-center gap-3">
60
+ {/* Amount input */}
61
+ <div className="flex-1">
62
+ <Input
63
+ variant="nofocus"
64
+ value={value}
65
+ onChange={handleInputChange}
66
+ placeholder={placeholder}
67
+ disabled={disabled}
68
+ className="text-2xl font-medium border-none bg-transparent p-0 h-auto focus:ring-0"
69
+ />
70
+ </div>
71
+
72
+ {/* Token selector - conditionally rendered */}
73
+ {!hideTokenSelector && (
74
+ <TokenSelector
75
+ tokens={tokens}
76
+ value={selectedToken}
77
+ onValueChange={onTokenChange}
78
+ disabled={disabled}
79
+ placeholder="Select token"
80
+ />
81
+ )}
82
+ </div>
83
+
84
+ {/* Bottom row: USD value and balance with MAX */}
85
+ <div className="flex items-center justify-between text-sm text-muted-foreground">
86
+ <div>{usdValue && <span>≈ ${usdValue}</span>}</div>
87
+ <div className="flex items-center gap-2">
88
+ {balance && (
89
+ <span>
90
+ Balance: {balance} {currentToken?.symbol || ""}
91
+ </span>
92
+ )}
93
+ {onMaxClick && (
94
+ <Chip
95
+ variant="default"
96
+ size="xs"
97
+ onClick={onMaxClick}
98
+ className={cn(
99
+ "cursor-pointer hover:bg-primary hover:text-primary-foreground transition-colors text-xs",
100
+ disabled && "cursor-not-allowed opacity-50"
101
+ )}
102
+ >
103
+ MAX
104
+ </Chip>
105
+ )}
106
+ </div>
107
+ </div>
108
+ </Card>
109
+ );
110
+ }
111
+ );
112
+
113
+ SwapInput.displayName = "SwapInput";
114
+
115
+ export { SwapInput, type Token };
@@ -0,0 +1,72 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+ import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
4
+ import { useEffect } from "react";
5
+
6
+ interface Token {
7
+ icon: React.ReactNode;
8
+ symbol: string;
9
+ address: string;
10
+ }
11
+
12
+ interface TokenSelectorProps {
13
+ tokens: Token[];
14
+ value?: string;
15
+ onValueChange?: (value: string) => void;
16
+ placeholder?: string;
17
+ disabled?: boolean;
18
+ className?: string;
19
+ }
20
+
21
+ const TokenSelector = React.forwardRef<HTMLButtonElement, TokenSelectorProps>(
22
+ ({ tokens, value, onValueChange, placeholder = "Select token", disabled, className }, ref) => {
23
+ // Auto-select first token if no value is provided and tokens exist
24
+ const effectiveValue = value || (tokens.length > 0 ? tokens[0].address : undefined);
25
+
26
+ // Find the selected token to display in the trigger
27
+ const selectedToken = tokens.find((token) => token.address === effectiveValue);
28
+
29
+ // Handle value change and auto-select first token on mount
30
+ useEffect(() => {
31
+ if (!value && tokens.length > 0 && onValueChange) {
32
+ onValueChange(tokens[0].address);
33
+ }
34
+ }, [value, tokens, onValueChange]);
35
+
36
+ return (
37
+ <Select value={effectiveValue} onValueChange={onValueChange} disabled={disabled}>
38
+ <SelectTrigger
39
+ ref={ref}
40
+ className={cn(
41
+ "w-auto min-w-[80px] h-8 px-2 py-1 bg-muted hover:bg-muted/80 border-none rounded-md text-sm font-medium",
42
+ "focus:ring-2 focus:ring-primary/20 focus:outline-none",
43
+ className
44
+ )}
45
+ >
46
+ {selectedToken ? (
47
+ <div className="flex items-center gap-1.5">
48
+ <span className="w-4 h-4 flex items-center justify-center">{selectedToken.icon}</span>
49
+ <span className="text-sm font-medium">{selectedToken.symbol}</span>
50
+ </div>
51
+ ) : (
52
+ <span className="text-sm text-muted-foreground">{placeholder}</span>
53
+ )}
54
+ </SelectTrigger>
55
+ <SelectContent>
56
+ {tokens.map((token) => (
57
+ <SelectItem key={token.address} value={token.address} className="cursor-pointer">
58
+ <div className="flex items-center gap-2">
59
+ <span className="w-4 h-4 flex items-center justify-center">{token.icon}</span>
60
+ <span className="text-sm font-medium">{token.symbol}</span>
61
+ </div>
62
+ </SelectItem>
63
+ ))}
64
+ </SelectContent>
65
+ </Select>
66
+ );
67
+ }
68
+ );
69
+
70
+ TokenSelector.displayName = "TokenSelector";
71
+
72
+ export { TokenSelector, type Token };