@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,87 +0,0 @@
1
- import { useMemo } from "react";
2
- import { Asset } from "../asset-list";
3
- import { WidgetListGroup } from "../../widget-list-items";
4
-
5
- interface UseAssetGroupingProps {
6
- assets: Asset[];
7
- groupByChain: boolean;
8
- }
9
-
10
- const CHAIN_ICONS: Record<number, string> = {
11
- 1: "https://storage.googleapis.com/turtle-assets/tokens/eth.png",
12
- 747474:
13
- "https://storage.googleapis.com/turtle-assets/partners/polygon/katana.svg",
14
- };
15
-
16
- export function useAssetGrouping({
17
- assets,
18
- groupByChain,
19
- }: UseAssetGroupingProps): WidgetListGroup<Asset>[] {
20
- return useMemo(() => {
21
- // First, sort all assets by balance (USD value) in descending order
22
- const sortedAssets = [...assets].sort((a, b) => {
23
- // Parse balance USD values, removing $ and commas
24
- const balanceA = parseFloat(a.balanceUSD.replace(/[$,]/g, "") || "0");
25
- const balanceB = parseFloat(b.balanceUSD.replace(/[$,]/g, "") || "0");
26
- return balanceB - balanceA;
27
- });
28
-
29
- if (!groupByChain) {
30
- return [
31
- {
32
- id: "all-assets",
33
- items: sortedAssets,
34
- },
35
- ];
36
- }
37
-
38
- // Group by chain
39
- const chainGroups = sortedAssets.reduce(
40
- (acc, asset) => {
41
- if (!acc[asset.chainId]) {
42
- acc[asset.chainId] = {
43
- id: asset.chainId,
44
- title: asset.chainName,
45
- // Improve this adding chain icon to balances data
46
- icon: CHAIN_ICONS[Number(asset.chainId)],
47
- items: [],
48
- };
49
- }
50
- acc[asset.chainId].items.push(asset);
51
- return acc;
52
- },
53
- {} as Record<string, WidgetListGroup<Asset>>,
54
- );
55
-
56
- console.log("chainGroups", chainGroups);
57
-
58
- // Sort groups by: 1) highest balance in group, 2) number of tokens with balance
59
- const groupsArray = Object.values(chainGroups);
60
- groupsArray.sort((a, b) => {
61
- // Get the highest balance in each group (items are already sorted)
62
- const maxBalanceA =
63
- a.items.length > 0
64
- ? parseFloat(a.items[0].balanceUSD.replace(/[$,]/g, "") || "0")
65
- : 0;
66
- const maxBalanceB =
67
- b.items.length > 0
68
- ? parseFloat(b.items[0].balanceUSD.replace(/[$,]/g, "") || "0")
69
- : 0;
70
-
71
- // If highest balances are equal, sort by number of tokens with non-zero balance
72
- if (maxBalanceA === maxBalanceB) {
73
- const nonZeroCountA = a.items.filter(
74
- (asset) => parseFloat(asset.balance) > 0,
75
- ).length;
76
- const nonZeroCountB = b.items.filter(
77
- (asset) => parseFloat(asset.balance) > 0,
78
- ).length;
79
- return nonZeroCountB - nonZeroCountA;
80
- }
81
-
82
- return maxBalanceB - maxBalanceA;
83
- });
84
-
85
- return groupsArray;
86
- }, [assets, groupByChain]);
87
- }
@@ -1,3 +0,0 @@
1
- export * from "./asset-list";
2
- export * from "./asset-row";
3
- export * from "./asset-filters";
@@ -1,121 +0,0 @@
1
- "use client";
2
- import * as React from "react";
3
- import { cn } from "@/lib/utils";
4
- import type { ReactNode } from "react";
5
-
6
- // Minimal Token type - only the fields we need
7
- interface TokenLike {
8
- symbol: string;
9
- logoUrl?: string;
10
- }
11
-
12
- interface BaseSelectorProps {
13
- icon?: React.ReactNode;
14
- text: string;
15
- onClick?: () => void;
16
- className?: string;
17
- variant?: "default" | "bordered";
18
- size?: "xs" | "sm" | "default";
19
- placeholder?: string;
20
- showIcon?: boolean;
21
- // New: Accept token directly
22
- token?: TokenLike;
23
- }
24
-
25
- // Utility convert icon url to img element
26
- const iconUrlToImg = (iconUrl?: string, alt: string = ""): ReactNode => {
27
- return iconUrl ? (
28
- <img src={iconUrl} alt={alt} className="h-4 w-4 rounded-full" />
29
- ) : (
30
- <div className="bg-muted flex h-4 w-4 items-center justify-center rounded-full text-xs">
31
- {alt.charAt(0)}
32
- </div>
33
- );
34
- };
35
-
36
- // Generate icon from token
37
- const getTokenIcon = (token: TokenLike): ReactNode => {
38
- return iconUrlToImg(token.logoUrl, token.symbol);
39
- };
40
-
41
- const BaseSelector = ({
42
- icon,
43
- text,
44
- onClick,
45
- className,
46
- variant = "bordered",
47
- size = "default",
48
- placeholder = "Select",
49
- showIcon = true,
50
- token,
51
- }: BaseSelectorProps) => {
52
- // Determine the actual icon to display
53
- const displayIcon = token ? getTokenIcon(token) : icon;
54
- const displayText = token?.symbol || text;
55
- const hasContent = displayText || displayIcon;
56
-
57
- return (
58
- <div
59
- onClick={onClick}
60
- className={cn(
61
- "bg-muted hover:bg-accent/70 text-foreground inline-flex w-auto min-w-[80px] cursor-pointer items-center justify-between rounded-full font-medium",
62
- "focus:ring-primary/20 transition-colors focus:ring-2 focus:outline-none",
63
- variant === "bordered" && "border-border border",
64
- size === "xs" && "h-7 min-w-[80px] gap-2 px-2.5 py-1",
65
- size === "sm" && "h-8 min-w-[75px] gap-1.5 px-3 py-1.5",
66
- size === "default" && "h-10 min-w-[80px] gap-1.5 px-3 py-2",
67
- className,
68
- )}
69
- role="button"
70
- tabIndex={0}
71
- >
72
- {hasContent ? (
73
- <div
74
- className={cn(
75
- "flex items-center",
76
- size === "xs" && "gap-2",
77
- size === "sm" && "gap-1.5",
78
- size === "default" && "gap-1.5",
79
- )}
80
- >
81
- {showIcon && displayIcon && (
82
- <span
83
- className={cn(
84
- "flex items-center justify-center",
85
- size === "xs" && "h-3 w-3.5",
86
- size === "sm" && "h-3.5 w-4",
87
- size === "default" && "h-4 w-5",
88
- )}
89
- >
90
- {displayIcon}
91
- </span>
92
- )}
93
- <span
94
- className={cn(
95
- "font-medium",
96
- size === "xs" && "text-xs",
97
- size === "sm" && "text-sm",
98
- size === "default" && "text-sm",
99
- )}
100
- >
101
- {displayText}
102
- </span>
103
- </div>
104
- ) : (
105
- <span
106
- className={cn(
107
- "text-muted-foreground",
108
- size === "xs" && "text-xs",
109
- size === "sm" && "text-sm",
110
- size === "default" && "text-sm",
111
- )}
112
- >
113
- {placeholder}
114
- </span>
115
- )}
116
- </div>
117
- );
118
- };
119
-
120
- export { BaseSelector, iconUrlToImg, getTokenIcon };
121
- export type { BaseSelectorProps, TokenLike };
@@ -1,82 +0,0 @@
1
- import * as React from "react";
2
- import { LabelWithIcon } from "@/components/ui/label-with-icon";
3
- import {
4
- WidgetItem,
5
- WidgetItemTop,
6
- WidgetItemBottom,
7
- WidgetItemLeft,
8
- WidgetItemRight,
9
- } from "./widget-item";
10
- import { IconList, IconListItem } from "@/components/ui/icon-list";
11
- import { WidgetItemStats } from "./widget-item-stats";
12
-
13
- interface CampaignItemValue {
14
- icon: string; // icon url
15
- name: string;
16
- aprRange: string;
17
- tvl: string;
18
- participants?: string;
19
- rewards?: IconListItem[];
20
- chains?: IconListItem[];
21
- }
22
-
23
- interface CampaignItemProps extends React.ComponentProps<"div"> {
24
- value: CampaignItemValue;
25
- onSelect?: () => void;
26
- selected?: boolean;
27
- }
28
-
29
- const CampaignItem = React.forwardRef<HTMLDivElement, CampaignItemProps>(
30
- ({ value, onSelect, selected = false, ...props }, ref) => {
31
- const { icon, name, tvl, participants, aprRange, rewards, chains } = value;
32
-
33
- // TODO: Refactor and add default icon here, maybe turtle icon?
34
- const defaultIcon = (
35
- <div className="bg-primary/20 flex h-6 w-6 items-center justify-center rounded-full">
36
- <svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
37
- <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm4.59-12.42L10 14.17l-2.59-2.58L6 13l4 4 8-8z" />
38
- </svg>
39
- </div>
40
- );
41
-
42
- const stats = [
43
- { label: "TVL", value: tvl },
44
- { label: "Participants", value: participants || "" },
45
- ];
46
-
47
- return (
48
- <WidgetItem ref={ref} onSelect={onSelect} selected={selected} {...props}>
49
- <WidgetItemTop>
50
- <WidgetItemLeft>
51
- <LabelWithIcon
52
- icon={icon || defaultIcon}
53
- textSize="base"
54
- iconSize="lg"
55
- >
56
- {name}
57
- </LabelWithIcon>
58
- </WidgetItemLeft>
59
- <WidgetItemRight>
60
- <span className="text-md text-foreground font-semibold">
61
- {aprRange || "N/A"}
62
- </span>
63
- </WidgetItemRight>
64
- </WidgetItemTop>
65
- <WidgetItemBottom>
66
- <WidgetItemLeft>
67
- {chains && <IconList items={chains} label="Chains" size="sm" />}
68
- <WidgetItemStats stats={stats} />
69
- </WidgetItemLeft>
70
- <WidgetItemRight className="flex flex-col items-end gap-1">
71
- {rewards && <IconList items={rewards} label="Rewards" size="sm" />}
72
- </WidgetItemRight>
73
- </WidgetItemBottom>
74
- </WidgetItem>
75
- );
76
- },
77
- );
78
-
79
- CampaignItem.displayName = "CampaignItem";
80
-
81
- export { CampaignItem };
82
- export type { CampaignItemProps, CampaignItemValue };
@@ -1,92 +0,0 @@
1
- import * as React from "react";
2
- import { LabelWithIcon } from "@/components/ui/label-with-icon";
3
- import {
4
- WidgetItem,
5
- WidgetItemTop,
6
- WidgetItemBottom,
7
- WidgetItemLeft,
8
- WidgetItemRight,
9
- } from "./widget-item";
10
- import { IconList, IconListItem } from "@/components/ui/icon-list";
11
- import { WidgetItemStats } from "./widget-item-stats";
12
- import { OpportunityType } from "../opportunity";
13
- import { SparkleIcon, SparklesIcon } from "lucide-react";
14
-
15
- interface DealItemValue {
16
- icon: string;
17
- name: string;
18
- apr: string;
19
- tvl: string;
20
- turtleBoost: string;
21
- chains?: IconListItem[];
22
- category: string;
23
- }
24
-
25
- interface DealItemProps extends React.ComponentProps<"div"> {
26
- value: DealItemValue;
27
- onSelect?: () => void;
28
- selected?: boolean;
29
- }
30
-
31
- const DealItem = React.forwardRef<HTMLDivElement, DealItemProps>(
32
- ({ value, onSelect, selected = false, ...props }, ref) => {
33
- const { icon, name, chains, tvl, turtleBoost, category } = value;
34
-
35
- const defaultIcon = (
36
- <div className="bg-primary/20 flex h-6 w-6 items-center justify-center rounded-full">
37
- <svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
38
- <path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58s1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41s-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z" />
39
- </svg>
40
- </div>
41
- );
42
-
43
- const stats = React.useMemo(
44
- () => [
45
- { label: "TVL", value: tvl },
46
- {
47
- label: "Turtle Boost",
48
- value: turtleBoost,
49
- icon: <SparklesIcon size={12} />,
50
- },
51
- ],
52
- [tvl, turtleBoost],
53
- );
54
-
55
- return (
56
- <WidgetItem ref={ref} onSelect={onSelect} selected={selected} {...props}>
57
- <WidgetItemTop>
58
- <WidgetItemLeft>
59
- <LabelWithIcon
60
- icon={icon || defaultIcon}
61
- textSize="base"
62
- iconSize="lg"
63
- >
64
- {name}
65
- </LabelWithIcon>
66
- </WidgetItemLeft>
67
- <WidgetItemRight>
68
- <span className="text-md text-foreground font-semibold">
69
- {turtleBoost}
70
- </span>
71
- </WidgetItemRight>
72
- </WidgetItemTop>
73
- <WidgetItemBottom>
74
- <WidgetItemLeft>
75
- <WidgetItemStats stats={stats} />
76
- {/* <OpportunityType type={"Boosted Deal"} /> */}
77
- </WidgetItemLeft>
78
- <WidgetItemRight>
79
- {chains && (
80
- <IconList items={chains} label="Available on" size="sm" />
81
- )}
82
- </WidgetItemRight>
83
- </WidgetItemBottom>
84
- </WidgetItem>
85
- );
86
- },
87
- );
88
-
89
- DealItem.displayName = "DealItem";
90
-
91
- export { DealItem };
92
- export type { DealItemProps };
@@ -1,36 +0,0 @@
1
- export * from "./base-selector";
2
- export * from "./widget-list-items";
3
- export * from "./asset-list";
4
-
5
- // Base components
6
- export {
7
- WidgetItem,
8
- WidgetItemTop,
9
- WidgetItemBottom,
10
- WidgetItemLeft,
11
- WidgetItemRight,
12
- } from "./widget-item";
13
- export type {
14
- WidgetItemProps,
15
- WidgetItemTopProps,
16
- WidgetItemBottomProps,
17
- WidgetItemLeftProps,
18
- WidgetItemRightProps,
19
- } from "./widget-item";
20
-
21
- // Helper components
22
- export { WidgetItemStats } from "./widget-item-stats";
23
- export type { WidgetItemStatsProps, Stat } from "./widget-item-stats";
24
-
25
- // Item components
26
- export { OpportunityItem } from "./opportunity-item";
27
- export type {
28
- OpportunityItemProps,
29
- OpportunityItemValue,
30
- } from "./opportunity-item";
31
-
32
- export { CampaignItem } from "./campaign-item";
33
- export type { CampaignItemProps, CampaignItemValue } from "./campaign-item";
34
-
35
- export { DealItem } from "./deal-item";
36
- export type { DealItemProps } from "./deal-item";
@@ -1,105 +0,0 @@
1
- import * as React from "react";
2
- import { LabelWithIcon } from "@/components/ui/label-with-icon";
3
- import {
4
- WidgetItem,
5
- WidgetItemTop,
6
- WidgetItemBottom,
7
- WidgetItemLeft,
8
- WidgetItemRight,
9
- } from "./widget-item";
10
- import { IconList, IconListItem } from "@/components/ui/icon-list";
11
- import { WidgetItemStats } from "./widget-item-stats";
12
- import { SparklesIcon } from "lucide-react";
13
-
14
- interface OpportunityItemValue {
15
- icon: string;
16
- name: string;
17
- tvl: string;
18
- apr: string;
19
- incentives: string[] | IconListItem[];
20
- aprBreakdown: {
21
- name: string;
22
- apr: number;
23
- description: string;
24
- iconUrl: string;
25
- boost: boolean;
26
- }[];
27
- chain: IconListItem;
28
- turtleBoost: string | null;
29
- partner: { icon: string; name: string } | null;
30
- }
31
-
32
- interface OpportunityItemProps extends React.ComponentProps<"div"> {
33
- value: OpportunityItemValue;
34
- incentives?: string[] | IconListItem[];
35
- chains?: IconListItem[];
36
- onSelect?: () => void;
37
- selected?: boolean;
38
- }
39
-
40
- const OpportunityItem = React.forwardRef<HTMLDivElement, OpportunityItemProps>(
41
- ({ value, onSelect, selected = false, ...props }, ref) => {
42
- const { icon, name, tvl, apr, incentives, chain, turtleBoost } = value;
43
-
44
- const defaultIcon = (
45
- <div className="bg-primary/20 flex h-6 w-6 items-center justify-center rounded-full">
46
- <svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
47
- <path d="M12 2L13.09 8.26L20 7L18.74 13.09L22 14L16.74 19.26L17 21L10.91 19.74L10 22L8.09 15.74L2 17L3.26 10.91L0 10L5.26 4.74L5 3L11.09 4.26L12 2Z" />
48
- </svg>
49
- </div>
50
- );
51
-
52
- const stats = React.useMemo(() => {
53
- if (!turtleBoost) return [{ label: "TVL", value: tvl || "" }];
54
- return [
55
- { label: "TVL", value: tvl || "" },
56
- {
57
- label: "Turtle Boost",
58
- value: turtleBoost,
59
- icon: <SparklesIcon size={12} />,
60
- },
61
- ];
62
- }, []);
63
-
64
- return (
65
- <WidgetItem ref={ref} onSelect={onSelect} selected={selected} {...props}>
66
- <WidgetItemTop>
67
- <WidgetItemLeft>
68
- <LabelWithIcon
69
- icon={icon || defaultIcon}
70
- textSize="base"
71
- iconSize="lg"
72
- >
73
- {name}
74
- </LabelWithIcon>
75
- </WidgetItemLeft>
76
- <WidgetItemRight>
77
- {apr && (
78
- <span className="text-md text-foreground font-semibold">
79
- {apr}
80
- </span>
81
- )}
82
- </WidgetItemRight>
83
- </WidgetItemTop>
84
- <WidgetItemBottom>
85
- <WidgetItemLeft>
86
- {chain && <IconList items={[chain]} label="Chains" size="sm" />}
87
- <WidgetItemStats stats={stats} />
88
- </WidgetItemLeft>
89
- <WidgetItemRight>
90
- <div className="flex flex-col items-end gap-1">
91
- {incentives && (
92
- <IconList items={incentives} label="Rewards" size="sm" />
93
- )}
94
- </div>
95
- </WidgetItemRight>
96
- </WidgetItemBottom>
97
- </WidgetItem>
98
- );
99
- },
100
- );
101
-
102
- OpportunityItem.displayName = "OpportunityItem";
103
-
104
- export { OpportunityItem };
105
- export type { OpportunityItemProps, OpportunityItemValue };
@@ -1,50 +0,0 @@
1
- import * as React from "react";
2
- import { cn } from "@/lib/utils";
3
-
4
- interface Stat {
5
- label: string;
6
- value: string;
7
- icon?: React.ReactNode;
8
- }
9
-
10
- interface WidgetItemStatsProps extends React.ComponentProps<"div"> {
11
- stats: Stat[];
12
- separator?: string;
13
- }
14
-
15
- const WidgetItemStats = React.forwardRef<HTMLDivElement, WidgetItemStatsProps>(
16
- ({ className, stats, separator = " | ", ...props }, ref) => {
17
- const validStats = stats.filter((stat) => stat.value);
18
-
19
- if (validStats.length === 0) return null;
20
-
21
- return (
22
- <div
23
- ref={ref}
24
- className={cn("text-muted-foreground flex gap-2 text-xs", className)}
25
- {...props}
26
- >
27
- {validStats.map((stat, index) => (
28
- <React.Fragment key={stat.label}>
29
- {index > 0 && <span>{separator}</span>}
30
- <span className="flex items-center justify-center gap-1">
31
- <span>{stat.label}: </span>
32
- {stat.icon ? (
33
- <span className="flex gap-1">
34
- {stat.icon} {stat.value}
35
- </span>
36
- ) : (
37
- <span>{stat.value}</span>
38
- )}
39
- </span>
40
- </React.Fragment>
41
- ))}
42
- </div>
43
- );
44
- },
45
- );
46
-
47
- WidgetItemStats.displayName = "WidgetItemStats";
48
-
49
- export { WidgetItemStats };
50
- export type { WidgetItemStatsProps, Stat };