@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,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
- }