@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,312 +0,0 @@
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
67
- className={size}
68
- fill="none"
69
- stroke="currentColor"
70
- viewBox="0 0 24 24"
71
- >
72
- <path
73
- strokeLinecap="round"
74
- strokeLinejoin="round"
75
- strokeWidth={2}
76
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
77
- />
78
- </svg>
79
- );
80
-
81
- const StepCheckIcon = () => (
82
- <svg
83
- className="h-4 w-4"
84
- fill="none"
85
- stroke="currentColor"
86
- viewBox="0 0 24 24"
87
- >
88
- <path
89
- strokeLinecap="round"
90
- strokeLinejoin="round"
91
- strokeWidth={2}
92
- d="M5 13l4 4L19 7"
93
- />
94
- </svg>
95
- );
96
-
97
- const formatTxHash = (hash: string) => {
98
- if (hash.length <= 12) return hash;
99
- return `${hash.slice(0, 6)}...${hash.slice(-6)}`;
100
- };
101
-
102
- return (
103
- <Card
104
- ref={ref}
105
- className={cn(
106
- "space-y-3 p-4 text-center",
107
- variant === "compact" && "space-y-2 p-3",
108
- className,
109
- )}
110
- {...props}
111
- >
112
- {/* Animation */}
113
- <div className="flex justify-center">
114
- <IconAnimation
115
- spinning={!completed && !cancelled}
116
- size={variant === "compact" ? "sm" : "default"}
117
- >
118
- {cancelled ? (
119
- <XIcon
120
- className={cn(
121
- variant === "compact" ? "h-5 w-5" : "h-6 w-6",
122
- "text-destructive",
123
- )}
124
- />
125
- ) : completed ? (
126
- <CheckIcon
127
- className={cn(
128
- variant === "compact" ? "h-5 w-5" : "h-6 w-6",
129
- "text-primary",
130
- )}
131
- />
132
- ) : (
133
- <TurtleIcon
134
- className={cn(
135
- variant === "compact" ? "h-5 w-5" : "h-6 w-6",
136
- "text-primary",
137
- )}
138
- />
139
- )}
140
- </IconAnimation>
141
- </div>
142
-
143
- {/* Title and description */}
144
- <div className="space-y-1">
145
- <h3
146
- className={cn(
147
- "text-foreground font-medium",
148
- variant === "compact" ? "text-xs" : "text-sm",
149
- )}
150
- >
151
- {title || defaultTitle}
152
- </h3>
153
- <p
154
- className={cn(
155
- "text-muted-foreground",
156
- variant === "compact" ? "text-xs" : "text-xs",
157
- )}
158
- >
159
- {description || defaultDescription}
160
- </p>
161
- </div>
162
-
163
- {/* Transaction summary (completed state) */}
164
- {completed && !cancelled && (amount || token || protocol) && (
165
- <div className="bg-muted/50 space-y-1 rounded-lg p-2">
166
- {amount && token && (
167
- <div className="text-xs font-medium">
168
- {amount} {token}
169
- </div>
170
- )}
171
- {protocol && (
172
- <div className="text-muted-foreground text-xs">
173
- via {protocol}
174
- </div>
175
- )}
176
- </div>
177
- )}
178
-
179
- {/* Transaction hash link */}
180
- {txHash && !cancelled && (
181
- <div className="space-y-1">
182
- <div className="text-muted-foreground text-xs">
183
- Transaction Hash
184
- </div>
185
- {explorerUrl ? (
186
- <a
187
- href={`${explorerUrl}/tx/${txHash}`}
188
- target="_blank"
189
- rel="noopener noreferrer"
190
- className="text-primary hover:text-primary/80 inline-flex items-center gap-2 text-xs transition-colors"
191
- >
192
- {formatTxHash(txHash)}
193
- <ExternalLinkIcon />
194
- </a>
195
- ) : (
196
- <div className="text-foreground font-mono text-xs">
197
- {formatTxHash(txHash)}
198
- </div>
199
- )}
200
- </div>
201
- )}
202
-
203
- {/* Estimated time (pending state) */}
204
- {!completed && !cancelled && estimatedTime && (
205
- <div className="text-muted-foreground text-xs">
206
- Estimated time: {estimatedTime}
207
- </div>
208
- )}
209
-
210
- {/* Progress steps (pending state) */}
211
- {!completed && !cancelled && steps.length > 0 && (
212
- <div className="border-border space-y-2 border-t pt-2">
213
- <div className="text-muted-foreground text-left text-xs font-medium">
214
- Progress
215
- </div>
216
- <div className="space-y-2">
217
- {steps.map((step, index) => (
218
- <div key={index} className="flex items-center gap-3 text-left">
219
- <div
220
- className={cn(
221
- "flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full border-2",
222
- step.completed &&
223
- "bg-primary border-primary text-primary-foreground",
224
- step.current &&
225
- !step.completed &&
226
- "border-primary text-primary animate-pulse",
227
- !step.completed &&
228
- !step.current &&
229
- "border-muted-foreground/30 text-muted-foreground",
230
- )}
231
- >
232
- {step.completed ? (
233
- <StepCheckIcon />
234
- ) : step.current ? (
235
- <div className="bg-primary h-2 w-2 animate-pulse rounded-full" />
236
- ) : (
237
- <div className="bg-muted-foreground/30 h-2 w-2 rounded-full" />
238
- )}
239
- </div>
240
- <div className="flex flex-1 items-center justify-between">
241
- <span
242
- className={cn(
243
- "text-xs",
244
- step.completed && "text-foreground",
245
- step.current &&
246
- !step.completed &&
247
- "text-foreground font-medium",
248
- !step.completed &&
249
- !step.current &&
250
- "text-muted-foreground",
251
- )}
252
- >
253
- {step.label}
254
- </span>
255
- {step.txHash && explorerUrl && (
256
- <a
257
- href={`${explorerUrl}/tx/${step.txHash}`}
258
- target="_blank"
259
- rel="noopener noreferrer"
260
- className="text-primary hover:text-primary/80 ml-2 inline-flex items-center gap-1 text-xs transition-colors"
261
- title={`View transaction: ${step.txHash}`}
262
- >
263
- <span className="font-mono">
264
- {step.txHash.slice(0, 6)}...{step.txHash.slice(-4)}
265
- </span>
266
- <ExternalLinkIcon size="w-3 h-3" />
267
- </a>
268
- )}
269
- </div>
270
- </div>
271
- ))}
272
- </div>
273
- </div>
274
- )}
275
-
276
- {/* Action buttons (completed or cancelled state) */}
277
- {(completed || cancelled) &&
278
- showActions &&
279
- (onViewDetails || onClose) && (
280
- <div
281
- className={cn(
282
- "flex gap-2 pt-2",
283
- variant === "compact" ? "flex-col" : "flex-row justify-center",
284
- )}
285
- >
286
- {!cancelled && onViewDetails && (
287
- <Button
288
- size={variant === "compact" ? "sm" : "default"}
289
- onClick={onViewDetails}
290
- >
291
- View Details
292
- </Button>
293
- )}
294
- {onClose && (
295
- <Button
296
- variant="default"
297
- size={variant === "compact" ? "sm" : "default"}
298
- onClick={onClose}
299
- >
300
- {cancelled ? "Try Again" : "Done"}
301
- </Button>
302
- )}
303
- </div>
304
- )}
305
- </Card>
306
- );
307
- },
308
- );
309
-
310
- TxStatus.displayName = "TxStatus";
311
-
312
- export { TxStatus };
@@ -1,113 +0,0 @@
1
- import React, { useCallback } from "react";
2
-
3
- import { Badge } from "@/components/ui/badge";
4
- import { NavigationBar, NavigationItem } from "@/components/ui/navigation-bar";
5
- import { SegmentControl } from "@/components/ui";
6
- import { SearchBar } from "@/components/features";
7
-
8
- export interface AssetFilterItem {
9
- id: string;
10
- label: string;
11
- icon?: React.ReactNode;
12
- }
13
-
14
- export interface AssetFiltersProps {
15
- // Available filters
16
- filters: AssetFilterItem[];
17
- // Currently active filter IDs
18
- activeFilters: string[];
19
- // Callback when filters change
20
- onFiltersChange: (filterIds: string[]) => void;
21
- // Display variant
22
- variant?: "badge" | "navigation";
23
- // Allow multiple filters (only for badge variant)
24
- multipleFilters?: boolean;
25
- // Search functionality
26
- showSearch?: boolean;
27
- searchValue?: string;
28
- onSearchChange?: (value: string) => void;
29
- searchPlaceholder?: string;
30
- searchDebounceDelay?: number;
31
- }
32
-
33
- export const AssetFilters: React.FC<AssetFiltersProps> = ({
34
- filters,
35
- activeFilters,
36
- onFiltersChange,
37
- variant = "badge",
38
- multipleFilters = true,
39
- showSearch = true,
40
- searchValue = "",
41
- onSearchChange,
42
- searchPlaceholder = "Search tokens...",
43
- searchDebounceDelay = 300,
44
- }) => {
45
- // Handle filter toggle
46
- const handleFilterToggle = useCallback(
47
- (filterId: string) => {
48
- if (variant === "badge" && multipleFilters) {
49
- // Toggle filter in multi-select mode
50
- const newFilters = activeFilters.includes(filterId)
51
- ? activeFilters.filter((id) => id !== filterId)
52
- : [...activeFilters, filterId];
53
- onFiltersChange(newFilters);
54
- } else {
55
- // Single select mode (navigation or single badge)
56
- const newFilters =
57
- activeFilters.includes(filterId) && activeFilters.length === 1
58
- ? [] // Deselect if it's the only active filter
59
- : [filterId];
60
- onFiltersChange(newFilters);
61
- }
62
- },
63
- [activeFilters, onFiltersChange, variant, multipleFilters],
64
- );
65
-
66
- return (
67
- <div className="space-y-3">
68
- {/* Search Bar */}
69
- {showSearch && onSearchChange && (
70
- <SearchBar
71
- value={searchValue}
72
- onChange={onSearchChange}
73
- placeholder={searchPlaceholder}
74
- debounce={searchDebounceDelay}
75
- />
76
- )}
77
-
78
- {/* Filters */}
79
- {filters.length > 0 && (
80
- <>
81
- {variant === "badge" ? (
82
- <div className="flex flex-wrap gap-2 px-2">
83
- {filters.map((filter) => (
84
- <Badge
85
- key={filter.id}
86
- variant={
87
- activeFilters.includes(filter.id) ? "default" : "muted"
88
- }
89
- className="bg-muted cursor-pointer rounded-full"
90
- onClick={() => handleFilterToggle(filter.id)}
91
- >
92
- {filter.icon && <span className="mr-1">{filter.icon}</span>}
93
- {filter.label}
94
- </Badge>
95
- ))}
96
- </div>
97
- ) : (
98
- <SegmentControl
99
- variant="segment"
100
- className="w-full"
101
- value={activeFilters[0]}
102
- onChange={(e) => handleFilterToggle(e)}
103
- items={filters.map((filter) => ({
104
- value: filter.id,
105
- label: filter.label,
106
- }))}
107
- />
108
- )}
109
- </>
110
- )}
111
- </div>
112
- );
113
- };
@@ -1,178 +0,0 @@
1
- "use client";
2
- import React, { useState } from "react";
3
- import { WidgetListItems } from "../widget-list-items";
4
- import { AssetRow } from "./asset-row";
5
- import {
6
- AssetFilters,
7
- AssetFilterItem as BaseAssetFilter,
8
- } from "./asset-filters";
9
- import { useAssetFiltering, useAssetGrouping } from "./hooks";
10
- import { ScrollArea } from "@/components/ui/scroll-area";
11
- import { cn } from "@/lib/utils";
12
-
13
- export interface Asset {
14
- id: string;
15
- symbol: string;
16
- name: string;
17
- icon: React.ReactNode;
18
- balance: string;
19
- balanceUSD: string;
20
- chainId: string;
21
- chainName: string;
22
- chainIcon?: React.ReactNode;
23
- address?: string;
24
- decimals?: number;
25
- }
26
-
27
- export interface AssetFilterProp extends BaseAssetFilter {
28
- filterFn: (asset: Asset) => boolean;
29
- }
30
-
31
- export interface AssetListProps {
32
- // Data
33
- assets: Asset[];
34
- onAssetClick?: (asset: Asset) => void;
35
-
36
- // Display options
37
- groupByChain?: boolean;
38
- showChainIcon?: boolean;
39
-
40
- // Filtering options
41
- filters?: AssetFilterProp[];
42
- filterVariant?: "badge" | "navigation";
43
- multipleFilters?: boolean;
44
-
45
- // Search options
46
- showSearch?: boolean;
47
- searchPlaceholder?: string;
48
- searchDebounceDelay?: number;
49
-
50
- // Styling
51
- className?: string;
52
- itemClassName?: string;
53
- emptyState?: React.ReactNode;
54
- }
55
-
56
- // Default filters
57
- const defaultFilters: AssetFilterProp[] = [
58
- {
59
- id: "all",
60
- label: "All",
61
- filterFn: (asset) => parseFloat(asset.balance) > 0,
62
- },
63
- {
64
- id: "stablecoins",
65
- label: "Stables",
66
- filterFn: (asset) =>
67
- ["USDC", "USDT", "DAI", "USD"].includes(asset.symbol.toUpperCase()) ||
68
- ["USDC", "USDT", "DAI", "USD"].includes(asset.name.toUpperCase()),
69
- },
70
- {
71
- id: "eth",
72
- label: "ETH",
73
- filterFn: (asset) =>
74
- ["ETH"].includes(asset.symbol.toUpperCase()) ||
75
- ["ETH"].includes(asset.name.toUpperCase()),
76
- },
77
- {
78
- id: "btc",
79
- label: "BTC",
80
- filterFn: (asset) =>
81
- ["BTC"].includes(asset.symbol.toUpperCase()) ||
82
- ["BTC"].includes(asset.name.toUpperCase()),
83
- },
84
- ];
85
-
86
- export const AssetList: React.FC<AssetListProps> = ({
87
- assets,
88
- onAssetClick,
89
- groupByChain = true,
90
- showChainIcon = false,
91
- filters = defaultFilters,
92
- filterVariant = "badge",
93
- multipleFilters = false,
94
- showSearch = true,
95
- searchPlaceholder = "Search tokens...",
96
- searchDebounceDelay = 300,
97
- className,
98
- itemClassName,
99
- emptyState,
100
- }) => {
101
- const [searchQuery, setSearchQuery] = useState("");
102
- const [activeFilterIds, setActiveFilterIds] = useState<string[]>(["all"]);
103
-
104
- // Use custom hooks for filtering and grouping
105
- const filteredAssets = useAssetFiltering({
106
- assets,
107
- searchQuery,
108
- activeFilterIds,
109
- filters,
110
- });
111
-
112
- const groups = useAssetGrouping({
113
- assets: filteredAssets,
114
- groupByChain,
115
- });
116
-
117
- const renderAsset = (asset: Asset) => (
118
- <AssetRow
119
- icon={
120
- showChainIcon && asset.chainIcon ? (
121
- <div className="relative">
122
- {asset.icon}
123
- <div className="bg-background border-border absolute -right-1 -bottom-1 flex h-4 w-4 items-center justify-center rounded-full border">
124
- {asset.chainIcon}
125
- </div>
126
- </div>
127
- ) : (
128
- asset.icon
129
- )
130
- }
131
- symbol={asset.symbol}
132
- name={asset.name}
133
- balance={asset.balance}
134
- balanceUSD={asset.balanceUSD}
135
- onClick={() => onAssetClick?.(asset)}
136
- className={itemClassName}
137
- />
138
- );
139
-
140
- return (
141
- <div className={cn("flex h-full flex-col", className)}>
142
- <div className="mb-8 flex-shrink-0">
143
- <AssetFilters
144
- filters={filters.map(({ filterFn, ...rest }) => rest)}
145
- activeFilters={activeFilterIds}
146
- onFiltersChange={setActiveFilterIds}
147
- variant={filterVariant}
148
- multipleFilters={multipleFilters}
149
- showSearch={showSearch}
150
- searchValue={searchQuery}
151
- onSearchChange={setSearchQuery}
152
- searchPlaceholder={searchPlaceholder}
153
- searchDebounceDelay={searchDebounceDelay}
154
- />
155
- </div>
156
-
157
- <ScrollArea className="flex-1">
158
- <div className="pb-4">
159
- <WidgetListItems
160
- groups={groups}
161
- renderItem={renderAsset}
162
- getItemKey={(asset) => asset.id}
163
- showGroupHeaders={groupByChain}
164
- emptyState={
165
- emptyState || (
166
- <div className="text-muted-foreground py-8 text-center">
167
- {searchQuery || activeFilterIds.length > 0
168
- ? "No assets match your filters"
169
- : "No assets available"}
170
- </div>
171
- )
172
- }
173
- />
174
- </div>
175
- </ScrollArea>
176
- </div>
177
- );
178
- };
@@ -1,45 +0,0 @@
1
- import React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- export interface AssetRowProps {
5
- icon: React.ReactNode;
6
- symbol: string;
7
- name: string;
8
- balance: string;
9
- balanceUSD: string;
10
- onClick?: () => void;
11
- className?: string;
12
- }
13
-
14
- export const AssetRow: React.FC<AssetRowProps> = ({
15
- icon,
16
- symbol,
17
- name,
18
- balance,
19
- balanceUSD,
20
- onClick,
21
- className,
22
- }) => {
23
- return (
24
- <div
25
- onClick={onClick}
26
- className={cn(
27
- "flex cursor-pointer items-center justify-between rounded-lg px-3.5 py-2.5 transition-colors",
28
- "hover:bg-muted-foreground/60",
29
- className,
30
- )}
31
- >
32
- <div className="flex items-center gap-3">
33
- <span className="text-xl">{icon}</span>
34
- <div>
35
- <div className="text-sm font-medium">{symbol}</div>
36
- <div className="text-muted-foreground text-xs">{name}</div>
37
- </div>
38
- </div>
39
- <div className="text-right">
40
- <div className="text-sm font-medium">{balance}</div>
41
- <div className="text-muted-foreground text-xs">({balanceUSD})</div>
42
- </div>
43
- </div>
44
- );
45
- };
@@ -1,2 +0,0 @@
1
- export * from "./use-asset-filtering";
2
- export * from "./use-asset-grouping";
@@ -1,44 +0,0 @@
1
- import { useMemo } from "react";
2
- import { Asset, AssetFilterProp } from "../asset-list";
3
-
4
- interface UseAssetFilteringProps {
5
- assets: Asset[];
6
- searchQuery: string;
7
- activeFilterIds: string[];
8
- filters: AssetFilterProp[];
9
- }
10
-
11
- export function useAssetFiltering({
12
- assets,
13
- searchQuery,
14
- activeFilterIds,
15
- filters,
16
- }: UseAssetFilteringProps): Asset[] {
17
- return useMemo(() => {
18
- let result = assets;
19
-
20
- // Apply search filter
21
- if (searchQuery) {
22
- const query = searchQuery.toLowerCase();
23
- result = result.filter(
24
- (asset) =>
25
- asset.symbol.toLowerCase().includes(query) ||
26
- asset.name.toLowerCase().includes(query) ||
27
- asset.address?.toLowerCase().includes(query),
28
- );
29
- }
30
-
31
- // Apply active filters
32
- if (activeFilterIds.length > 0) {
33
- const activeFilterFns = filters
34
- .filter((f) => activeFilterIds.includes(f.id))
35
- .map((f) => f.filterFn);
36
-
37
- result = result.filter((asset) =>
38
- activeFilterFns.every((filterFn) => filterFn(asset)),
39
- );
40
- }
41
-
42
- return result;
43
- }, [assets, searchQuery, activeFilterIds, filters]);
44
- }