@turtleclub/ui 0.7.0-beta.33 → 0.7.0-beta.35

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 (135) hide show
  1. package/dist/index.cjs +10331 -110
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +7227 -48026
  4. package/dist/index.js.map +1 -1
  5. package/package.json +17 -11
  6. package/.prettierrc.json +0 -4
  7. package/.turbo/turbo-build.log +0 -182
  8. package/CHANGELOG.md +0 -801
  9. package/components.json +0 -21
  10. package/src/components/charts/QUICK_REFERENCE.md +0 -323
  11. package/src/components/charts/README.md +0 -658
  12. package/src/components/charts/RECHARTS_FEATURES.md +0 -458
  13. package/src/components/charts/area-chart.tsx +0 -248
  14. package/src/components/charts/bar-chart.tsx +0 -362
  15. package/src/components/charts/index.ts +0 -4
  16. package/src/components/charts/pie-chart.tsx +0 -277
  17. package/src/components/charts/radial-chart.tsx +0 -312
  18. package/src/components/features/api-status/index.tsx +0 -23
  19. package/src/components/features/data-table/data-table.tsx +0 -538
  20. package/src/components/features/data-table/expand-toggle.tsx +0 -17
  21. package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
  22. package/src/components/features/data-table/index.ts +0 -3
  23. package/src/components/features/data-table/item-info.tsx +0 -19
  24. package/src/components/features/data-table/skeleton.tsx +0 -23
  25. package/src/components/features/data-table/sort-dropdown.tsx +0 -118
  26. package/src/components/features/data-table/sortable-header.tsx +0 -37
  27. package/src/components/features/index.ts +0 -6
  28. package/src/components/features/page-heading.tsx +0 -27
  29. package/src/components/features/search-bar.tsx +0 -55
  30. package/src/components/features/segmented-navigation.tsx +0 -18
  31. package/src/components/features/sidebar-layout.tsx +0 -279
  32. package/src/components/features/turtle-tooltip.tsx +0 -67
  33. package/src/components/icons/arrow.tsx +0 -23
  34. package/src/components/icons/beta.tsx +0 -95
  35. package/src/components/icons/dot.tsx +0 -102
  36. package/src/components/icons/index.ts +0 -7
  37. package/src/components/icons/issue.tsx +0 -106
  38. package/src/components/icons/turtle.tsx +0 -156
  39. package/src/components/icons/update.tsx +0 -113
  40. package/src/components/icons/warning.tsx +0 -95
  41. package/src/components/molecules/index.ts +0 -9
  42. package/src/components/molecules/opportunity/index.ts +0 -10
  43. package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
  44. package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
  45. package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
  46. package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
  47. package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
  48. package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
  49. package/src/components/molecules/route-details.tsx +0 -112
  50. package/src/components/molecules/slippage-selector.tsx +0 -200
  51. package/src/components/molecules/swap-details.tsx +0 -55
  52. package/src/components/molecules/swap-input.tsx +0 -186
  53. package/src/components/molecules/tabs.tsx +0 -79
  54. package/src/components/molecules/token-selector.tsx +0 -180
  55. package/src/components/molecules/tx-status.tsx +0 -312
  56. package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
  57. package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
  58. package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
  59. package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
  60. package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
  61. package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
  62. package/src/components/molecules/widget/asset-list/index.ts +0 -3
  63. package/src/components/molecules/widget/base-selector.tsx +0 -121
  64. package/src/components/molecules/widget/campaign-item.tsx +0 -82
  65. package/src/components/molecules/widget/deal-item.tsx +0 -92
  66. package/src/components/molecules/widget/index.ts +0 -36
  67. package/src/components/molecules/widget/opportunity-item.tsx +0 -105
  68. package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
  69. package/src/components/molecules/widget/widget-item.tsx +0 -139
  70. package/src/components/molecules/widget/widget-list-items.tsx +0 -86
  71. package/src/components/ui/alert-dialog.tsx +0 -163
  72. package/src/components/ui/animated-background/animated-background.tsx +0 -182
  73. package/src/components/ui/animated-background/index.ts +0 -1
  74. package/src/components/ui/avatar.tsx +0 -73
  75. package/src/components/ui/badge.tsx +0 -59
  76. package/src/components/ui/banner.tsx +0 -84
  77. package/src/components/ui/button.tsx +0 -100
  78. package/src/components/ui/card.tsx +0 -119
  79. package/src/components/ui/chart.tsx +0 -346
  80. package/src/components/ui/checkbox.tsx +0 -32
  81. package/src/components/ui/chip.tsx +0 -52
  82. package/src/components/ui/collapsible.tsx +0 -34
  83. package/src/components/ui/combobox.tsx +0 -730
  84. package/src/components/ui/command.tsx +0 -184
  85. package/src/components/ui/dialog.tsx +0 -129
  86. package/src/components/ui/dropdown.tsx +0 -316
  87. package/src/components/ui/field.tsx +0 -244
  88. package/src/components/ui/heading.tsx +0 -74
  89. package/src/components/ui/hover-card.tsx +0 -139
  90. package/src/components/ui/icon-animation.tsx +0 -82
  91. package/src/components/ui/icon-list.tsx +0 -168
  92. package/src/components/ui/index.ts +0 -48
  93. package/src/components/ui/info-card.tsx +0 -110
  94. package/src/components/ui/input-group.tsx +0 -170
  95. package/src/components/ui/input.tsx +0 -72
  96. package/src/components/ui/label-with-icon.tsx +0 -122
  97. package/src/components/ui/label.tsx +0 -24
  98. package/src/components/ui/multi-select.tsx +0 -1090
  99. package/src/components/ui/navigation-bar.tsx +0 -153
  100. package/src/components/ui/navigation-menu.tsx +0 -188
  101. package/src/components/ui/opportunity-details-v1.tsx +0 -104
  102. package/src/components/ui/pagination.tsx +0 -127
  103. package/src/components/ui/popover.tsx +0 -48
  104. package/src/components/ui/scroll-area.tsx +0 -64
  105. package/src/components/ui/segment-control.tsx +0 -146
  106. package/src/components/ui/select.tsx +0 -199
  107. package/src/components/ui/separator.tsx +0 -26
  108. package/src/components/ui/sheet.tsx +0 -139
  109. package/src/components/ui/sidebar.tsx +0 -728
  110. package/src/components/ui/skeleton.tsx +0 -14
  111. package/src/components/ui/slider.tsx +0 -58
  112. package/src/components/ui/sonner.tsx +0 -24
  113. package/src/components/ui/switch.tsx +0 -29
  114. package/src/components/ui/table-shadcn.tsx +0 -110
  115. package/src/components/ui/table.tsx +0 -117
  116. package/src/components/ui/textarea.tsx +0 -22
  117. package/src/components/ui/toggle-group.tsx +0 -71
  118. package/src/components/ui/toggle.tsx +0 -47
  119. package/src/components/ui/tooltip.tsx +0 -66
  120. package/src/hooks/index.ts +0 -1
  121. package/src/hooks/useIsMobile.ts +0 -77
  122. package/src/index.ts +0 -16
  123. package/src/lib/utils.ts +0 -6
  124. package/src/styles/globals.css +0 -181
  125. package/src/styles/themes/index.css +0 -9
  126. package/src/styles/themes/semantic.css +0 -117
  127. package/src/styles/tokens/colors.css +0 -124
  128. package/src/styles/tokens/index.css +0 -15
  129. package/src/styles/tokens/radius.css +0 -18
  130. package/src/styles/tokens/spacing.css +0 -58
  131. package/src/styles/tokens/typography.css +0 -87
  132. package/src/tokens/index.ts +0 -108
  133. package/tsconfig.json +0 -20
  134. package/vite.config.js +0 -49
  135. /package/{src/images/enso.png → dist/enso-22FJ4GNK.png} +0 -0
@@ -1,200 +0,0 @@
1
- import * as React from "react";
2
- import { useState, useCallback, useEffect } from "react";
3
- import { Edit3, InfoIcon } from "lucide-react";
4
- import { cn } from "@/lib/utils";
5
- import { Tooltip, TooltipContent, TooltipTrigger } from "../ui";
6
-
7
- const SLIPPAGE_LIMITS = {
8
- MIN: 0.1,
9
- MAX: 50,
10
- } as const;
11
-
12
- const VALIDATION_REGEX = /^\d*\.?\d*$/;
13
-
14
- const ICON_SIZE = 10;
15
- const INPUT_WIDTH = "w-10";
16
-
17
- interface SlippageSelectorProps {
18
- /** Current slippage value as percentage */
19
- value: number;
20
- /** Callback when slippage value changes */
21
- onChange: (value: number) => void;
22
- /** Additional CSS classes */
23
- className?: string;
24
- /** Label text for the selector */
25
- label?: string;
26
- /** Minimum allowed slippage value */
27
- minValue?: number;
28
- /** Maximum allowed slippage value */
29
- maxValue?: number;
30
- }
31
-
32
- // Validation utilities
33
- const isValidSlippageInput = (input: string): boolean =>
34
- VALIDATION_REGEX.test(input);
35
-
36
- const isValidSlippageValue = (
37
- value: number,
38
- min: number,
39
- max: number,
40
- ): boolean => !isNaN(value) && value >= min && value <= max;
41
-
42
- const formatSlippageValue = (value: number): string => value.toString();
43
-
44
- export function SlippageSelector({
45
- value,
46
- onChange,
47
- className,
48
- label = "Max Slippage",
49
- minValue = SLIPPAGE_LIMITS.MIN,
50
- maxValue = SLIPPAGE_LIMITS.MAX,
51
- }: SlippageSelectorProps) {
52
- const [isEditing, setIsEditing] = useState(false);
53
- const [inputValue, setInputValue] = useState(formatSlippageValue(value));
54
-
55
- useEffect(() => {
56
- if (!isEditing) {
57
- setInputValue(formatSlippageValue(value));
58
- }
59
- }, [value, isEditing]);
60
-
61
- const startEditing = useCallback(() => {
62
- setIsEditing(true);
63
- setInputValue(formatSlippageValue(value * 100));
64
- }, [value]);
65
-
66
- const cancelEditing = useCallback(() => {
67
- setInputValue(formatSlippageValue(value * 100));
68
- setIsEditing(false);
69
- }, [value]);
70
-
71
- const submitValue = useCallback(() => {
72
- const numValue = parseFloat(inputValue);
73
-
74
- if (isValidSlippageValue(numValue, minValue, maxValue)) {
75
- // Convert to percentage
76
- onChange(numValue / 100);
77
- setIsEditing(false);
78
- } else {
79
- // Reset to current value if invalid
80
- cancelEditing();
81
- }
82
- }, [inputValue, minValue, maxValue, onChange, cancelEditing]);
83
-
84
- const handleInputChange = useCallback(
85
- (e: React.ChangeEvent<HTMLInputElement>) => {
86
- const newValue = e.target.value;
87
-
88
- // First check if the input format is valid (regex validation)
89
- if (!isValidSlippageInput(newValue)) {
90
- return;
91
- }
92
-
93
- // If it's an empty string or just a decimal point, allow it for UX
94
- if (newValue === "" || newValue === ".") {
95
- setInputValue(newValue);
96
- return;
97
- }
98
-
99
- // Check if the numeric value exceeds the maximum limit
100
- const numValue = parseFloat(newValue);
101
- if (!isNaN(numValue) && numValue > maxValue) {
102
- return; // Block input that exceeds maxValue
103
- }
104
-
105
- setInputValue(newValue);
106
- },
107
- [maxValue],
108
- );
109
-
110
- const handleKeyDown = useCallback(
111
- (e: React.KeyboardEvent) => {
112
- switch (e.key) {
113
- case "Enter":
114
- e.preventDefault();
115
- submitValue();
116
- break;
117
- case "Escape":
118
- e.preventDefault();
119
- cancelEditing();
120
- break;
121
- }
122
- },
123
- [submitValue, cancelEditing],
124
- );
125
-
126
- const handleBlur = useCallback(() => {
127
- submitValue();
128
- }, [submitValue]);
129
-
130
- return (
131
- <div className={cn("flex items-center justify-between", className)}>
132
- <span
133
- className="text-muted-foreground text-xs font-medium"
134
- id="slippage-label"
135
- >
136
- <div className="flex items-center">
137
- {label}
138
- <Tooltip>
139
- <TooltipTrigger>
140
- <InfoIcon className="ml-1 size-3" />
141
- </TooltipTrigger>
142
- <TooltipContent>
143
- <p className="text-xs">
144
- Must be between <span className="font-bold">0.1%</span> and{" "}
145
- <span className="font-bold">50%</span>
146
- </p>
147
- </TooltipContent>
148
- </Tooltip>
149
- </div>
150
- </span>
151
-
152
- <div className="flex items-center gap-1">
153
- {isEditing ? (
154
- <input
155
- type="text"
156
- value={inputValue}
157
- onChange={handleInputChange}
158
- onKeyDown={handleKeyDown}
159
- onBlur={handleBlur}
160
- className={cn(
161
- INPUT_WIDTH,
162
- "border-none bg-transparent text-right text-xs outline-none",
163
- "text-foreground focus:ring-0 focus:outline-none",
164
- )}
165
- aria-labelledby="slippage-label"
166
- aria-describedby="slippage-range"
167
- autoFocus
168
- autoComplete="off"
169
- spellCheck={false}
170
- />
171
- ) : (
172
- <span
173
- className="text-foreground text-xs font-medium"
174
- aria-live="polite"
175
- >
176
- {value * 100}%
177
- </span>
178
- )}
179
-
180
- <button
181
- onClick={startEditing}
182
- className={cn(
183
- "hover:bg-muted rounded p-1 transition-colors",
184
- "text-muted-foreground hover:text-foreground",
185
- "focus:ring-ring focus:ring-2 focus:ring-offset-2 focus:outline-none",
186
- )}
187
- aria-label={`Edit slippage value. Current value: ${value * 100}%`}
188
- type="button"
189
- >
190
- <Edit3 size={ICON_SIZE} />
191
- </button>
192
- </div>
193
-
194
- {/* Screen reader helper text */}
195
- <span id="slippage-range" className="sr-only">
196
- Enter a value between {minValue}% and {maxValue}%
197
- </span>
198
- </div>
199
- );
200
- }
@@ -1,55 +0,0 @@
1
- import * as React from "react";
2
- import { SlippageSelector } from "@/index";
3
-
4
- interface SwapDetailsProps extends React.HTMLAttributes<HTMLDivElement> {
5
- details: {
6
- deposit?: string;
7
- receive?: string;
8
- equivalentInUsd?: string;
9
- defaultOpen?: boolean;
10
- slippage?: number;
11
- setSlippage?: (slippage: number) => void;
12
- };
13
- }
14
-
15
- const SwapDetails = React.forwardRef<HTMLDivElement, SwapDetailsProps>(
16
- ({ className, details, ...props }, ref) => {
17
- const { deposit, receive, equivalentInUsd, slippage, setSlippage } =
18
- details;
19
-
20
- return (
21
- <div className="text-foreground animate-in fade-in-0 space-y-2 px-2 py-4 transition-all duration-300">
22
- {/* Amount Out*/}
23
- <div className="flex items-center justify-between text-sm">
24
- <span className="text-foreground text-sm font-semibold">
25
- Amount Out
26
- </span>
27
- <span className="font-medium">{receive}</span>
28
- </div>
29
-
30
- {/* Slippage Selector */}
31
- {slippage !== undefined && setSlippage && (
32
- <SlippageSelector
33
- className="text-sm"
34
- value={slippage}
35
- onChange={setSlippage}
36
- />
37
- )}
38
-
39
- {/* Amount Out in USD */}
40
- <div className="flex items-center gap-2">
41
- <span className="text-sm font-medium">
42
- {deposit} = {receive}
43
- </span>
44
- <span className="text-muted-foreground text-xs">
45
- (≈ ${equivalentInUsd})
46
- </span>
47
- </div>
48
- </div>
49
- );
50
- },
51
- );
52
-
53
- SwapDetails.displayName = "SwapDetails";
54
-
55
- export { SwapDetails };
@@ -1,186 +0,0 @@
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
- import { BaseSelector } from "./widget/base-selector";
8
-
9
- interface SwapInputProps extends Omit<
10
- React.HTMLAttributes<HTMLDivElement>,
11
- "onChange"
12
- > {
13
- value?: string;
14
- tokens: Token[];
15
- selectedToken?: string;
16
- usdValue?: string;
17
- balance?: string;
18
- placeholder?: string;
19
- disabled?: boolean;
20
- hideTokenSelector?: boolean;
21
- isWalletConnected?: boolean;
22
- onChange?: (value: string) => void;
23
- onMaxClick?: () => void;
24
- onTokenChange?: (tokenAddress: string) => void;
25
- // New props for custom token selector
26
- useCustomTokenSelector?: boolean;
27
- selectedTokenData?: Token | null;
28
- onCustomTokenSelectorClick?: () => void;
29
- showTokenSelectorBalance?: boolean;
30
- showInputBalance?: boolean;
31
- triggerTokenSelectorWidth?: number;
32
- }
33
-
34
- const SwapInput = React.forwardRef<HTMLDivElement, SwapInputProps>(
35
- (
36
- {
37
- className,
38
- value = "",
39
- tokens,
40
- selectedToken,
41
- usdValue,
42
- balance,
43
- placeholder = "0",
44
- disabled = false,
45
- hideTokenSelector = false,
46
- isWalletConnected = true,
47
- onChange,
48
- onMaxClick,
49
- onTokenChange,
50
- // New props
51
- useCustomTokenSelector = false,
52
- selectedTokenData,
53
- onCustomTokenSelectorClick,
54
- showTokenSelectorBalance = true,
55
- showInputBalance = true,
56
- triggerTokenSelectorWidth,
57
- ...props
58
- },
59
- ref,
60
- ) => {
61
- // Find the selected token for balance display
62
- const currentToken = useCustomTokenSelector
63
- ? selectedTokenData
64
- : tokens.find((token) => token.address === selectedToken);
65
-
66
- const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
67
- const newValue = e.target.value;
68
- // Allow only numbers and decimal point
69
- if (/^\d*\.?\d*$/.test(newValue)) {
70
- onChange?.(newValue);
71
- }
72
- };
73
-
74
- // If wallet is not connected, show connection message
75
- if (!isWalletConnected) {
76
- return (
77
- <Card
78
- ref={ref}
79
- className={cn("flex items-center justify-center py-8", className)}
80
- {...props}
81
- >
82
- <div className="text-center">
83
- <p className="text-muted-foreground text-sm">
84
- Connect your wallet to continue
85
- </p>
86
- </div>
87
- </Card>
88
- );
89
- }
90
-
91
- return (
92
- <Card
93
- variant="shadow"
94
- ref={ref}
95
- className={cn("space-y-3", disabled && "opacity-50", className)}
96
- {...props}
97
- >
98
- {/* Main input row */}
99
- <div className="flex items-center gap-6">
100
- {/* Amount input */}
101
- <div className="flex-1">
102
- <Input
103
- variant="nofocus"
104
- value={value}
105
- onChange={handleInputChange}
106
- placeholder={placeholder}
107
- disabled={disabled}
108
- className="text-foreground/90 h-auto border-none bg-transparent p-0 text-4xl font-[400] shadow-none focus:ring-0"
109
- />
110
- </div>
111
-
112
- {/* Token selector - conditionally rendered */}
113
- {!hideTokenSelector &&
114
- (useCustomTokenSelector ? (
115
- <BaseSelector
116
- token={currentToken ?? undefined}
117
- text={currentToken?.symbol || ""}
118
- onClick={onCustomTokenSelectorClick}
119
- placeholder="Select token"
120
- size="sm"
121
- variant="bordered"
122
- className={cn(
123
- "rounded-full",
124
- disabled && "cursor-not-allowed opacity-50",
125
- )}
126
- showIcon={true}
127
- />
128
- ) : (
129
- <TokenSelector
130
- tokens={tokens}
131
- value={selectedToken}
132
- onValueChange={onTokenChange}
133
- disabled={disabled}
134
- placeholder="Select token"
135
- size="sm"
136
- variant="default"
137
- className="rounded-full"
138
- showBalance={showTokenSelectorBalance}
139
- triggerWidth={triggerTokenSelectorWidth}
140
- />
141
- ))}
142
- </div>
143
-
144
- {/* Bottom row: USD value and balance with MAX */}
145
- {showInputBalance && (
146
- <div className="text-muted-foreground flex items-center justify-between text-sm">
147
- <div>
148
- {usdValue ? (
149
- <span>≈ {usdValue}</span>
150
- ) : value ? (
151
- <span className="text-destructive/50">
152
- Insufficient balance
153
- </span>
154
- ) : null}
155
- </div>
156
- <div className="flex items-center gap-2">
157
- {balance && (
158
- <span className="text-sm">
159
- Balance: {balance} {currentToken?.symbol || ""}
160
- </span>
161
- )}
162
- {onMaxClick && (
163
- <Chip
164
- variant="default"
165
- size="xs"
166
- onClick={onMaxClick}
167
- className={cn(
168
- "hover:bg-primary hover:text-primary-foreground h-5 cursor-pointer px-2 py-0.5 text-xs transition-colors",
169
- disabled && "cursor-not-allowed opacity-50",
170
- )}
171
- >
172
- MAX
173
- </Chip>
174
- )}
175
- </div>
176
- </div>
177
- )}
178
- </Card>
179
- );
180
- },
181
- );
182
-
183
- SwapInput.displayName = "SwapInput";
184
-
185
- export { SwapInput };
186
- export type { Token, SwapInputProps };
@@ -1,79 +0,0 @@
1
- import React, { ReactNode, useMemo, useState } from "react";
2
- import { NavigationBar, NavigationItem } from "../ui";
3
- import { cn } from "@/lib/utils";
4
-
5
- interface Tab {
6
- value: string;
7
- label: string;
8
- content: ReactNode;
9
- disabled?: boolean;
10
- }
11
-
12
- interface TabsProps {
13
- tabs: Tab[];
14
- defaultValue?: string;
15
- value?: string;
16
- onValueChange?: (value: string) => void;
17
- layout?: (content: ReactNode) => ReactNode;
18
- className?: string;
19
- }
20
-
21
- const Tabs = ({
22
- tabs,
23
- defaultValue,
24
- value,
25
- onValueChange,
26
- layout,
27
- className,
28
- }: TabsProps) => {
29
- const [internalValue, setInternalValue] = useState(
30
- defaultValue ||
31
- value ||
32
- tabs.find((tab) => !tab.disabled)?.value ||
33
- tabs[0]?.value,
34
- );
35
- const activeTab = value ?? internalValue;
36
-
37
- const handleTabChange = (newValue: string) => {
38
- if (value === undefined) {
39
- setInternalValue(newValue);
40
- }
41
- onValueChange?.(newValue);
42
- };
43
-
44
- const activeTabContent = useMemo(
45
- () => tabs.find((tab) => tab.value === activeTab)?.content,
46
- [activeTab, tabs],
47
- );
48
-
49
- const layoutContent = useMemo(() => {
50
- if (layout) {
51
- return layout(activeTabContent);
52
- }
53
- return (
54
- <div className="min-h-0 flex-1 overflow-hidden">{activeTabContent}</div>
55
- );
56
- }, [activeTabContent, layout]);
57
-
58
- return (
59
- <div className={cn("flex flex-col", className)}>
60
- <NavigationBar variant="menuBar" activeValue={activeTab} className="mb-2">
61
- {tabs.map((tab) => (
62
- <NavigationItem
63
- key={tab.value}
64
- value={tab.value}
65
- active={tab.value === activeTab}
66
- onClick={() => !tab.disabled && handleTabChange(tab.value)}
67
- disabled={tab.disabled}
68
- variant="menuBarDefault"
69
- >
70
- {tab.label}
71
- </NavigationItem>
72
- ))}
73
- </NavigationBar>
74
- {layoutContent}
75
- </div>
76
- );
77
- };
78
-
79
- export { Tabs, type TabsProps, type Tab };
@@ -1,180 +0,0 @@
1
- "use client";
2
- import * as React from "react";
3
- import { cn } from "@/lib/utils";
4
- import {
5
- Select,
6
- SelectContent,
7
- SelectItem,
8
- SelectTrigger,
9
- } from "@/components/ui/select";
10
- import { useEffect } from "react";
11
-
12
- interface Token {
13
- symbol: string;
14
- address: string;
15
- logoUrl?: string;
16
- // Optional for backwards compatibility
17
- icon?: React.ReactNode;
18
- balance?: string;
19
- }
20
-
21
- interface TokenSelectorProps {
22
- tokens: Token[];
23
- value?: string;
24
- onValueChange?: (value: string) => void;
25
- placeholder?: string;
26
- disabled?: boolean;
27
- className?: string;
28
- variant?: "default" | "bordered";
29
- size?: "xs" | "sm" | "default";
30
- showBalance?: boolean;
31
- triggerWidth?: number;
32
- }
33
-
34
- const TokenSelector = ({
35
- tokens,
36
- value,
37
- onValueChange,
38
- placeholder = "Select token",
39
- disabled,
40
- className,
41
- variant = "bordered",
42
- size = "default",
43
- showBalance = true,
44
- triggerWidth,
45
- }: TokenSelectorProps) => {
46
- // Auto-select first token if no value is provided and tokens exist
47
- const effectiveValue =
48
- value || (tokens.length > 0 ? tokens[0].address : undefined);
49
-
50
- // Find the selected token to display in the trigger
51
- const selectedToken = tokens.find(
52
- (token) => token.address === effectiveValue,
53
- );
54
-
55
- // Helper to get icon (either provided icon or generated from logoUrl)
56
- const getTokenIcon = (token: Token) => {
57
- if (token.icon) return token.icon;
58
- if (token.logoUrl) {
59
- return (
60
- <img
61
- src={token.logoUrl}
62
- alt={token.symbol}
63
- className="size-full rounded-full"
64
- />
65
- );
66
- }
67
- return null;
68
- };
69
-
70
- // Handle value change and auto-select first token on mount
71
- useEffect(() => {
72
- if (!value && tokens.length > 0 && onValueChange) {
73
- onValueChange(tokens[0].address);
74
- }
75
- }, [value, tokens, onValueChange]);
76
-
77
- return (
78
- <Select
79
- value={effectiveValue}
80
- onValueChange={onValueChange}
81
- disabled={disabled}
82
- >
83
- <SelectTrigger
84
- // variant={variant}
85
- // size={size}
86
- style={triggerWidth ? { width: `${triggerWidth}px` } : undefined}
87
- className={cn(
88
- "bg-muted hover:bg-muted/80 w-auto min-w-[80px] rounded-md font-medium",
89
- "focus:ring-primary/20 focus:ring-2 focus:outline-none",
90
- size === "xs" && "min-w-[80px] gap-2",
91
- size === "sm" && "min-w-[75px] gap-1.5",
92
- size === "default" && "min-w-[80px] gap-1.5",
93
- className,
94
- )}
95
- >
96
- {selectedToken ? (
97
- <div
98
- className={cn(
99
- "flex items-center",
100
- size === "xs" && "gap-2",
101
- size === "sm" && "gap-1.5",
102
- size === "default" && "gap-1.5",
103
- )}
104
- >
105
- <span
106
- className={cn(
107
- "flex items-center justify-center",
108
- size === "xs" && "h-3 w-3.5",
109
- size === "sm" && "h-3.5 w-4",
110
- size === "default" && "h-4 w-5",
111
- )}
112
- >
113
- {getTokenIcon(selectedToken)}
114
- </span>
115
- <span
116
- className={cn(
117
- "font-medium",
118
- size === "xs" && "text-xs",
119
- size === "sm" && "text-sm",
120
- size === "default" && "text-sm",
121
- )}
122
- >
123
- {selectedToken.symbol}
124
- </span>
125
- </div>
126
- ) : (
127
- <span
128
- className={cn(
129
- "text-muted-foreground",
130
- size === "xs" && "text-xs",
131
- size === "sm" && "text-sm",
132
- size === "default" && "text-sm",
133
- )}
134
- >
135
- {placeholder}
136
- </span>
137
- )}
138
- </SelectTrigger>
139
- <SelectContent className="border-muted gap-2 border">
140
- {tokens.map((token) => (
141
- <SelectItem
142
- key={token.address}
143
- value={token.address}
144
- className="cursor-pointer py-2"
145
- >
146
- <div className="flex flex-col gap-2">
147
- <div className="flex items-center gap-2">
148
- <span
149
- className={cn(
150
- "flex items-center justify-center",
151
- size === "xs" && "h-3.5 w-3.5",
152
- size === "sm" && "h-4 w-4",
153
- size === "default" && "h-4 w-5",
154
- )}
155
- >
156
- {getTokenIcon(token)}
157
- </span>
158
- <span
159
- className={cn(
160
- "font-medium",
161
- size === "xs" && "text-xs",
162
- size === "sm" && "text-sm",
163
- size === "default" && "text-sm",
164
- )}
165
- >
166
- {token.symbol}
167
- </span>
168
- </div>
169
- {showBalance && (
170
- <span className="text-xs text-white/80">≈ {token.balance}</span>
171
- )}
172
- </div>
173
- </SelectItem>
174
- ))}
175
- </SelectContent>
176
- </Select>
177
- );
178
- };
179
-
180
- export { TokenSelector, type Token };