@turtleclub/ui 0.7.0-beta.32 → 0.7.0-beta.34

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 (139) hide show
  1. package/dist/index.cjs +10331 -110
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +7652 -47844
  4. package/dist/index.js.map +1 -1
  5. package/dist/types/components/features/sidebar-layout.d.ts +2 -0
  6. package/dist/types/components/features/sidebar-layout.d.ts.map +1 -1
  7. package/dist/types/components/ui/chart.d.ts +1 -1
  8. package/dist/types/components/ui/chart.d.ts.map +1 -1
  9. package/package.json +26 -22
  10. package/.prettierrc.json +0 -4
  11. package/.turbo/turbo-build.log +0 -182
  12. package/CHANGELOG.md +0 -795
  13. package/components.json +0 -21
  14. package/src/components/charts/QUICK_REFERENCE.md +0 -323
  15. package/src/components/charts/README.md +0 -658
  16. package/src/components/charts/RECHARTS_FEATURES.md +0 -458
  17. package/src/components/charts/area-chart.tsx +0 -248
  18. package/src/components/charts/bar-chart.tsx +0 -362
  19. package/src/components/charts/index.ts +0 -4
  20. package/src/components/charts/pie-chart.tsx +0 -277
  21. package/src/components/charts/radial-chart.tsx +0 -312
  22. package/src/components/features/api-status/index.tsx +0 -23
  23. package/src/components/features/data-table/data-table.tsx +0 -538
  24. package/src/components/features/data-table/expand-toggle.tsx +0 -17
  25. package/src/components/features/data-table/fuzzy-filter.tsx +0 -34
  26. package/src/components/features/data-table/index.ts +0 -3
  27. package/src/components/features/data-table/item-info.tsx +0 -19
  28. package/src/components/features/data-table/skeleton.tsx +0 -23
  29. package/src/components/features/data-table/sort-dropdown.tsx +0 -118
  30. package/src/components/features/data-table/sortable-header.tsx +0 -37
  31. package/src/components/features/index.ts +0 -6
  32. package/src/components/features/page-heading.tsx +0 -27
  33. package/src/components/features/search-bar.tsx +0 -55
  34. package/src/components/features/segmented-navigation.tsx +0 -18
  35. package/src/components/features/sidebar-layout.tsx +0 -193
  36. package/src/components/features/turtle-tooltip.tsx +0 -67
  37. package/src/components/icons/arrow.tsx +0 -23
  38. package/src/components/icons/beta.tsx +0 -95
  39. package/src/components/icons/dot.tsx +0 -102
  40. package/src/components/icons/index.ts +0 -7
  41. package/src/components/icons/issue.tsx +0 -106
  42. package/src/components/icons/turtle.tsx +0 -156
  43. package/src/components/icons/update.tsx +0 -113
  44. package/src/components/icons/warning.tsx +0 -95
  45. package/src/components/molecules/index.ts +0 -9
  46. package/src/components/molecules/opportunity/index.ts +0 -10
  47. package/src/components/molecules/opportunity/opportunity-apr.tsx +0 -129
  48. package/src/components/molecules/opportunity/opportunity-disclaimer.tsx +0 -46
  49. package/src/components/molecules/opportunity/opportunity-rate-estimator.tsx +0 -62
  50. package/src/components/molecules/opportunity/opportunity-section.tsx +0 -113
  51. package/src/components/molecules/opportunity/opportunity-selector.tsx +0 -30
  52. package/src/components/molecules/opportunity/opportunity-type.tsx +0 -16
  53. package/src/components/molecules/route-details.tsx +0 -112
  54. package/src/components/molecules/slippage-selector.tsx +0 -200
  55. package/src/components/molecules/swap-details.tsx +0 -55
  56. package/src/components/molecules/swap-input.tsx +0 -186
  57. package/src/components/molecules/tabs.tsx +0 -79
  58. package/src/components/molecules/token-selector.tsx +0 -180
  59. package/src/components/molecules/tx-status.tsx +0 -312
  60. package/src/components/molecules/widget/asset-list/asset-filters.tsx +0 -113
  61. package/src/components/molecules/widget/asset-list/asset-list.tsx +0 -178
  62. package/src/components/molecules/widget/asset-list/asset-row.tsx +0 -45
  63. package/src/components/molecules/widget/asset-list/hooks/index.ts +0 -2
  64. package/src/components/molecules/widget/asset-list/hooks/use-asset-filtering.ts +0 -44
  65. package/src/components/molecules/widget/asset-list/hooks/use-asset-grouping.ts +0 -87
  66. package/src/components/molecules/widget/asset-list/index.ts +0 -3
  67. package/src/components/molecules/widget/base-selector.tsx +0 -121
  68. package/src/components/molecules/widget/campaign-item.tsx +0 -82
  69. package/src/components/molecules/widget/deal-item.tsx +0 -92
  70. package/src/components/molecules/widget/index.ts +0 -36
  71. package/src/components/molecules/widget/opportunity-item.tsx +0 -105
  72. package/src/components/molecules/widget/widget-item-stats.tsx +0 -50
  73. package/src/components/molecules/widget/widget-item.tsx +0 -139
  74. package/src/components/molecules/widget/widget-list-items.tsx +0 -86
  75. package/src/components/ui/alert-dialog.tsx +0 -163
  76. package/src/components/ui/animated-background/animated-background.tsx +0 -182
  77. package/src/components/ui/animated-background/index.ts +0 -1
  78. package/src/components/ui/avatar.tsx +0 -73
  79. package/src/components/ui/badge.tsx +0 -59
  80. package/src/components/ui/banner.tsx +0 -84
  81. package/src/components/ui/button.tsx +0 -100
  82. package/src/components/ui/card.tsx +0 -119
  83. package/src/components/ui/chart.tsx +0 -346
  84. package/src/components/ui/checkbox.tsx +0 -32
  85. package/src/components/ui/chip.tsx +0 -52
  86. package/src/components/ui/collapsible.tsx +0 -34
  87. package/src/components/ui/combobox.tsx +0 -730
  88. package/src/components/ui/command.tsx +0 -184
  89. package/src/components/ui/dialog.tsx +0 -129
  90. package/src/components/ui/dropdown.tsx +0 -316
  91. package/src/components/ui/field.tsx +0 -244
  92. package/src/components/ui/heading.tsx +0 -74
  93. package/src/components/ui/hover-card.tsx +0 -139
  94. package/src/components/ui/icon-animation.tsx +0 -82
  95. package/src/components/ui/icon-list.tsx +0 -168
  96. package/src/components/ui/index.ts +0 -48
  97. package/src/components/ui/info-card.tsx +0 -110
  98. package/src/components/ui/input-group.tsx +0 -170
  99. package/src/components/ui/input.tsx +0 -72
  100. package/src/components/ui/label-with-icon.tsx +0 -122
  101. package/src/components/ui/label.tsx +0 -24
  102. package/src/components/ui/multi-select.tsx +0 -1090
  103. package/src/components/ui/navigation-bar.tsx +0 -153
  104. package/src/components/ui/navigation-menu.tsx +0 -188
  105. package/src/components/ui/opportunity-details-v1.tsx +0 -104
  106. package/src/components/ui/pagination.tsx +0 -127
  107. package/src/components/ui/popover.tsx +0 -48
  108. package/src/components/ui/scroll-area.tsx +0 -64
  109. package/src/components/ui/segment-control.tsx +0 -146
  110. package/src/components/ui/select.tsx +0 -199
  111. package/src/components/ui/separator.tsx +0 -26
  112. package/src/components/ui/sheet.tsx +0 -139
  113. package/src/components/ui/sidebar.tsx +0 -728
  114. package/src/components/ui/skeleton.tsx +0 -14
  115. package/src/components/ui/slider.tsx +0 -58
  116. package/src/components/ui/sonner.tsx +0 -24
  117. package/src/components/ui/switch.tsx +0 -29
  118. package/src/components/ui/table-shadcn.tsx +0 -110
  119. package/src/components/ui/table.tsx +0 -117
  120. package/src/components/ui/textarea.tsx +0 -22
  121. package/src/components/ui/toggle-group.tsx +0 -71
  122. package/src/components/ui/toggle.tsx +0 -47
  123. package/src/components/ui/tooltip.tsx +0 -66
  124. package/src/hooks/index.ts +0 -1
  125. package/src/hooks/useIsMobile.ts +0 -77
  126. package/src/index.ts +0 -16
  127. package/src/lib/utils.ts +0 -6
  128. package/src/styles/globals.css +0 -181
  129. package/src/styles/themes/index.css +0 -9
  130. package/src/styles/themes/semantic.css +0 -117
  131. package/src/styles/tokens/colors.css +0 -124
  132. package/src/styles/tokens/index.css +0 -15
  133. package/src/styles/tokens/radius.css +0 -18
  134. package/src/styles/tokens/spacing.css +0 -58
  135. package/src/styles/tokens/typography.css +0 -87
  136. package/src/tokens/index.ts +0 -108
  137. package/tsconfig.json +0 -20
  138. package/vite.config.js +0 -49
  139. /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 };