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