@avail-project/widgets 0.0.1

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 (143) hide show
  1. package/README.md +34 -0
  2. package/dist/common/components/ErrorBoundary.d.ts +44 -0
  3. package/dist/common/components/ErrorBoundary.d.ts.map +1 -0
  4. package/dist/common/components/ErrorBoundary.js +65 -0
  5. package/dist/common/hooks/useDebouncedCallback.d.ts +13 -0
  6. package/dist/common/hooks/useDebouncedCallback.d.ts.map +1 -0
  7. package/dist/common/hooks/useDebouncedCallback.js +42 -0
  8. package/dist/common/hooks/useDebouncedValue.d.ts +5 -0
  9. package/dist/common/hooks/useDebouncedValue.d.ts.map +1 -0
  10. package/dist/common/hooks/useDebouncedValue.js +15 -0
  11. package/dist/common/hooks/useInterval.d.ts +11 -0
  12. package/dist/common/hooks/useInterval.d.ts.map +1 -0
  13. package/dist/common/hooks/useInterval.js +25 -0
  14. package/dist/common/hooks/useLatest.d.ts +12 -0
  15. package/dist/common/hooks/useLatest.d.ts.map +1 -0
  16. package/dist/common/hooks/useLatest.js +19 -0
  17. package/dist/common/hooks/useNexusError.d.ts +20 -0
  18. package/dist/common/hooks/useNexusError.d.ts.map +1 -0
  19. package/dist/common/hooks/useNexusError.js +120 -0
  20. package/dist/common/hooks/usePolling.d.ts +7 -0
  21. package/dist/common/hooks/usePolling.d.ts.map +1 -0
  22. package/dist/common/hooks/usePolling.js +29 -0
  23. package/dist/common/hooks/useStableCallback.d.ts +6 -0
  24. package/dist/common/hooks/useStableCallback.d.ts.map +1 -0
  25. package/dist/common/hooks/useStableCallback.js +13 -0
  26. package/dist/common/hooks/useStopwatch.d.ts +17 -0
  27. package/dist/common/hooks/useStopwatch.d.ts.map +1 -0
  28. package/dist/common/hooks/useStopwatch.js +44 -0
  29. package/dist/common/hooks/useTransactionExecution.d.ts +62 -0
  30. package/dist/common/hooks/useTransactionExecution.d.ts.map +1 -0
  31. package/dist/common/hooks/useTransactionExecution.js +273 -0
  32. package/dist/common/hooks/useTransactionFlow.d.ts +71 -0
  33. package/dist/common/hooks/useTransactionFlow.d.ts.map +1 -0
  34. package/dist/common/hooks/useTransactionFlow.js +435 -0
  35. package/dist/common/index.d.ts +17 -0
  36. package/dist/common/index.d.ts.map +1 -0
  37. package/dist/common/index.js +16 -0
  38. package/dist/common/tx/steps.d.ts +19 -0
  39. package/dist/common/tx/steps.d.ts.map +1 -0
  40. package/dist/common/tx/steps.js +89 -0
  41. package/dist/common/tx/types.d.ts +11 -0
  42. package/dist/common/tx/types.d.ts.map +1 -0
  43. package/dist/common/tx/types.js +19 -0
  44. package/dist/common/tx/useTransactionSteps.d.ts +21 -0
  45. package/dist/common/tx/useTransactionSteps.d.ts.map +1 -0
  46. package/dist/common/tx/useTransactionSteps.js +40 -0
  47. package/dist/common/types/transaction-flow.d.ts +48 -0
  48. package/dist/common/types/transaction-flow.d.ts.map +1 -0
  49. package/dist/common/types/transaction-flow.js +2 -0
  50. package/dist/common/utils/constant.d.ts +99 -0
  51. package/dist/common/utils/constant.d.ts.map +1 -0
  52. package/dist/common/utils/constant.js +370 -0
  53. package/dist/common/utils/token-pricing.d.ts +33 -0
  54. package/dist/common/utils/token-pricing.d.ts.map +1 -0
  55. package/dist/common/utils/token-pricing.js +255 -0
  56. package/dist/common/utils/transaction-flow.d.ts +35 -0
  57. package/dist/common/utils/transaction-flow.d.ts.map +1 -0
  58. package/dist/common/utils/transaction-flow.js +65 -0
  59. package/dist/index.d.ts +5 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +2 -0
  62. package/dist/lib/utils.d.ts +4 -0
  63. package/dist/lib/utils.d.ts.map +1 -0
  64. package/dist/lib/utils.js +8 -0
  65. package/dist/nexus/NexusProvider.d.ts +50 -0
  66. package/dist/nexus/NexusProvider.d.ts.map +1 -0
  67. package/dist/nexus/NexusProvider.js +572 -0
  68. package/dist/nexus-widget/components/address-identicon.d.ts +5 -0
  69. package/dist/nexus-widget/components/address-identicon.d.ts.map +1 -0
  70. package/dist/nexus-widget/components/address-identicon.js +39 -0
  71. package/dist/nexus-widget/components/amount-input-unified.d.ts +18 -0
  72. package/dist/nexus-widget/components/amount-input-unified.d.ts.map +1 -0
  73. package/dist/nexus-widget/components/amount-input-unified.js +88 -0
  74. package/dist/nexus-widget/components/deposit-idle-form.d.ts +29 -0
  75. package/dist/nexus-widget/components/deposit-idle-form.d.ts.map +1 -0
  76. package/dist/nexus-widget/components/deposit-idle-form.js +506 -0
  77. package/dist/nexus-widget/components/nexus-widget-progress-screen.d.ts +36 -0
  78. package/dist/nexus-widget/components/nexus-widget-progress-screen.d.ts.map +1 -0
  79. package/dist/nexus-widget/components/nexus-widget-progress-screen.js +737 -0
  80. package/dist/nexus-widget/components/pay-using-selector.d.ts +9 -0
  81. package/dist/nexus-widget/components/pay-using-selector.d.ts.map +1 -0
  82. package/dist/nexus-widget/components/pay-using-selector.js +37 -0
  83. package/dist/nexus-widget/components/pay-with-sources.d.ts +11 -0
  84. package/dist/nexus-widget/components/pay-with-sources.d.ts.map +1 -0
  85. package/dist/nexus-widget/components/pay-with-sources.js +266 -0
  86. package/dist/nexus-widget/components/receive-asset-selector.d.ts +28 -0
  87. package/dist/nexus-widget/components/receive-asset-selector.d.ts.map +1 -0
  88. package/dist/nexus-widget/components/receive-asset-selector.js +1178 -0
  89. package/dist/nexus-widget/components/recipient-input.d.ts +10 -0
  90. package/dist/nexus-widget/components/recipient-input.d.ts.map +1 -0
  91. package/dist/nexus-widget/components/recipient-input.js +44 -0
  92. package/dist/nexus-widget/components/send-idle-form.d.ts +29 -0
  93. package/dist/nexus-widget/components/send-idle-form.d.ts.map +1 -0
  94. package/dist/nexus-widget/components/send-idle-form.js +548 -0
  95. package/dist/nexus-widget/components/status-alerts.d.ts +7 -0
  96. package/dist/nexus-widget/components/status-alerts.d.ts.map +1 -0
  97. package/dist/nexus-widget/components/status-alerts.js +17 -0
  98. package/dist/nexus-widget/components/swap-asset-selector.d.ts +74 -0
  99. package/dist/nexus-widget/components/swap-asset-selector.d.ts.map +1 -0
  100. package/dist/nexus-widget/components/swap-asset-selector.js +1910 -0
  101. package/dist/nexus-widget/components/swap-idle-form.d.ts +29 -0
  102. package/dist/nexus-widget/components/swap-idle-form.d.ts.map +1 -0
  103. package/dist/nexus-widget/components/swap-idle-form.js +1289 -0
  104. package/dist/nexus-widget/components/swap-intent-preview.d.ts +95 -0
  105. package/dist/nexus-widget/components/swap-intent-preview.d.ts.map +1 -0
  106. package/dist/nexus-widget/components/swap-intent-preview.js +1050 -0
  107. package/dist/nexus-widget/nexus-widget.d.ts +4 -0
  108. package/dist/nexus-widget/nexus-widget.d.ts.map +1 -0
  109. package/dist/nexus-widget/nexus-widget.js +7819 -0
  110. package/dist/nexus-widget/sdk-types.d.ts +13 -0
  111. package/dist/nexus-widget/sdk-types.d.ts.map +1 -0
  112. package/dist/nexus-widget/sdk-types.js +1 -0
  113. package/dist/nexus-widget/theme.d.ts +156 -0
  114. package/dist/nexus-widget/theme.d.ts.map +1 -0
  115. package/dist/nexus-widget/theme.js +159 -0
  116. package/dist/nexus-widget/types.d.ts +141 -0
  117. package/dist/nexus-widget/types.d.ts.map +1 -0
  118. package/dist/nexus-widget/types.js +1 -0
  119. package/dist/nexus-widget/utils/citrea-tokens.d.ts +82 -0
  120. package/dist/nexus-widget/utils/citrea-tokens.d.ts.map +1 -0
  121. package/dist/nexus-widget/utils/citrea-tokens.js +136 -0
  122. package/dist/nexus-widget/utils/deposit-source-selection.d.ts +55 -0
  123. package/dist/nexus-widget/utils/deposit-source-selection.d.ts.map +1 -0
  124. package/dist/nexus-widget/utils/deposit-source-selection.js +219 -0
  125. package/dist/swaps/components/stacked-token-icons.d.ts +15 -0
  126. package/dist/swaps/components/stacked-token-icons.d.ts.map +1 -0
  127. package/dist/swaps/components/stacked-token-icons.js +38 -0
  128. package/dist/swaps/components/step-flow.d.ts +32 -0
  129. package/dist/swaps/components/step-flow.d.ts.map +1 -0
  130. package/dist/swaps/components/step-flow.js +54 -0
  131. package/dist/swaps/components/token-icon.d.ts +11 -0
  132. package/dist/swaps/components/token-icon.d.ts.map +1 -0
  133. package/dist/swaps/components/token-icon.js +16 -0
  134. package/dist/swaps/components/transaction-progress.d.ts +43 -0
  135. package/dist/swaps/components/transaction-progress.d.ts.map +1 -0
  136. package/dist/swaps/components/transaction-progress.js +94 -0
  137. package/dist/ui/button.d.ts +11 -0
  138. package/dist/ui/button.d.ts.map +1 -0
  139. package/dist/ui/button.js +34 -0
  140. package/dist/ui/dialog.d.ts +17 -0
  141. package/dist/ui/dialog.d.ts.map +1 -0
  142. package/dist/ui/dialog.js +47 -0
  143. package/package.json +60 -0
@@ -0,0 +1,1910 @@
1
+ // biome-ignore-all lint: NexusWidget registry component from shadcn registry.
2
+ "use client";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ import { formatTokenBalance } from "@avail-project/nexus-core/utils";
5
+ import Decimal from "decimal.js";
6
+ import { Check, ChevronDown, ChevronUp, Globe, Info, Loader2, Minus, Search, X, } from "lucide-react";
7
+ import React, { useCallback, useEffect, useMemo, useRef, useState, } from "react";
8
+ import { createPortal } from "react-dom";
9
+ import { CHAIN_METADATA, getSdkSwapSupportedChainIds, getShortChainName, isSwapSupportedBySdkChainList, } from "../../common/utils/constant";
10
+ const tabularNums = {
11
+ fontFeatureSettings: '"tnum"',
12
+ fontVariantNumeric: "tabular-nums",
13
+ };
14
+ const brand = "var(--foreground-brand)";
15
+ export function deriveTokenOptions(swapBalance, swapSupportedChains) {
16
+ const tokens = [];
17
+ for (const asset of swapBalance) {
18
+ for (const bd of asset.breakdown ?? []) {
19
+ if (!isSwapSupportedBySdkChainList(bd.chain?.id, swapSupportedChains)) {
20
+ continue;
21
+ }
22
+ if (Number.parseFloat(bd.balance ?? "0") <= 0)
23
+ continue;
24
+ const chainMeta = bd.chain?.id ? CHAIN_METADATA[bd.chain.id] : undefined;
25
+ tokens.push({
26
+ contractAddress: bd.contractAddress,
27
+ symbol: bd.symbol ?? asset.symbol,
28
+ name: bd.symbol ?? asset.symbol,
29
+ logo: asset.logo ?? "",
30
+ decimals: bd.decimals ?? asset.decimals ?? 18,
31
+ balance: bd.balance,
32
+ balanceInFiat: bd.balanceInFiat != null
33
+ ? `$${Number(bd.balanceInFiat).toFixed(2)}`
34
+ : "$0.00",
35
+ chainId: bd.chain?.id,
36
+ chainName: getShortChainName(bd.chain?.id, chainMeta?.name ?? bd.chain?.name),
37
+ chainLogo: chainMeta?.logo ?? bd.chain?.logo,
38
+ });
39
+ }
40
+ }
41
+ const seen = new Map();
42
+ for (const t of tokens) {
43
+ seen.set(`${t.contractAddress.toLowerCase()}-${t.chainId}`, t);
44
+ }
45
+ return Array.from(seen.values());
46
+ }
47
+ /* ── Radio dot (circular) ── */
48
+ export const RadioDot = ({ selected }) => (_jsx("div", { style: {
49
+ width: 20,
50
+ height: 20,
51
+ borderRadius: "999px",
52
+ boxSizing: "border-box",
53
+ border: selected ? "none" : "2px solid #E8E8E7",
54
+ backgroundColor: selected ? brand : "#FFFFFE",
55
+ display: "flex",
56
+ alignItems: "center",
57
+ justifyContent: "center",
58
+ flexShrink: 0,
59
+ }, children: selected && (_jsx("div", { style: {
60
+ width: 8,
61
+ height: 8,
62
+ borderRadius: "999px",
63
+ backgroundColor: "#FFFFFE",
64
+ } })) }));
65
+ const SelectionControl = ({ selected, indeterminate = false, multi, }) => {
66
+ if (!multi)
67
+ return _jsx(RadioDot, { selected: selected });
68
+ const isActive = selected || indeterminate;
69
+ return (_jsxs("div", { style: {
70
+ alignItems: "center",
71
+ backgroundColor: isActive ? brand : "#FFFFFE",
72
+ border: isActive ? "none" : "1.5px solid #E0E0DE",
73
+ borderRadius: "5px",
74
+ boxSizing: "border-box",
75
+ display: "flex",
76
+ flexShrink: 0,
77
+ height: 20,
78
+ justifyContent: "center",
79
+ width: 20,
80
+ }, children: [selected && (_jsx(Check, { style: { color: "#FFFFFE", height: 14, width: 14 } })), !selected && indeterminate && (_jsx(Minus, { style: { color: "#FFFFFE", height: 14, width: 14 } }))] }));
81
+ };
82
+ /* ── Chain logo cluster ── */
83
+ const ChainLogos = ({ tokens }) => {
84
+ const clusterRef = useRef(null);
85
+ const closeTimerRef = useRef(null);
86
+ const [showTooltip, setShowTooltip] = useState(false);
87
+ const [tooltipRect, setTooltipRect] = useState(null);
88
+ const uniqueChains = useMemo(() => {
89
+ const seen = new Set();
90
+ const out = [];
91
+ for (const t of tokens) {
92
+ if (t.chainId && !seen.has(t.chainId)) {
93
+ const balanceValue = Number(String(t.balance ?? "").replace(/[^0-9.]/g, "") || 0);
94
+ seen.add(t.chainId);
95
+ out.push({
96
+ id: t.chainId,
97
+ logo: t.chainLogo,
98
+ name: getShortChainName(t.chainId, t.chainName),
99
+ balance: t.balance,
100
+ balanceInFiat: t.balanceInFiat,
101
+ balanceValue: Number.isNaN(balanceValue) || !Number.isFinite(balanceValue)
102
+ ? 0
103
+ : balanceValue,
104
+ fiatValue: getTokenFiatValue(t),
105
+ });
106
+ }
107
+ }
108
+ return out.sort((a, b) => {
109
+ if (a.fiatValue !== b.fiatValue)
110
+ return b.fiatValue - a.fiatValue;
111
+ if (a.balanceValue !== b.balanceValue) {
112
+ return b.balanceValue - a.balanceValue;
113
+ }
114
+ return (a.name ?? "").localeCompare(b.name ?? "");
115
+ });
116
+ }, [tokens]);
117
+ const maxShow = 3;
118
+ const shown = uniqueChains.slice(0, maxShow);
119
+ const openTooltip = () => {
120
+ if (closeTimerRef.current) {
121
+ clearTimeout(closeTimerRef.current);
122
+ closeTimerRef.current = null;
123
+ }
124
+ setTooltipRect(clusterRef.current?.getBoundingClientRect() ?? null);
125
+ setShowTooltip(true);
126
+ };
127
+ const closeTooltip = () => {
128
+ if (closeTimerRef.current)
129
+ clearTimeout(closeTimerRef.current);
130
+ closeTimerRef.current = setTimeout(() => {
131
+ setShowTooltip(false);
132
+ closeTimerRef.current = null;
133
+ }, 120);
134
+ };
135
+ useEffect(() => {
136
+ return () => {
137
+ if (closeTimerRef.current)
138
+ clearTimeout(closeTimerRef.current);
139
+ };
140
+ }, []);
141
+ const showTooltipAbove = tooltipRect ? tooltipRect.top > 240 : true;
142
+ const tooltip = showTooltip && uniqueChains.length > 1 && tooltipRect
143
+ ? createPortal(_jsxs("div", { onMouseEnter: openTooltip, onMouseLeave: closeTooltip, style: {
144
+ backgroundColor: "#FFFFFE",
145
+ border: "1px solid #E8E8E7",
146
+ borderRadius: 10,
147
+ boxShadow: "0 8px 24px rgba(22,22,21,0.12)",
148
+ ...tabularNums,
149
+ left: Math.min(Math.max(tooltipRect.left - 24, 8), Math.max(8, window.innerWidth - 248)),
150
+ maxHeight: 220,
151
+ minWidth: 240,
152
+ overflowY: "auto",
153
+ padding: "10px 12px",
154
+ pointerEvents: "auto",
155
+ position: "fixed",
156
+ top: showTooltipAbove
157
+ ? tooltipRect.top - 12
158
+ : tooltipRect.bottom + 8,
159
+ transform: showTooltipAbove ? "translateY(-100%)" : "none",
160
+ zIndex: 2147483647,
161
+ }, children: [_jsxs("div", { style: {
162
+ alignItems: "center",
163
+ color: "#848483",
164
+ display: "flex",
165
+ fontFamily: '"Geist", system-ui, sans-serif',
166
+ fontSize: 11,
167
+ fontWeight: 700,
168
+ justifyContent: "space-between",
169
+ letterSpacing: "0.06em",
170
+ marginBottom: 8,
171
+ gap: 12,
172
+ }, children: [_jsxs("span", { children: ["UNIFIED \u00B7 ", uniqueChains.length, " CHAINS"] }), _jsx("span", { style: { color: "#161615", fontSize: 12, letterSpacing: 0 }, children: tokens
173
+ .reduce((sum, token) => sum + getTokenFiatValue(token), 0)
174
+ .toLocaleString(undefined, {
175
+ currency: "USD",
176
+ maximumFractionDigits: 2,
177
+ style: "currency",
178
+ }) })] }), uniqueChains.map((chain) => (_jsxs("div", { style: {
179
+ alignItems: "center",
180
+ display: "flex",
181
+ justifyContent: "space-between",
182
+ gap: 12,
183
+ padding: "4px 0",
184
+ }, children: [_jsxs("div", { style: {
185
+ alignItems: "center",
186
+ display: "flex",
187
+ gap: 8,
188
+ minWidth: 0,
189
+ }, children: [chain.logo ? (_jsx("img", { alt: "", src: chain.logo, style: {
190
+ borderRadius: "999px",
191
+ height: 16,
192
+ objectFit: "cover",
193
+ width: 16,
194
+ } })) : (_jsx("div", { style: {
195
+ backgroundColor: "#E8E8E7",
196
+ borderRadius: "999px",
197
+ height: 16,
198
+ width: 16,
199
+ } })), _jsx("span", { style: {
200
+ color: "#363635",
201
+ fontFamily: '"Geist", system-ui, sans-serif',
202
+ fontSize: 13,
203
+ overflow: "hidden",
204
+ textOverflow: "ellipsis",
205
+ whiteSpace: "nowrap",
206
+ }, children: chain.name || "Unknown chain" })] }), _jsx("span", { style: {
207
+ color: "#161615",
208
+ fontFamily: '"Geist", system-ui, sans-serif',
209
+ fontSize: 13,
210
+ fontWeight: 600,
211
+ whiteSpace: "nowrap",
212
+ }, children: chain.balance
213
+ ? formatTokenAmountDisplay(String(chain.balance).replace(/\s+[^\s]+$/, ""))
214
+ : chain.balanceInFiat || "0" })] }, chain.id)))] }), document.body)
215
+ : null;
216
+ return (_jsxs("div", { onMouseEnter: openTooltip, onMouseLeave: closeTooltip, ref: clusterRef, style: {
217
+ display: "flex",
218
+ alignItems: "center",
219
+ gap: 2,
220
+ position: "relative",
221
+ }, children: [tooltip, shown.map((c, i) => c.logo ? (_jsx("img", { alt: "", src: c.logo, style: {
222
+ width: 16,
223
+ height: 16,
224
+ borderRadius: "999px",
225
+ objectFit: "cover",
226
+ border: "1px solid #fff",
227
+ } }, c.id)) : (_jsx("div", { style: {
228
+ width: 16,
229
+ height: 16,
230
+ borderRadius: "999px",
231
+ backgroundColor: "#E8E8E7",
232
+ } }, c.id))), _jsxs("span", { style: {
233
+ fontFamily: '"Geist", system-ui, sans-serif',
234
+ fontSize: 12,
235
+ color: "#848483",
236
+ marginLeft: 2,
237
+ }, children: [uniqueChains.length, " chain", uniqueChains.length !== 1 ? "s" : ""] })] }));
238
+ };
239
+ const FILTER_TABS = [
240
+ { key: "all", label: "All" },
241
+ { key: "native", label: "Native" },
242
+ { key: "stables", label: "Stables" },
243
+ { key: "custom", label: "Custom" },
244
+ ];
245
+ const STABLE_SYMBOLS = new Set([
246
+ "AUSD",
247
+ "BDO",
248
+ "BRZ",
249
+ "BTCUSD",
250
+ "BUSD",
251
+ "bnbUSD",
252
+ "crvUSD",
253
+ "CUSD",
254
+ "DAI",
255
+ "DAI.e",
256
+ "dEURO",
257
+ "DUSD",
258
+ "EURA",
259
+ "EURC",
260
+ "EURCV",
261
+ "EURe",
262
+ "EURS",
263
+ "EURT",
264
+ "eUSD",
265
+ "frxUSD",
266
+ "GHO",
267
+ "GUSD",
268
+ "JUST",
269
+ "jEUR",
270
+ "JUSD",
271
+ "LUSD",
272
+ "rUSD",
273
+ "RLUSD",
274
+ "sUSD",
275
+ "svJUSD",
276
+ "TUSD",
277
+ "USDC",
278
+ "USDC.e",
279
+ "USDS",
280
+ "USDT",
281
+ "USDT0",
282
+ "USDT.e",
283
+ "ctUSD",
284
+ "PYUSD",
285
+ "USDe",
286
+ "xDAI",
287
+ "USD0",
288
+ "USDM",
289
+ ]);
290
+ const normalizeTokenGroupSymbol = (symbol) => symbol
291
+ .trim()
292
+ .toUpperCase()
293
+ .replaceAll("₮", "T")
294
+ .replaceAll(/[^A-Z0-9]/g, "");
295
+ const STABLE_SYMBOL_KEYS = new Set(Array.from(STABLE_SYMBOLS, normalizeTokenGroupSymbol));
296
+ const isStableToken = (token) => STABLE_SYMBOL_KEYS.has(normalizeTokenGroupSymbol(token.symbol));
297
+ function isNativeToken(t) {
298
+ if (isNativeLikeAddress(t.contractAddress))
299
+ return true;
300
+ const sym = t.symbol.toUpperCase();
301
+ const chain = (t.chainName || "").toLowerCase();
302
+ if (sym === "ETH")
303
+ return (!chain.includes("bnb") &&
304
+ !chain.includes("bsc") &&
305
+ !chain.includes("polygon") &&
306
+ !chain.includes("monad") &&
307
+ !chain.includes("hyperevm"));
308
+ if (sym === "POL" || sym === "MATIC")
309
+ return chain.includes("polygon");
310
+ if (sym === "HYPE")
311
+ return chain.includes("hyperevm");
312
+ if (sym === "MON")
313
+ return chain.includes("monad");
314
+ if (sym === "BNB")
315
+ return chain.includes("bnb") || chain.includes("bsc");
316
+ if (sym === "AVAX")
317
+ return chain.includes("avalanche");
318
+ if (sym === "FTM")
319
+ return chain.includes("fantom");
320
+ if (sym === "CELO")
321
+ return chain.includes("celo");
322
+ if (sym === "SUI")
323
+ return chain.includes("sui");
324
+ if (sym === "APT")
325
+ return chain.includes("aptos");
326
+ if (sym === "SOL")
327
+ return chain.includes("solana");
328
+ return false;
329
+ }
330
+ const MIN_FIAT_THRESHOLD = 0;
331
+ const CHAIN_SELECTOR_CLOSE_MS = 220;
332
+ const MODAL_HEIGHT_TRANSITION_MS = 260;
333
+ const modalHeightTransitionStyle = {
334
+ interpolateSize: "allow-keywords",
335
+ };
336
+ const modalHeightTransition = `height ${MODAL_HEIGHT_TRANSITION_MS}ms ease, max-height ${MODAL_HEIGHT_TRANSITION_MS}ms ease`;
337
+ export const SWAP_CHAIN_DISPLAY_ORDER = [
338
+ 1, // Ethereum
339
+ 42161, // Arbitrum
340
+ 8453, // Base
341
+ 137, // Polygon
342
+ 10, // OP
343
+ 999, // HyperEVM
344
+ 56, // BSC
345
+ // 43114, // Avalanche
346
+ 143, // Monad
347
+ 4326, // MegaETH
348
+ 4114, // Citrea
349
+ 8217, // Kaia
350
+ 534352, // Scroll
351
+ ];
352
+ const SWAP_CHAIN_DISPLAY_ORDER_RANK = new Map(SWAP_CHAIN_DISPLAY_ORDER.map((chainId, index) => [chainId, index]));
353
+ export const SWAP_CHAIN_DISPLAY_ORDER_SET = new Set(SWAP_CHAIN_DISPLAY_ORDER);
354
+ export const sortChainIdsBySwapDisplayOrder = (chainIds) => [...chainIds].sort((a, b) => {
355
+ const aRank = SWAP_CHAIN_DISPLAY_ORDER_RANK.get(a) ?? Number.MAX_SAFE_INTEGER;
356
+ const bRank = SWAP_CHAIN_DISPLAY_ORDER_RANK.get(b) ?? Number.MAX_SAFE_INTEGER;
357
+ if (aRank !== bRank)
358
+ return aRank - bRank;
359
+ const aName = CHAIN_METADATA[a]?.name ?? String(a);
360
+ const bName = CHAIN_METADATA[b]?.name ?? String(b);
361
+ return aName.localeCompare(bName);
362
+ });
363
+ export const compareChainsBySwapDisplayOrder = (a, b) => {
364
+ const aRank = SWAP_CHAIN_DISPLAY_ORDER_RANK.get(a.chainId ?? -1) ??
365
+ Number.MAX_SAFE_INTEGER;
366
+ const bRank = SWAP_CHAIN_DISPLAY_ORDER_RANK.get(b.chainId ?? -1) ??
367
+ Number.MAX_SAFE_INTEGER;
368
+ if (aRank !== bRank)
369
+ return aRank - bRank;
370
+ return (a.chainName ?? "").localeCompare(b.chainName ?? "");
371
+ };
372
+ const UNIFIED_MAINNET_CHAIN_IDS = new Set([
373
+ 1, 10, 56, 137, 143, 999, 4114, 8217, 8453, 42161, 43114, 534352, 4326,
374
+ ]);
375
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
376
+ const getTokenFiatValue = (token) => {
377
+ const parsed = Number(String(token.balanceInFiat ?? "").replace(/[^0-9.]/g, "") || 0);
378
+ return isNaN(parsed) || !isFinite(parsed) ? 0 : parsed;
379
+ };
380
+ const formatBalanceWithSymbol = (token) => {
381
+ const symbol = token.symbol?.trim() || "";
382
+ const balanceStr = String(token.balance ?? "").trim();
383
+ let cleanBalance = balanceStr;
384
+ if (symbol) {
385
+ cleanBalance = balanceStr.replace(new RegExp(`\\s*${escapeRegExp(symbol)}$`, "i"), "");
386
+ }
387
+ const formatted = formatTokenAmountDisplay(cleanBalance);
388
+ return symbol ? `${formatted} ${symbol}` : formatted;
389
+ };
390
+ const parseTokenAmount = (value) => {
391
+ if (value === null || value === undefined || value === "")
392
+ return undefined;
393
+ if (Decimal.isDecimal(value))
394
+ return value;
395
+ const cleaned = String(value).replace(/[^0-9.-]/g, "");
396
+ if (!cleaned || cleaned === "-" || cleaned === "." || cleaned === "-.") {
397
+ return undefined;
398
+ }
399
+ try {
400
+ const parsed = new Decimal(cleaned);
401
+ return parsed.isFinite() ? parsed : undefined;
402
+ }
403
+ catch {
404
+ return undefined;
405
+ }
406
+ };
407
+ const compareTokensByUsdBalance = (a, b) => {
408
+ const fiatDelta = getTokenFiatValue(b) - getTokenFiatValue(a);
409
+ if (fiatDelta !== 0)
410
+ return fiatDelta;
411
+ const aBalance = parseTokenAmount(a.balance) ?? new Decimal(0);
412
+ const bBalance = parseTokenAmount(b.balance) ?? new Decimal(0);
413
+ const balanceDelta = bBalance.cmp(aBalance);
414
+ if (balanceDelta !== 0)
415
+ return balanceDelta;
416
+ const chainDelta = compareChainsBySwapDisplayOrder(a, b);
417
+ if (chainDelta !== 0)
418
+ return chainDelta;
419
+ return `${a.symbol} ${a.chainName}`.localeCompare(`${b.symbol} ${b.chainName}`);
420
+ };
421
+ const sortTokensByUsdBalance = (tokens) => [...tokens].sort(compareTokensByUsdBalance);
422
+ export const formatTokenAmountDisplay = (value) => {
423
+ const amount = parseTokenAmount(value) ?? new Decimal(0);
424
+ const abs = amount.abs();
425
+ if (amount.isZero())
426
+ return "0";
427
+ const compactUnits = [
428
+ { suffix: "T", value: new Decimal(1_000_000_000_000) },
429
+ { suffix: "B", value: new Decimal(1_000_000_000) },
430
+ { suffix: "M", value: new Decimal(1_000_000) },
431
+ ];
432
+ for (const unit of compactUnits) {
433
+ if (abs.gte(unit.value)) {
434
+ return `${amount
435
+ .div(unit.value)
436
+ .toDecimalPlaces(4, Decimal.ROUND_DOWN)
437
+ .toFixed()}${unit.suffix}`;
438
+ }
439
+ }
440
+ const minDisplay = new Decimal("0.00000001");
441
+ if (amount.gt(0) && amount.lt(minDisplay)) {
442
+ return `>${minDisplay.toFixed()}`;
443
+ }
444
+ return amount.toDecimalPlaces(8, Decimal.ROUND_DOWN).toFixed();
445
+ };
446
+ const addThousandsSeparators = (value) => {
447
+ const [integerPart, decimalPart] = value.split(".");
448
+ const withSeparators = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
449
+ return decimalPart === undefined
450
+ ? withSeparators
451
+ : `${withSeparators}.${decimalPart}`;
452
+ };
453
+ export const formatUsdBalanceLabel = (value) => {
454
+ const amount = parseTokenAmount(value) ?? new Decimal(0);
455
+ const abs = amount.abs();
456
+ if (amount.isZero())
457
+ return "$0.00";
458
+ if (amount.gt(0) && amount.lt(0.01))
459
+ return "<$0.01";
460
+ const compactUnits = [
461
+ { suffix: "T", value: new Decimal(1_000_000_000_000) },
462
+ { suffix: "B", value: new Decimal(1_000_000_000) },
463
+ { suffix: "M", value: new Decimal(1_000_000) },
464
+ ];
465
+ for (const unit of compactUnits) {
466
+ if (abs.gte(unit.value)) {
467
+ return `$${amount
468
+ .div(unit.value)
469
+ .toDecimalPlaces(4, Decimal.ROUND_DOWN)
470
+ .toFixed()}${unit.suffix}`;
471
+ }
472
+ }
473
+ return `$${addThousandsSeparators(amount.toDecimalPlaces(2).toFixed(2))}`;
474
+ };
475
+ export const formatSelectedTokenBalanceLabel = (token) => {
476
+ if (!token)
477
+ return "";
478
+ const symbol = token.symbol || "";
479
+ const formatted = formatTokenAmountDisplay(token.balance);
480
+ return symbol ? `${formatted} ${symbol}` : formatted;
481
+ };
482
+ const getSearchTerms = (query) => query.toLowerCase().trim().split(/\s+/).filter(Boolean);
483
+ const includesTerm = (value, term) => (value ?? "").toLowerCase().includes(term);
484
+ const startsWithTerm = (value, term) => (value ?? "").toLowerCase().startsWith(term);
485
+ const equalsTerm = (value, term) => (value ?? "").toLowerCase() === term;
486
+ export const getTokenSearchRank = (token, query) => {
487
+ const terms = getSearchTerms(query);
488
+ if (terms.length === 0)
489
+ return null;
490
+ let matchedTerms = 0;
491
+ let symbolExactTerms = 0;
492
+ let symbolPrefixTerms = 0;
493
+ let symbolIncludeTerms = 0;
494
+ let namePrefixTerms = 0;
495
+ let tokenExactTerms = 0;
496
+ let tokenPrefixTerms = 0;
497
+ let tokenIncludeTerms = 0;
498
+ let chainExactTerms = 0;
499
+ let chainPrefixTerms = 0;
500
+ let chainIncludeTerms = 0;
501
+ let addressTerms = 0;
502
+ for (const term of terms) {
503
+ const symbolExact = equalsTerm(token.symbol, term);
504
+ const symbolPrefix = symbolExact || startsWithTerm(token.symbol, term);
505
+ const symbolInclude = symbolPrefix || includesTerm(token.symbol, term);
506
+ const nameExact = equalsTerm(token.name, term);
507
+ const namePrefix = nameExact || startsWithTerm(token.name, term);
508
+ const nameInclude = namePrefix || includesTerm(token.name, term);
509
+ const tokenExact = symbolExact || nameExact;
510
+ const tokenPrefix = tokenExact || symbolPrefix || namePrefix;
511
+ const tokenInclude = tokenPrefix || symbolInclude || nameInclude;
512
+ const chainExact = equalsTerm(token.chainName, term);
513
+ const chainPrefix = chainExact || startsWithTerm(token.chainName, term);
514
+ const chainInclude = chainPrefix || includesTerm(token.chainName, term);
515
+ const addressMatch = includesTerm(token.contractAddress, term);
516
+ if (tokenInclude || chainInclude || addressMatch)
517
+ matchedTerms += 1;
518
+ if (symbolExact)
519
+ symbolExactTerms += 1;
520
+ if (symbolPrefix)
521
+ symbolPrefixTerms += 1;
522
+ if (symbolInclude)
523
+ symbolIncludeTerms += 1;
524
+ if (namePrefix)
525
+ namePrefixTerms += 1;
526
+ if (tokenExact)
527
+ tokenExactTerms += 1;
528
+ if (tokenPrefix)
529
+ tokenPrefixTerms += 1;
530
+ if (tokenInclude)
531
+ tokenIncludeTerms += 1;
532
+ if (chainExact)
533
+ chainExactTerms += 1;
534
+ if (chainPrefix)
535
+ chainPrefixTerms += 1;
536
+ if (chainInclude)
537
+ chainIncludeTerms += 1;
538
+ if (addressMatch)
539
+ addressTerms += 1;
540
+ }
541
+ const allTermsMatched = matchedTerms === terms.length;
542
+ if (!allTermsMatched)
543
+ return null;
544
+ if (terms.length > 1 &&
545
+ chainIncludeTerms > 0 &&
546
+ symbolIncludeTerms === 0 &&
547
+ addressTerms === 0 &&
548
+ namePrefixTerms === 0) {
549
+ return null;
550
+ }
551
+ const hasTokenMatch = tokenIncludeTerms > 0;
552
+ const hasChainMatch = chainIncludeTerms > 0;
553
+ const isTokenChainMatch = allTermsMatched && hasTokenMatch && hasChainMatch;
554
+ let score = 20;
555
+ if (isTokenChainMatch) {
556
+ if (symbolExactTerms > 0 && chainExactTerms > 0)
557
+ score = 0;
558
+ else if (symbolExactTerms > 0 && chainPrefixTerms > 0)
559
+ score = 1;
560
+ else if (symbolExactTerms > 0 && chainIncludeTerms > 0)
561
+ score = 2;
562
+ else if (symbolPrefixTerms > 0 && chainIncludeTerms > 0)
563
+ score = 3;
564
+ else if (symbolIncludeTerms > 0 && chainIncludeTerms > 0)
565
+ score = 4;
566
+ else if (namePrefixTerms > 0 && chainIncludeTerms > 0)
567
+ score = 5;
568
+ else
569
+ score = 6;
570
+ }
571
+ else if (symbolExactTerms > 0)
572
+ score = 7;
573
+ else if (symbolPrefixTerms > 0)
574
+ score = 8;
575
+ else if (symbolIncludeTerms > 0)
576
+ score = 9;
577
+ else if (tokenExactTerms > 0)
578
+ score = 10;
579
+ else if (tokenPrefixTerms > 0)
580
+ score = 11;
581
+ else if (tokenIncludeTerms > 0)
582
+ score = 12;
583
+ else if (chainExactTerms > 0)
584
+ score = 13;
585
+ else if (chainPrefixTerms > 0)
586
+ score = 14;
587
+ else if (chainIncludeTerms > 0)
588
+ score = 15;
589
+ else if (addressTerms > 0)
590
+ score = 16;
591
+ return {
592
+ allTermsMatched,
593
+ isTokenChainMatch,
594
+ matchedTerms,
595
+ score,
596
+ tokenExactTerms,
597
+ tokenIncludeTerms,
598
+ };
599
+ };
600
+ const isPrioritySearchMatch = (token, query) => {
601
+ const rank = getTokenSearchRank(token, query);
602
+ return Boolean(rank &&
603
+ (rank.isTokenChainMatch ||
604
+ rank.tokenExactTerms > 0 ||
605
+ rank.allTermsMatched));
606
+ };
607
+ const compareTokensBySearch = (a, b, query) => {
608
+ const aRank = getTokenSearchRank(a, query);
609
+ const bRank = getTokenSearchRank(b, query);
610
+ const aScore = aRank?.score ?? Number.MAX_SAFE_INTEGER;
611
+ const bScore = bRank?.score ?? Number.MAX_SAFE_INTEGER;
612
+ if (aScore !== bScore)
613
+ return aScore - bScore;
614
+ const aMatched = aRank?.matchedTerms ?? 0;
615
+ const bMatched = bRank?.matchedTerms ?? 0;
616
+ if (aMatched !== bMatched)
617
+ return bMatched - aMatched;
618
+ const aFiat = getTokenFiatValue(a);
619
+ const bFiat = getTokenFiatValue(b);
620
+ if (aFiat !== bFiat)
621
+ return bFiat - aFiat;
622
+ return `${a.symbol} ${a.chainName}`.localeCompare(`${b.symbol} ${b.chainName}`);
623
+ };
624
+ function getUnifiedSymbol(token) {
625
+ if (token.chainId && !UNIFIED_MAINNET_CHAIN_IDS.has(token.chainId)) {
626
+ return null;
627
+ }
628
+ const symbol = normalizeTokenGroupSymbol(token.symbol);
629
+ if (symbol.includes("USDC") || symbol === "USDM")
630
+ return "USDC";
631
+ if (symbol.includes("USDT"))
632
+ return "USDT";
633
+ if (symbol === "ETH")
634
+ return "ETH";
635
+ return null;
636
+ }
637
+ function sameTokenOption(a, b) {
638
+ if (!a || !b)
639
+ return false;
640
+ if (a.isUnified || b.isUnified) {
641
+ return Boolean(a.isUnified && b.isUnified && a.unifiedSymbol === b.unifiedSymbol);
642
+ }
643
+ return (sameContractAddress(a.contractAddress, b.contractAddress) &&
644
+ a.chainId === b.chainId);
645
+ }
646
+ function isSameTokenList(a, b) {
647
+ if (a.length !== b.length)
648
+ return false;
649
+ return a.every((tokenA) => b.some((tokenB) => sameTokenOption(tokenA, tokenB)));
650
+ }
651
+ function dedupeTokenOptions(tokens) {
652
+ return tokens.reduce((acc, token) => {
653
+ if (!acc.some((item) => sameTokenOption(item, token))) {
654
+ acc.push(token);
655
+ }
656
+ return acc;
657
+ }, []);
658
+ }
659
+ function mergeTokenOptions(base, additions) {
660
+ return dedupeTokenOptions([...base, ...additions]);
661
+ }
662
+ function removeTokenOptions(base, removals) {
663
+ return base.filter((token) => !removals.some((removal) => sameTokenOption(token, removal)));
664
+ }
665
+ function isNativeLikeAddress(address) {
666
+ const normalized = (address ?? "").toLowerCase();
667
+ return (normalized === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ||
668
+ normalized === "0x0000000000000000000000000000000000000000");
669
+ }
670
+ function addressTail(address) {
671
+ const normalized = (address ?? "").toLowerCase();
672
+ if (!normalized.startsWith("0x"))
673
+ return normalized;
674
+ return normalized.slice(-40);
675
+ }
676
+ function sameContractAddress(a, b) {
677
+ const normalizedA = (a ?? "").toLowerCase();
678
+ const normalizedB = (b ?? "").toLowerCase();
679
+ if (!normalizedA || !normalizedB)
680
+ return normalizedA === normalizedB;
681
+ if (normalizedA === normalizedB)
682
+ return true;
683
+ if (isNativeLikeAddress(normalizedA) && isNativeLikeAddress(normalizedB)) {
684
+ return true;
685
+ }
686
+ return addressTail(normalizedA) === addressTail(normalizedB);
687
+ }
688
+ export function SwapAssetSelector({ title, swapBalance, swapSupportedChains, staticOptions, onSelect, onBack, isMulti, selectedTokens = [], editingAssetIndex = null, onToggle, onClearSelection, onDone, allowUnified = false, preserveSelectedBelowMinimum = false, showBelowMinimumInline = false, allowSelectedTokenRemoval = false, hideCustomTab = false, autoSelectFilterTabs = false, initialFilterTab = "all", filterTabBehavior = "select-all", onFilterTabSelect, lockedTokens = [], onSelectionChange, requiredUsd, }) {
689
+ const sdkSwapSupportedChainIds = useMemo(() => getSdkSwapSupportedChainIds(swapSupportedChains), [swapSupportedChains]);
690
+ const selectorRef = useRef(null);
691
+ const listRef = useRef(null);
692
+ const chainCloseTimerRef = useRef(null);
693
+ const [portalRoot, setPortalRoot] = useState(null);
694
+ const [query, setQuery] = useState("");
695
+ const normalizedInitialFilterTab = hideCustomTab && initialFilterTab === "custom" ? "all" : initialFilterTab;
696
+ const [activeTab, setActiveTab] = useState(normalizedInitialFilterTab);
697
+ const [showBelowMin, setShowBelowMin] = useState(false);
698
+ const [isSearchFocused, setIsSearchFocused] = useState(false);
699
+ const [showChainSelector, setShowChainSelector] = useState(false);
700
+ const [isChainSelectorClosing, setIsChainSelectorClosing] = useState(false);
701
+ const [chainQuery, setChainQuery] = useState("");
702
+ const [selectedChainFilter, setSelectedChainFilter] = useState(null);
703
+ const [isChainSearchFocused, setIsChainSearchFocused] = useState(false);
704
+ const stableListHeightRef = useRef(0);
705
+ const [stableListHeight, setStableListHeight] = useState(null);
706
+ const lockedSelectedTokens = useMemo(() => dedupeTokenOptions(lockedTokens), [lockedTokens]);
707
+ const isLockedToken = useCallback((token) => lockedSelectedTokens.some((locked) => sameTokenOption(locked, token)), [lockedSelectedTokens]);
708
+ const [draftSelectedTokens, setDraftSelectedTokens] = useState(() => mergeTokenOptions(selectedTokens, lockedSelectedTokens));
709
+ useEffect(() => {
710
+ if (!isMulti)
711
+ return;
712
+ setDraftSelectedTokens(mergeTokenOptions(selectedTokens, lockedSelectedTokens));
713
+ }, [isMulti, lockedSelectedTokens, selectedTokens]);
714
+ const activeSelectedTokens = isMulti ? draftSelectedTokens : selectedTokens;
715
+ const emitSelectionChange = useCallback((tokens) => {
716
+ const next = mergeTokenOptions(tokens, lockedSelectedTokens);
717
+ if (isMulti) {
718
+ setDraftSelectedTokens(next);
719
+ return;
720
+ }
721
+ onSelectionChange?.(next);
722
+ }, [isMulti, lockedSelectedTokens, onSelectionChange]);
723
+ const visibleFilterTabs = useMemo(() => hideCustomTab
724
+ ? FILTER_TABS.filter((tab) => tab.key !== "custom")
725
+ : FILTER_TABS, [hideCustomTab]);
726
+ useEffect(() => {
727
+ if (hideCustomTab && activeTab === "custom") {
728
+ setActiveTab("all");
729
+ }
730
+ }, [activeTab, hideCustomTab]);
731
+ useEffect(() => {
732
+ setActiveTab((current) => current === normalizedInitialFilterTab
733
+ ? current
734
+ : normalizedInitialFilterTab);
735
+ }, [normalizedInitialFilterTab]);
736
+ useEffect(() => {
737
+ if (listRef.current) {
738
+ listRef.current.scrollTop = 0;
739
+ }
740
+ }, [query, activeTab, selectedChainFilter]);
741
+ const allTokens = useMemo(() => {
742
+ const isSwapSupportedToken = (token) => isSwapSupportedBySdkChainList(token.chainId, swapSupportedChains);
743
+ const baseTokens = staticOptions
744
+ ? staticOptions.filter(isSwapSupportedToken)
745
+ : swapBalance
746
+ ? deriveTokenOptions(swapBalance, swapSupportedChains)
747
+ : [];
748
+ if (!preserveSelectedBelowMinimum && lockedSelectedTokens.length === 0) {
749
+ return sortTokensByUsdBalance(baseTokens);
750
+ }
751
+ const merged = [...baseTokens];
752
+ const selectedSourceTokens = [
753
+ ...activeSelectedTokens,
754
+ ...lockedSelectedTokens,
755
+ ].flatMap((token) => token.isUnified && token.sourceTokens?.length
756
+ ? token.sourceTokens
757
+ : [token]);
758
+ for (const selectedToken of selectedSourceTokens) {
759
+ const alreadyPresent = merged.some((token) => sameTokenOption(token, selectedToken));
760
+ if (!alreadyPresent) {
761
+ merged.push(selectedToken);
762
+ }
763
+ }
764
+ return sortTokensByUsdBalance(merged.filter(isSwapSupportedToken));
765
+ }, [
766
+ lockedSelectedTokens,
767
+ preserveSelectedBelowMinimum,
768
+ activeSelectedTokens,
769
+ swapBalance,
770
+ swapSupportedChains,
771
+ staticOptions,
772
+ ]);
773
+ const getFilterTabTokens = useCallback((tab) => {
774
+ let result = allTokens;
775
+ if (selectedChainFilter !== null) {
776
+ result = result.filter((token) => token.chainId === selectedChainFilter);
777
+ }
778
+ if (tab === "native")
779
+ result = result.filter(isNativeToken);
780
+ else if (tab === "stables") {
781
+ result = result.filter(isStableToken);
782
+ }
783
+ if (showBelowMinimumInline) {
784
+ return mergeTokenOptions(result, lockedSelectedTokens);
785
+ }
786
+ return mergeTokenOptions(result.filter((token) => getTokenFiatValue(token) >= MIN_FIAT_THRESHOLD), lockedSelectedTokens.filter((token) => getTokenFiatValue(token) >= MIN_FIAT_THRESHOLD));
787
+ }, [allTokens, lockedSelectedTokens, selectedChainFilter]);
788
+ const selectionMatchesFilterTab = useCallback((tab) => {
789
+ if (tab === "custom")
790
+ return true;
791
+ const expected = getFilterTabTokens(tab);
792
+ const selected = mergeTokenOptions(activeSelectedTokens, lockedSelectedTokens);
793
+ return (selected.length === expected.length &&
794
+ selected.every((token) => expected.some((expectedToken) => sameTokenOption(expectedToken, token))));
795
+ }, [activeSelectedTokens, getFilterTabTokens, lockedSelectedTokens]);
796
+ useEffect(() => {
797
+ if (!autoSelectFilterTabs ||
798
+ filterTabBehavior === "source-pool" ||
799
+ !isMulti ||
800
+ activeTab === "custom")
801
+ return;
802
+ if (activeSelectedTokens.length === 0 && lockedSelectedTokens.length === 0)
803
+ return;
804
+ if (!selectionMatchesFilterTab(activeTab)) {
805
+ setActiveTab("custom");
806
+ }
807
+ }, [
808
+ activeTab,
809
+ activeSelectedTokens.length,
810
+ autoSelectFilterTabs,
811
+ filterTabBehavior,
812
+ isMulti,
813
+ lockedSelectedTokens.length,
814
+ selectionMatchesFilterTab,
815
+ ]);
816
+ /* Search + tab + chain filter */
817
+ const filtered = useMemo(() => {
818
+ let result = allTokens;
819
+ if (selectedChainFilter !== null) {
820
+ result = result.filter((t) => t.chainId === selectedChainFilter);
821
+ }
822
+ if (query.trim()) {
823
+ result = result
824
+ .filter((t) => getTokenSearchRank(t, query) !== null)
825
+ .sort((a, b) => compareTokensBySearch(a, b, query));
826
+ }
827
+ if (activeTab === "native")
828
+ result = result.filter(isNativeToken);
829
+ else if (activeTab === "stables")
830
+ result = result.filter(isStableToken);
831
+ else if (activeTab === "custom" && !autoSelectFilterTabs)
832
+ result = result.filter((t) => !isNativeToken(t) && !isStableToken(t));
833
+ return result;
834
+ }, [activeTab, allTokens, autoSelectFilterTabs, query, selectedChainFilter]);
835
+ const isTokenSelectedForVisibility = useCallback((token) => {
836
+ if (!preserveSelectedBelowMinimum)
837
+ return false;
838
+ return activeSelectedTokens.some((selected) => sameTokenOption(selected, token) ||
839
+ Boolean(selected.isUnified &&
840
+ selected.sourceTokens?.some((source) => sameTokenOption(source, token))));
841
+ }, [activeSelectedTokens, preserveSelectedBelowMinimum]);
842
+ const isUnifiedSelectedForVisibility = useCallback((symbol) => preserveSelectedBelowMinimum &&
843
+ activeSelectedTokens.some((selected) => selected.isUnified && selected.unifiedSymbol === symbol), [activeSelectedTokens, preserveSelectedBelowMinimum]);
844
+ /* Split into above/below minimum */
845
+ const { aboveMin, belowMin } = useMemo(() => {
846
+ const above = [];
847
+ const below = [];
848
+ for (const t of filtered) {
849
+ const fiat = getTokenFiatValue(t);
850
+ if (showBelowMinimumInline ||
851
+ fiat >= MIN_FIAT_THRESHOLD ||
852
+ isTokenSelectedForVisibility(t) ||
853
+ isPrioritySearchMatch(t, query))
854
+ above.push(t);
855
+ else
856
+ below.push(t);
857
+ }
858
+ return { aboveMin: above, belowMin: below };
859
+ }, [filtered, isTokenSelectedForVisibility, query, showBelowMinimumInline]);
860
+ /* Group by symbol */
861
+ const groupedFiltered = useMemo(() => {
862
+ const groups = {};
863
+ for (const token of filtered) {
864
+ const unifiedSym = allowUnified ? getUnifiedSymbol(token) : null;
865
+ if (!showBelowMinimumInline &&
866
+ unifiedSym &&
867
+ getTokenFiatValue(token) < MIN_FIAT_THRESHOLD) {
868
+ continue;
869
+ }
870
+ const key = unifiedSym ?? `${token.contractAddress}-${token.chainId}`;
871
+ if (!groups[key])
872
+ groups[key] = [];
873
+ groups[key].push(token);
874
+ }
875
+ return Object.values(groups)
876
+ .map((group) => {
877
+ const sortedGroup = sortTokensByUsdBalance(group);
878
+ let totalFiatVal = 0;
879
+ let totalBalVal = 0;
880
+ for (const t of sortedGroup) {
881
+ const fiatVal = getTokenFiatValue(t);
882
+ totalFiatVal += isNaN(fiatVal) || !isFinite(fiatVal) ? 0 : fiatVal;
883
+ const balStr = String(t.balance ?? "").replace(/[^0-9.]/g, "");
884
+ const balVal = Number(balStr || 0);
885
+ totalBalVal += isNaN(balVal) || !isFinite(balVal) ? 0 : balVal;
886
+ }
887
+ const unifiedSym = allowUnified
888
+ ? getUnifiedSymbol(sortedGroup[0])
889
+ : null;
890
+ const symbol = unifiedSym ?? sortedGroup[0].symbol;
891
+ const isStable = ["USDC", "USDT", "DAI", "USDM", "CTUSD"].includes(symbol.toUpperCase());
892
+ const maxDigits = isStable ? 2 : 6;
893
+ const formattedBal = totalBalVal.toLocaleString(undefined, {
894
+ minimumFractionDigits: 0,
895
+ maximumFractionDigits: maxDigits,
896
+ });
897
+ return {
898
+ symbol,
899
+ logo: sortedGroup[0].logo,
900
+ totalFiat: totalFiatVal,
901
+ totalFiatStr: `$${totalFiatVal.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`,
902
+ totalBalStr: `${formattedBal} ${symbol}`,
903
+ totalBalRaw: totalBalVal,
904
+ tokens: sortedGroup,
905
+ isUnifiedCandidate: Boolean(unifiedSym && group.length > 1),
906
+ };
907
+ })
908
+ .filter((group) => {
909
+ const hasSelectedToken = group.tokens.some(isTokenSelectedForVisibility);
910
+ const hasSelectedUnified = isUnifiedSelectedForVisibility(group.symbol);
911
+ const hasPrioritySearchMatch = group.tokens.some((token) => isPrioritySearchMatch(token, query));
912
+ if (showBelowMinimumInline) {
913
+ return true;
914
+ }
915
+ if (group.isUnifiedCandidate) {
916
+ return (group.totalFiat >= MIN_FIAT_THRESHOLD ||
917
+ hasSelectedToken ||
918
+ hasSelectedUnified ||
919
+ hasPrioritySearchMatch);
920
+ }
921
+ return group.tokens.some((token) => getTokenFiatValue(token) >= MIN_FIAT_THRESHOLD ||
922
+ isTokenSelectedForVisibility(token) ||
923
+ isPrioritySearchMatch(token, query));
924
+ })
925
+ .sort((a, b) => {
926
+ if (query.trim()) {
927
+ const aScore = Math.min(...a.tokens.map((token) => getTokenSearchRank(token, query)?.score ??
928
+ Number.MAX_SAFE_INTEGER));
929
+ const bScore = Math.min(...b.tokens.map((token) => getTokenSearchRank(token, query)?.score ??
930
+ Number.MAX_SAFE_INTEGER));
931
+ if (aScore !== bScore)
932
+ return aScore - bScore;
933
+ }
934
+ return b.totalFiat - a.totalFiat;
935
+ });
936
+ }, [
937
+ filtered,
938
+ allowUnified,
939
+ isTokenSelectedForVisibility,
940
+ isUnifiedSelectedForVisibility,
941
+ query,
942
+ ]);
943
+ const [expandedGroups, setExpandedGroups] = useState(new Set());
944
+ const toggleGroup = (symbol, e) => {
945
+ e.stopPropagation();
946
+ setExpandedGroups((prev) => {
947
+ const next = new Set(prev);
948
+ if (next.has(symbol))
949
+ next.delete(symbol);
950
+ else
951
+ next.add(symbol);
952
+ return next;
953
+ });
954
+ };
955
+ const isTokenSelectedInOtherSlot = (token) => !allowSelectedTokenRemoval &&
956
+ activeSelectedTokens.some((st, idx) => idx !== editingAssetIndex && sameTokenOption(st, token));
957
+ const isTokenSelectedInCurrentSlot = (token) => {
958
+ if (isMulti) {
959
+ return activeSelectedTokens.some((st) => sameTokenOption(st, token) ||
960
+ Boolean(st.isUnified &&
961
+ st.sourceTokens?.some((source) => sameTokenOption(source, token))));
962
+ }
963
+ if (allowSelectedTokenRemoval) {
964
+ return activeSelectedTokens.some((st) => sameTokenOption(st, token) ||
965
+ Boolean(st.isUnified &&
966
+ st.sourceTokens?.some((source) => sameTokenOption(source, token))));
967
+ }
968
+ if (editingAssetIndex === null)
969
+ return false;
970
+ const st = activeSelectedTokens[editingAssetIndex];
971
+ return sameTokenOption(st, token);
972
+ };
973
+ const isGroupUnifiedSelectedInOtherSlot = (group) => {
974
+ if (allowSelectedTokenRemoval)
975
+ return false;
976
+ const relevantTokens = isMulti
977
+ ? activeSelectedTokens
978
+ : activeSelectedTokens.filter((_, idx) => idx !== editingAssetIndex);
979
+ return relevantTokens.some((st) => st.isUnified && st.unifiedSymbol === group.symbol);
980
+ };
981
+ const isGroupUnifiedSelectedInCurrentSlot = (group) => {
982
+ if (isMulti) {
983
+ return activeSelectedTokens.some((st) => st.isUnified && st.unifiedSymbol === group.symbol);
984
+ }
985
+ if (editingAssetIndex === null)
986
+ return false;
987
+ const st = activeSelectedTokens[editingAssetIndex];
988
+ return Boolean(st?.isUnified && st.unifiedSymbol === group.symbol);
989
+ };
990
+ const isAnyTokenInGroupSelectedInOtherSlot = (group) => {
991
+ if (allowSelectedTokenRemoval)
992
+ return false;
993
+ const relevantTokens = isMulti
994
+ ? activeSelectedTokens
995
+ : activeSelectedTokens.filter((_, idx) => idx !== editingAssetIndex);
996
+ return relevantTokens.some((st) => group.tokens.some((gt) => sameTokenOption(gt, st)) ||
997
+ (st.isUnified && st.unifiedSymbol === group.symbol));
998
+ };
999
+ const handleFilterTabClick = (tab) => {
1000
+ setActiveTab(tab);
1001
+ if (autoSelectFilterTabs && isMulti && tab !== "custom") {
1002
+ if (filterTabBehavior === "source-pool") {
1003
+ onFilterTabSelect?.(tab);
1004
+ return;
1005
+ }
1006
+ if (!onSelectionChange)
1007
+ return;
1008
+ emitSelectionChange(getFilterTabTokens(tab));
1009
+ }
1010
+ };
1011
+ const handleClearSelection = () => {
1012
+ if (isMulti && onSelectionChange) {
1013
+ setActiveTab("custom");
1014
+ emitSelectionChange([]);
1015
+ return;
1016
+ }
1017
+ onClearSelection?.();
1018
+ };
1019
+ const handleMultiTokenToggle = (token) => {
1020
+ if (!autoSelectFilterTabs || !isMulti || !onSelectionChange) {
1021
+ onToggle?.(token);
1022
+ return;
1023
+ }
1024
+ setActiveTab("custom");
1025
+ const current = mergeTokenOptions(activeSelectedTokens, lockedSelectedTokens);
1026
+ const targets = token.isUnified && token.sourceTokens?.length
1027
+ ? token.sourceTokens
1028
+ : [token];
1029
+ const unlockedTargets = targets.filter((target) => !isLockedToken(target));
1030
+ if (unlockedTargets.length === 0)
1031
+ return;
1032
+ const allTargetsSelected = unlockedTargets.every((target) => current.some((item) => sameTokenOption(item, target)));
1033
+ const next = allTargetsSelected
1034
+ ? removeTokenOptions(current, unlockedTargets)
1035
+ : mergeTokenOptions(current, unlockedTargets);
1036
+ emitSelectionChange(next);
1037
+ };
1038
+ /* ── Render a single-chain token row ── */
1039
+ const renderTokenRow = (token, indent = false, isDisabledByUnified = false) => {
1040
+ const selectedInOther = !isMulti && isTokenSelectedInOtherSlot(token);
1041
+ if (selectedInOther)
1042
+ return null;
1043
+ const selectedInCurrent = isTokenSelectedInCurrentSlot(token);
1044
+ const locked = isLockedToken(token);
1045
+ const disabled = isDisabledByUnified || locked;
1046
+ return (_jsxs("button", { disabled: disabled, onClick: () => {
1047
+ if (disabled)
1048
+ return;
1049
+ if (isMulti) {
1050
+ handleMultiTokenToggle(token);
1051
+ }
1052
+ else if (allowSelectedTokenRemoval &&
1053
+ selectedInCurrent &&
1054
+ onToggle) {
1055
+ onToggle(token);
1056
+ }
1057
+ else
1058
+ onSelect(token);
1059
+ }, style: {
1060
+ width: "100%",
1061
+ display: "flex",
1062
+ alignItems: "center",
1063
+ justifyContent: "space-between",
1064
+ padding: "10px 14px",
1065
+ paddingLeft: indent ? "36px" : "14px",
1066
+ backgroundColor: "transparent",
1067
+ border: "none",
1068
+ cursor: disabled ? "not-allowed" : "pointer",
1069
+ borderBottom: "1px solid #F0F0EF",
1070
+ boxSizing: "border-box",
1071
+ opacity: isDisabledByUnified ? 0.5 : 1,
1072
+ }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [_jsx(SelectionControl, { multi: Boolean(isMulti), selected: selectedInCurrent }), _jsx("div", { style: { flexShrink: 0, width: 40, height: 40 }, children: token.logo ? (_jsx("img", { alt: token.symbol, onError: (e) => {
1073
+ e.target.style.display = "none";
1074
+ }, src: token.logo, style: {
1075
+ width: 40,
1076
+ height: 40,
1077
+ borderRadius: "999px",
1078
+ objectFit: "cover",
1079
+ } })) : (_jsx("div", { style: {
1080
+ width: 40,
1081
+ height: 40,
1082
+ borderRadius: "999px",
1083
+ backgroundColor: brand,
1084
+ display: "flex",
1085
+ alignItems: "center",
1086
+ justifyContent: "center",
1087
+ color: "#fff",
1088
+ fontSize: 14,
1089
+ fontWeight: 700,
1090
+ }, children: token.symbol.slice(0, 2) })) }), _jsxs("div", { style: {
1091
+ display: "flex",
1092
+ flexDirection: "column",
1093
+ alignItems: "flex-start",
1094
+ }, children: [_jsx("span", { style: {
1095
+ fontFamily: '"Geist", system-ui, sans-serif',
1096
+ fontWeight: 500,
1097
+ fontSize: 15,
1098
+ color: "#161615",
1099
+ }, children: token.symbol }), token.chainName && (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [token.chainLogo && (_jsx("img", { alt: "", src: token.chainLogo, style: {
1100
+ borderRadius: "999px",
1101
+ height: 14,
1102
+ objectFit: "cover",
1103
+ width: 14,
1104
+ } })), _jsx("span", { style: {
1105
+ fontFamily: '"Geist", system-ui, sans-serif',
1106
+ fontSize: 13,
1107
+ color: "#848483",
1108
+ }, children: token.chainName })] }))] })] }), _jsxs("div", { style: {
1109
+ display: "flex",
1110
+ flexDirection: "column",
1111
+ alignItems: "flex-end",
1112
+ }, children: [_jsx("span", { style: {
1113
+ fontFamily: '"Geist", system-ui, sans-serif',
1114
+ fontWeight: 500,
1115
+ fontSize: 14,
1116
+ color: "#161615",
1117
+ }, children: formatBalanceWithSymbol(token) }), _jsxs("span", { style: {
1118
+ fontFamily: '"Geist", system-ui, sans-serif',
1119
+ fontSize: 13,
1120
+ color: "#848483",
1121
+ }, children: ["\u2248 ", token.balanceInFiat] })] })] }, `${token.contractAddress}-${token.chainId}`));
1122
+ };
1123
+ /* ── Render a unified (multi-chain) group row ── */
1124
+ const renderGroupRow = (group) => {
1125
+ if (!group.isUnifiedCandidate) {
1126
+ return group.tokens
1127
+ .filter((token) => getTokenFiatValue(token) >= MIN_FIAT_THRESHOLD ||
1128
+ isTokenSelectedForVisibility(token) ||
1129
+ isPrioritySearchMatch(token, query))
1130
+ .map((token) => renderTokenRow(token));
1131
+ }
1132
+ const unifiedSelectedInOther = !isMulti && isGroupUnifiedSelectedInOtherSlot(group);
1133
+ if (unifiedSelectedInOther)
1134
+ return null;
1135
+ const individualTokens = group.tokens.filter((token) => getTokenFiatValue(token) >= MIN_FIAT_THRESHOLD ||
1136
+ isTokenSelectedForVisibility(token) ||
1137
+ isPrioritySearchMatch(token, query));
1138
+ const hasVisibleUnifiedRow = (group.totalFiat >= MIN_FIAT_THRESHOLD ||
1139
+ isUnifiedSelectedForVisibility(group.symbol)) &&
1140
+ !unifiedSelectedInOther;
1141
+ const visibleTokensCount = individualTokens.filter((t) => !isTokenSelectedInOtherSlot(t)).length;
1142
+ if (!hasVisibleUnifiedRow && visibleTokensCount === 0)
1143
+ return null;
1144
+ const isExpanded = expandedGroups.has(group.symbol);
1145
+ const unifiedSelectedInCurrent = isGroupUnifiedSelectedInCurrentSlot(group);
1146
+ const anyIndividualSelectedInOther = isAnyTokenInGroupSelectedInOtherSlot(group);
1147
+ const anyIndividualSelectedInCurrent = group.tokens.some(isTokenSelectedInCurrentSlot);
1148
+ const selectedChildCount = group.tokens.filter(isTokenSelectedInCurrentSlot).length;
1149
+ const areAllChildrenSelected = group.tokens.length > 0 && selectedChildCount === group.tokens.length;
1150
+ const isPartiallySelected = selectedChildCount > 0 && selectedChildCount < group.tokens.length;
1151
+ const shouldHideUnifiedRow = !isMulti &&
1152
+ (anyIndividualSelectedInOther ||
1153
+ anyIndividualSelectedInCurrent ||
1154
+ (group.totalFiat < MIN_FIAT_THRESHOLD &&
1155
+ !isUnifiedSelectedForVisibility(group.symbol)));
1156
+ const shouldHideIndividualRows = !isMulti && (unifiedSelectedInOther || unifiedSelectedInCurrent);
1157
+ const unifiedToken = {
1158
+ ...group.tokens[0],
1159
+ balance: String(group.totalBalRaw),
1160
+ balanceInFiat: group.totalFiatStr,
1161
+ chainId: undefined,
1162
+ chainName: "All Chains",
1163
+ chainLogo: "/nexus-widget/all-chains.png",
1164
+ contractAddress: `${group.symbol}-UNIFIED`,
1165
+ isUnified: true,
1166
+ name: group.symbol,
1167
+ sourceTokens: group.tokens,
1168
+ symbol: group.symbol,
1169
+ unifiedSymbol: group.symbol,
1170
+ };
1171
+ return (_jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [!shouldHideUnifiedRow && (_jsxs("button", { onClick: (e) => {
1172
+ if (isMulti) {
1173
+ toggleGroup(group.symbol, e);
1174
+ return;
1175
+ }
1176
+ onSelect(unifiedToken);
1177
+ }, style: {
1178
+ width: "100%",
1179
+ display: "flex",
1180
+ alignItems: "center",
1181
+ justifyContent: "space-between",
1182
+ padding: "10px 14px",
1183
+ backgroundColor: "transparent",
1184
+ border: "none",
1185
+ cursor: "pointer",
1186
+ borderBottom: "1px solid #F0F0EF",
1187
+ boxSizing: "border-box",
1188
+ }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [_jsx("div", { onClick: (e) => {
1189
+ e.stopPropagation();
1190
+ if (isMulti)
1191
+ handleMultiTokenToggle(unifiedToken);
1192
+ else
1193
+ onSelect(unifiedToken);
1194
+ }, style: { cursor: "pointer" }, children: _jsx(SelectionControl, { indeterminate: isMulti ? isPartiallySelected : false, multi: Boolean(isMulti), selected: isMulti ? areAllChildrenSelected : unifiedSelectedInCurrent }) }), _jsx("div", { style: {
1195
+ position: "relative",
1196
+ flexShrink: 0,
1197
+ width: 40,
1198
+ height: 40,
1199
+ }, children: group.logo ? (_jsx("img", { alt: group.symbol, onError: (e) => {
1200
+ e.target.style.display = "none";
1201
+ }, src: group.logo, style: {
1202
+ width: 40,
1203
+ height: 40,
1204
+ borderRadius: "999px",
1205
+ objectFit: "cover",
1206
+ } })) : (_jsx("div", { style: {
1207
+ width: 40,
1208
+ height: 40,
1209
+ borderRadius: "999px",
1210
+ backgroundColor: brand,
1211
+ display: "flex",
1212
+ alignItems: "center",
1213
+ justifyContent: "center",
1214
+ color: "#fff",
1215
+ fontSize: 14,
1216
+ fontWeight: 700,
1217
+ }, children: group.symbol.slice(0, 2) })) }), _jsxs("div", { style: {
1218
+ display: "flex",
1219
+ flexDirection: "column",
1220
+ alignItems: "flex-start",
1221
+ }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [_jsx("span", { style: {
1222
+ fontFamily: '"Geist", system-ui, sans-serif',
1223
+ fontWeight: 500,
1224
+ fontSize: 15,
1225
+ color: "#161615",
1226
+ }, children: group.symbol }), _jsx("span", { style: {
1227
+ fontFamily: '"Geist", system-ui, sans-serif',
1228
+ fontSize: 11,
1229
+ fontWeight: 600,
1230
+ color: brand,
1231
+ backgroundColor: "#E8F0FF",
1232
+ borderRadius: 4,
1233
+ padding: "2px 8px",
1234
+ letterSpacing: "0.04em",
1235
+ lineHeight: "18px",
1236
+ }, children: "UNIFIED" })] }), _jsx(ChainLogos, { tokens: group.tokens })] })] }), _jsx("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: _jsxs("div", { style: {
1237
+ display: "flex",
1238
+ flexDirection: "column",
1239
+ alignItems: "flex-end",
1240
+ }, children: [_jsx("span", { style: {
1241
+ fontFamily: '"Geist", system-ui, sans-serif',
1242
+ fontWeight: 500,
1243
+ fontSize: 14,
1244
+ color: "#161615",
1245
+ }, children: group.totalBalStr }), _jsxs("span", { style: {
1246
+ fontFamily: '"Geist", system-ui, sans-serif',
1247
+ fontSize: 13,
1248
+ color: "#848483",
1249
+ }, children: ["\u2248 ", group.totalFiatStr] })] }) })] })), isMulti ? (_jsx("div", { style: {
1250
+ display: "grid",
1251
+ gridTemplateRows: isExpanded ? "1fr" : "0fr",
1252
+ opacity: isExpanded ? 1 : 0,
1253
+ transition: "grid-template-rows 0.3s ease, opacity 0.3s ease",
1254
+ }, children: _jsx("div", { style: { overflow: "hidden" }, children: group.tokens.map((token) => renderTokenRow(token, true, false)) }) })) : (!shouldHideIndividualRows &&
1255
+ individualTokens.map((token) => renderTokenRow(token)))] }, group.symbol));
1256
+ };
1257
+ const isLoading = !staticOptions && swapBalance === null;
1258
+ const selectedAssetCount = activeSelectedTokens.length;
1259
+ const requiredUsdAmount = parseTokenAmount(requiredUsd);
1260
+ const selectedUsdAmount = activeSelectedTokens.reduce((sum, token) => {
1261
+ if (token.isUnified && token.sourceTokens?.length) {
1262
+ return sum.plus(token.sourceTokens.reduce((sourceSum, source) => {
1263
+ const value = parseTokenAmount(source.balanceInFiat) ?? new Decimal(0);
1264
+ return value.gte(MIN_FIAT_THRESHOLD)
1265
+ ? sourceSum.plus(value)
1266
+ : sourceSum;
1267
+ }, new Decimal(0)));
1268
+ }
1269
+ const value = parseTokenAmount(token.balanceInFiat) ?? new Decimal(0);
1270
+ return value.gte(MIN_FIAT_THRESHOLD) ? sum.plus(value) : sum;
1271
+ }, new Decimal(0));
1272
+ const selectionDeficitUsdAmount = requiredUsdAmount && selectedUsdAmount.lt(requiredUsdAmount)
1273
+ ? requiredUsdAmount.minus(selectedUsdAmount)
1274
+ : new Decimal(0);
1275
+ const shouldShowSelectionProgress = Boolean(isMulti &&
1276
+ requiredUsdAmount &&
1277
+ requiredUsdAmount.gt(0) &&
1278
+ selectionDeficitUsdAmount.gt(0));
1279
+ const selectionProgressPercent = shouldShowSelectionProgress && requiredUsdAmount
1280
+ ? Decimal.min(100, selectedUsdAmount.div(requiredUsdAmount).mul(100)).toNumber()
1281
+ : 0;
1282
+ const subtitle = isMulti
1283
+ ? `${selectedAssetCount} asset${selectedAssetCount === 1 ? "" : "s"} selected`
1284
+ : "";
1285
+ useEffect(() => {
1286
+ setPortalRoot(selectorRef.current?.closest("[data-nexus-widget-root]"));
1287
+ }, []);
1288
+ useEffect(() => {
1289
+ return () => {
1290
+ if (chainCloseTimerRef.current) {
1291
+ clearTimeout(chainCloseTimerRef.current);
1292
+ }
1293
+ };
1294
+ }, []);
1295
+ const openChainSelector = () => {
1296
+ if (chainCloseTimerRef.current) {
1297
+ clearTimeout(chainCloseTimerRef.current);
1298
+ chainCloseTimerRef.current = null;
1299
+ }
1300
+ setChainQuery("");
1301
+ setIsChainSelectorClosing(false);
1302
+ setShowChainSelector(true);
1303
+ };
1304
+ const closeChainSelector = () => {
1305
+ if (chainCloseTimerRef.current) {
1306
+ clearTimeout(chainCloseTimerRef.current);
1307
+ }
1308
+ setIsChainSelectorClosing(true);
1309
+ chainCloseTimerRef.current = setTimeout(() => {
1310
+ setShowChainSelector(false);
1311
+ setIsChainSelectorClosing(false);
1312
+ chainCloseTimerRef.current = null;
1313
+ }, CHAIN_SELECTOR_CLOSE_MS);
1314
+ };
1315
+ const chainOptions = useMemo(() => {
1316
+ const options = new Map();
1317
+ for (const chain of swapSupportedChains ?? []) {
1318
+ if (!sdkSwapSupportedChainIds &&
1319
+ !SWAP_CHAIN_DISPLAY_ORDER_SET.has(chain.id)) {
1320
+ continue;
1321
+ }
1322
+ if (!isSwapSupportedBySdkChainList(chain.id, swapSupportedChains)) {
1323
+ continue;
1324
+ }
1325
+ options.set(chain.id, {
1326
+ contractAddress: "",
1327
+ symbol: "",
1328
+ name: getShortChainName(chain.id, chain.name),
1329
+ decimals: 18,
1330
+ balance: "0",
1331
+ balanceInFiat: "$0.00",
1332
+ chainId: chain.id,
1333
+ chainName: getShortChainName(chain.id, chain.name),
1334
+ chainLogo: chain.logo,
1335
+ });
1336
+ }
1337
+ for (const token of allTokens) {
1338
+ if (!token.chainId ||
1339
+ (!sdkSwapSupportedChainIds &&
1340
+ !SWAP_CHAIN_DISPLAY_ORDER_SET.has(token.chainId)) ||
1341
+ !isSwapSupportedBySdkChainList(token.chainId, swapSupportedChains)) {
1342
+ continue;
1343
+ }
1344
+ if (!options.has(token.chainId)) {
1345
+ options.set(token.chainId, token);
1346
+ }
1347
+ }
1348
+ return Array.from(options.values()).sort(compareChainsBySwapDisplayOrder);
1349
+ }, [allTokens, sdkSwapSupportedChainIds, swapSupportedChains]);
1350
+ const selectedChainToken = selectedChainFilter === null
1351
+ ? undefined
1352
+ : chainOptions.find((token) => token.chainId === selectedChainFilter);
1353
+ const selectedChainLabel = selectedChainFilter === null
1354
+ ? "All chains"
1355
+ : selectedChainToken?.chainName || "Chain";
1356
+ const handleDone = () => {
1357
+ if (isMulti && onSelectionChange) {
1358
+ onSelectionChange(mergeTokenOptions(draftSelectedTokens, lockedSelectedTokens));
1359
+ }
1360
+ onDone?.();
1361
+ };
1362
+ return (_jsxs("div", { ref: selectorRef, style: {
1363
+ ...modalHeightTransitionStyle,
1364
+ boxSizing: "border-box",
1365
+ display: "flex",
1366
+ flex: "1 1 auto",
1367
+ flexDirection: "column",
1368
+ height: "100%",
1369
+ maxHeight: "100%",
1370
+ minHeight: 0,
1371
+ overflow: "hidden",
1372
+ padding: "12px",
1373
+ transition: modalHeightTransition,
1374
+ width: "100%",
1375
+ willChange: "height, max-height",
1376
+ }, children: [_jsx("div", { style: {
1377
+ width: "100%",
1378
+ display: "flex",
1379
+ justifyContent: "center",
1380
+ marginBottom: 10,
1381
+ }, children: _jsx("div", { style: {
1382
+ width: 32,
1383
+ height: 4,
1384
+ borderRadius: 2,
1385
+ backgroundColor: "#E8E8E7",
1386
+ } }) }), _jsxs("div", { style: {
1387
+ display: "flex",
1388
+ alignItems: "flex-start",
1389
+ gap: 12,
1390
+ marginBottom: 12,
1391
+ }, children: [_jsx("button", { onClick: onBack, style: {
1392
+ width: 32,
1393
+ height: 32,
1394
+ borderRadius: 8,
1395
+ border: "1px solid #E8E8E7",
1396
+ display: "flex",
1397
+ alignItems: "center",
1398
+ justifyContent: "center",
1399
+ backgroundColor: "#FFFFFE",
1400
+ cursor: "pointer",
1401
+ flexShrink: 0,
1402
+ }, children: _jsx(ChevronDown, { style: { width: 16, height: 16, transform: "rotate(90deg)" } }) }), _jsxs("div", { style: {
1403
+ display: "flex",
1404
+ flexDirection: "column",
1405
+ gap: "6px",
1406
+ minWidth: 0,
1407
+ flex: "1 1 auto",
1408
+ }, children: [_jsx("span", { style: {
1409
+ fontFamily: '"Geist", system-ui, sans-serif',
1410
+ fontSize: 18,
1411
+ fontWeight: 600,
1412
+ color: "#161615",
1413
+ }, children: title }), subtitle && (_jsx("span", { style: {
1414
+ fontFamily: '"Geist", system-ui, sans-serif',
1415
+ fontSize: 13,
1416
+ color: "#848483",
1417
+ }, children: subtitle }))] }), isMulti &&
1418
+ selectedAssetCount > 0 &&
1419
+ (onClearSelection || onSelectionChange) && (_jsx("button", { onClick: handleClearSelection, style: {
1420
+ backgroundColor: "transparent",
1421
+ border: "none",
1422
+ color: brand,
1423
+ cursor: "pointer",
1424
+ flexShrink: 0,
1425
+ fontFamily: '"Geist", system-ui, sans-serif',
1426
+ fontSize: 13,
1427
+ fontWeight: 500,
1428
+ lineHeight: "20px",
1429
+ padding: "2px 0",
1430
+ }, children: "Deselect all" }))] }), _jsx("div", { style: { paddingBottom: 6 }, children: _jsxs("div", { style: {
1431
+ display: "flex",
1432
+ alignItems: "center",
1433
+ height: 42,
1434
+ gap: 8,
1435
+ borderRadius: 12,
1436
+ border: `1px solid ${isSearchFocused ? "#A8C9FF" : "#E8E8E7"}`,
1437
+ boxShadow: isSearchFocused
1438
+ ? "0 0 0 1px rgba(0,107,244,0.16)"
1439
+ : "none",
1440
+ padding: "0 8px 0 16px",
1441
+ backgroundColor: "#F0F0EF",
1442
+ }, children: [_jsx(Search, { style: { width: 20, height: 20, color: "#848483", flexShrink: 0 } }), _jsx("input", { onBlur: () => setIsSearchFocused(false), onChange: (e) => setQuery(e.target.value), onFocus: () => setIsSearchFocused(true), placeholder: "Search token, chain or address", style: {
1443
+ flex: 1,
1444
+ backgroundColor: "transparent",
1445
+ border: "none",
1446
+ outline: "none",
1447
+ fontFamily: '"Geist", system-ui, sans-serif',
1448
+ fontSize: 14,
1449
+ color: "#161615",
1450
+ minWidth: 0,
1451
+ }, value: query }), query && (_jsx("button", { onClick: () => setQuery(""), style: {
1452
+ background: "none",
1453
+ border: "none",
1454
+ cursor: "pointer",
1455
+ padding: 0,
1456
+ }, children: _jsx(X, { style: { width: 16, height: 16, color: "#848483" } }) })), _jsxs("button", { onClick: openChainSelector, style: {
1457
+ display: "flex",
1458
+ alignItems: "center",
1459
+ gap: 5,
1460
+ padding: "4px 8px 4px 5px",
1461
+ borderRadius: 999,
1462
+ backgroundColor: "#FFFFFE",
1463
+ border: "1px solid #E8E8E7",
1464
+ cursor: "pointer",
1465
+ height: 38,
1466
+ flexShrink: 0,
1467
+ boxShadow: "0px 1px 2px rgba(0,0,0,0.05)",
1468
+ }, children: [selectedChainFilter === null ? (_jsx(Globe, { style: {
1469
+ width: 16,
1470
+ height: 16,
1471
+ color: "#161615",
1472
+ flexShrink: 0,
1473
+ } })) : (_jsx("img", { alt: selectedChainLabel, src: selectedChainToken?.chainLogo, style: {
1474
+ width: 18,
1475
+ height: 18,
1476
+ borderRadius: "999px",
1477
+ objectFit: "cover",
1478
+ flexShrink: 0,
1479
+ } })), _jsx("span", { style: {
1480
+ color: "#161615",
1481
+ fontFamily: '"Geist", system-ui, sans-serif',
1482
+ fontSize: "14px",
1483
+ fontWeight: 500,
1484
+ lineHeight: "18px",
1485
+ maxWidth: "86px",
1486
+ overflow: "hidden",
1487
+ textOverflow: "ellipsis",
1488
+ whiteSpace: "nowrap",
1489
+ }, children: selectedChainLabel }), _jsx(ChevronDown, { style: { width: 14, height: 14, color: "#848483" } })] })] }) }), _jsx("div", { style: {
1490
+ display: "flex",
1491
+ gap: 0,
1492
+ backgroundColor: "#F0F0EF",
1493
+ borderRadius: 8,
1494
+ padding: 4,
1495
+ marginBottom: 6,
1496
+ }, children: visibleFilterTabs.map((tab) => (_jsx("button", { onClick: () => handleFilterTabClick(tab.key), disabled: tab.key === "custom" && autoSelectFilterTabs, style: {
1497
+ flex: 1,
1498
+ padding: "6px 0",
1499
+ backgroundColor: activeTab === tab.key ? "#FFFFFE" : "transparent",
1500
+ border: "none",
1501
+ borderRadius: 6,
1502
+ cursor: "pointer",
1503
+ fontFamily: '"Geist", system-ui, sans-serif',
1504
+ fontSize: 13,
1505
+ fontWeight: 500,
1506
+ color: activeTab === tab.key ? "#161615" : "#848483",
1507
+ boxShadow: activeTab === tab.key ? "0px 1px 2px rgba(0,0,0,0.05)" : "none",
1508
+ transition: "all 0.15s",
1509
+ }, children: autoSelectFilterTabs && tab.key === "all" ? "Any" : tab.label }, tab.key))) }), _jsx("div", { ref: listRef, style: {
1510
+ flex: "1 1 auto",
1511
+ minHeight: 0,
1512
+ overflowY: "auto",
1513
+ paddingBottom: 6,
1514
+ }, children: isLoading ? (_jsxs("div", { style: {
1515
+ display: "flex",
1516
+ flexDirection: "column",
1517
+ alignItems: "center",
1518
+ justifyContent: "center",
1519
+ padding: "40px 0",
1520
+ gap: 12,
1521
+ }, children: [_jsx(Loader2, { style: {
1522
+ width: 20,
1523
+ height: 20,
1524
+ color: "#848483",
1525
+ animation: "spin 1s linear infinite",
1526
+ } }), _jsx("p", { style: {
1527
+ fontFamily: '"Geist", system-ui, sans-serif',
1528
+ fontSize: 14,
1529
+ color: "#848483",
1530
+ }, children: "Loading assets\u2026" })] })) : aboveMin.length === 0 && belowMin.length === 0 ? (_jsx("p", { style: {
1531
+ fontFamily: '"Geist", system-ui, sans-serif',
1532
+ fontSize: 14,
1533
+ color: "#848483",
1534
+ textAlign: "center",
1535
+ padding: "32px 0",
1536
+ }, children: "No tokens found" })) : (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [groupedFiltered.length > 0 && (_jsx("div", { style: {
1537
+ border: "1px solid #E8E8E7",
1538
+ borderRadius: 14,
1539
+ overflowX: "hidden",
1540
+ overflowY: "visible",
1541
+ backgroundColor: "#FFFFFE",
1542
+ }, children: groupedFiltered.map((group) => group.tokens.length === 1
1543
+ ? renderTokenRow(group.tokens[0])
1544
+ : renderGroupRow(group)) })), belowMin.length > 0 && (_jsxs("div", { style: {
1545
+ backgroundColor: "#FFFFFE",
1546
+ border: "1px solid #E8E8E7",
1547
+ borderRadius: 12,
1548
+ overflow: "hidden",
1549
+ }, children: [_jsxs("button", { onClick: () => setShowBelowMin((v) => !v), style: {
1550
+ width: "100%",
1551
+ display: "flex",
1552
+ alignItems: "center",
1553
+ justifyContent: "space-between",
1554
+ padding: "11px 12px",
1555
+ backgroundColor: "transparent",
1556
+ border: "none",
1557
+ cursor: "pointer",
1558
+ boxSizing: "border-box",
1559
+ }, children: [_jsxs("div", { style: {
1560
+ display: "flex",
1561
+ alignItems: "center",
1562
+ gap: 10,
1563
+ minWidth: 0,
1564
+ }, children: [_jsx("span", { style: {
1565
+ alignItems: "center",
1566
+ backgroundColor: "#FFF0D6",
1567
+ borderRadius: "999px",
1568
+ display: "flex",
1569
+ flexShrink: 0,
1570
+ height: 22,
1571
+ justifyContent: "center",
1572
+ width: 22,
1573
+ }, children: _jsx(Info, { style: { width: 12, height: 12, color: "#D98A1C" } }) }), _jsxs("div", { style: {
1574
+ display: "flex",
1575
+ flexDirection: "column",
1576
+ alignItems: "flex-start",
1577
+ minWidth: 0,
1578
+ }, children: [_jsx("span", { style: {
1579
+ fontFamily: '"Geist", system-ui, sans-serif',
1580
+ fontWeight: 600,
1581
+ fontSize: 13,
1582
+ color: "#161615",
1583
+ lineHeight: "20px",
1584
+ }, children: "Tokens below minimum" }), _jsx("span", { style: {
1585
+ fontFamily: '"Geist", system-ui, sans-serif',
1586
+ fontSize: 12,
1587
+ color: "#848483",
1588
+ lineHeight: "18px",
1589
+ textAlign: "left",
1590
+ }, children: showBelowMin
1591
+ ? "Tokens under $1 are unavailable for swaps"
1592
+ : "Hidden to prevent failed swaps" })] })] }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [belowMin.slice(0, 3).map((t, i) => t.logo ? (_jsx("img", { alt: "", src: t.logo, style: {
1593
+ width: 18,
1594
+ height: 18,
1595
+ borderRadius: "999px",
1596
+ objectFit: "cover",
1597
+ marginLeft: i > 0 ? -6 : 0,
1598
+ border: "1.5px solid #fff",
1599
+ } }, `bm-${t.contractAddress}-${t.chainId}`)) : (_jsx("div", { style: {
1600
+ width: 18,
1601
+ height: 18,
1602
+ borderRadius: "999px",
1603
+ backgroundColor: "#E8E8E7",
1604
+ marginLeft: i > 0 ? -6 : 0,
1605
+ border: "1.5px solid #fff",
1606
+ } }, `bm-${t.contractAddress}-${t.chainId}`))), belowMin.length > 3 && (_jsxs("div", { style: {
1607
+ width: 18,
1608
+ height: 18,
1609
+ borderRadius: "999px",
1610
+ backgroundColor: "#161615",
1611
+ display: "flex",
1612
+ alignItems: "center",
1613
+ justifyContent: "center",
1614
+ fontSize: 9,
1615
+ fontWeight: 700,
1616
+ color: "#fff",
1617
+ marginLeft: -6,
1618
+ border: "1.5px solid #fff",
1619
+ }, children: ["+", belowMin.length - 3] }))] }), showBelowMin ? (_jsx(ChevronUp, { style: { width: 18, height: 18, color: "#848483" } })) : (_jsx(ChevronDown, { style: { width: 18, height: 18, color: "#848483" } }))] })] }), _jsx("div", { "aria-hidden": !showBelowMin, style: {
1620
+ borderTop: showBelowMin
1621
+ ? "1px solid #F0F0EF"
1622
+ : "0px solid transparent",
1623
+ display: "grid",
1624
+ gridTemplateRows: showBelowMin ? "1fr" : "0fr",
1625
+ opacity: showBelowMin ? 1 : 0,
1626
+ overflow: "hidden",
1627
+ transition: "grid-template-rows 240ms ease, opacity 180ms ease, border-top-width 240ms ease",
1628
+ }, children: _jsx("div", { style: { minHeight: 0, overflow: "hidden" }, children: belowMin.map((token, index) => (_jsxs("div", { style: {
1629
+ display: "flex",
1630
+ alignItems: "center",
1631
+ justifyContent: "space-between",
1632
+ borderTop: index === 0 ? "none" : "1px solid #F0F0EF",
1633
+ opacity: 0.58,
1634
+ padding: "8px 12px",
1635
+ }, children: [_jsxs("div", { style: {
1636
+ display: "flex",
1637
+ alignItems: "center",
1638
+ gap: 9,
1639
+ minWidth: 0,
1640
+ }, children: [_jsxs("div", { style: {
1641
+ position: "relative",
1642
+ width: 22,
1643
+ height: 22,
1644
+ flexShrink: 0,
1645
+ }, children: [token.logo ? (_jsx("img", { alt: token.symbol, src: token.logo, style: {
1646
+ filter: "grayscale(0.2)",
1647
+ width: 22,
1648
+ height: 22,
1649
+ borderRadius: "999px",
1650
+ objectFit: "cover",
1651
+ } })) : (_jsx("div", { style: {
1652
+ width: 22,
1653
+ height: 22,
1654
+ borderRadius: "999px",
1655
+ backgroundColor: "#C8C8C7",
1656
+ display: "flex",
1657
+ alignItems: "center",
1658
+ justifyContent: "center",
1659
+ color: "#fff",
1660
+ fontSize: 9,
1661
+ fontWeight: 700,
1662
+ }, children: token.symbol.slice(0, 2) })), token.chainLogo && (_jsx("img", { alt: "", src: token.chainLogo, style: {
1663
+ border: "1.5px solid #FFFFFE",
1664
+ borderRadius: "999px",
1665
+ bottom: -2,
1666
+ filter: "grayscale(0.2)",
1667
+ height: 10,
1668
+ objectFit: "cover",
1669
+ position: "absolute",
1670
+ right: -2,
1671
+ width: 10,
1672
+ } }))] }), _jsxs("span", { style: {
1673
+ fontFamily: '"Geist", system-ui, sans-serif',
1674
+ fontWeight: 500,
1675
+ fontSize: 12,
1676
+ color: "#848483",
1677
+ lineHeight: "18px",
1678
+ minWidth: 0,
1679
+ overflow: "hidden",
1680
+ textOverflow: "ellipsis",
1681
+ whiteSpace: "nowrap",
1682
+ }, children: [token.symbol, " on", " ", token.chainName || "Unknown chain"] })] }), _jsx("span", { style: {
1683
+ fontFamily: '"Geist", system-ui, sans-serif',
1684
+ fontSize: 12,
1685
+ color: "#848483",
1686
+ fontWeight: 500,
1687
+ lineHeight: "18px",
1688
+ flexShrink: 0,
1689
+ marginLeft: 12,
1690
+ }, children: token.balanceInFiat })] }, `${token.contractAddress}-${token.chainId}`))) }) })] }))] })) }), isMulti && (_jsxs("div", { style: { flexShrink: 0, paddingBottom: 6, marginTop: "auto" }, children: [shouldShowSelectionProgress && requiredUsdAmount && (_jsxs("div", { style: {
1691
+ borderTop: "1px solid #E8E8E7",
1692
+ boxSizing: "border-box",
1693
+ marginBottom: 12,
1694
+ paddingTop: 12,
1695
+ }, children: [_jsxs("div", { style: {
1696
+ alignItems: "baseline",
1697
+ display: "flex",
1698
+ justifyContent: "space-between",
1699
+ marginBottom: 8,
1700
+ }, children: [_jsx("span", { style: {
1701
+ color: "#848483",
1702
+ fontFamily: '"Geist", system-ui, sans-serif',
1703
+ fontSize: 13,
1704
+ lineHeight: "20px",
1705
+ }, children: "Required" }), _jsxs("span", { style: {
1706
+ color: "#848483",
1707
+ fontFamily: '"Geist", system-ui, sans-serif',
1708
+ fontSize: 13,
1709
+ lineHeight: "20px",
1710
+ }, children: [_jsx("strong", { style: { color: "#161615", fontWeight: 600 }, children: formatUsdBalanceLabel(selectionDeficitUsdAmount) }), " ", "more"] })] }), _jsx("div", { style: {
1711
+ backgroundColor: "#F0F0EF",
1712
+ borderRadius: "999px",
1713
+ height: 6,
1714
+ overflow: "hidden",
1715
+ width: "100%",
1716
+ }, children: _jsx("div", { style: {
1717
+ backgroundColor: brand,
1718
+ borderRadius: "999px",
1719
+ height: "100%",
1720
+ transition: "width 240ms ease",
1721
+ width: `${selectionProgressPercent}%`,
1722
+ } }) })] })), !shouldShowSelectionProgress && (_jsx("button", { onClick: handleDone, style: {
1723
+ width: "100%",
1724
+ height: 48,
1725
+ display: "flex",
1726
+ alignItems: "center",
1727
+ justifyContent: "center",
1728
+ backgroundColor: "#1F1F1F",
1729
+ color: "#FFFFFE",
1730
+ border: "none",
1731
+ borderRadius: 14,
1732
+ cursor: "pointer",
1733
+ fontFamily: '"Geist", system-ui, sans-serif',
1734
+ fontSize: 16,
1735
+ fontWeight: 600,
1736
+ boxShadow: "0px 1px 4px 0px #5555550D",
1737
+ }, children: "Done" }))] })), showChainSelector &&
1738
+ (() => {
1739
+ const chainModal = (_jsxs("div", { style: {
1740
+ bottom: 0,
1741
+ display: "flex",
1742
+ flexDirection: "column",
1743
+ justifyContent: "flex-end",
1744
+ left: 0,
1745
+ pointerEvents: "none",
1746
+ position: "absolute",
1747
+ right: 0,
1748
+ top: 0,
1749
+ zIndex: 50,
1750
+ }, children: [_jsx("div", { onClick: closeChainSelector, style: {
1751
+ backgroundColor: "rgba(0,0,0,0.22)",
1752
+ bottom: 0,
1753
+ left: 0,
1754
+ pointerEvents: "auto",
1755
+ position: "absolute",
1756
+ right: 0,
1757
+ top: 0,
1758
+ opacity: isChainSelectorClosing ? 0 : 1,
1759
+ transition: `opacity ${CHAIN_SELECTOR_CLOSE_MS}ms ease`,
1760
+ } }), _jsxs("div", { className: isChainSelectorClosing
1761
+ ? undefined
1762
+ : "animate-in slide-in-from-bottom-full duration-300", "data-nexus-widget-sheet": true, style: {
1763
+ ...modalHeightTransitionStyle,
1764
+ backgroundColor: "#FFFFFE",
1765
+ borderRadius: "24px 24px 0 0",
1766
+ boxShadow: "0 -4px 16px rgba(0,0,0,0.08)",
1767
+ boxSizing: "border-box",
1768
+ display: "flex",
1769
+ flexDirection: "column",
1770
+ height: "90%",
1771
+ maxHeight: "90%",
1772
+ overflow: "hidden",
1773
+ padding: "12px",
1774
+ pointerEvents: "auto",
1775
+ position: "relative",
1776
+ transform: isChainSelectorClosing
1777
+ ? "translateY(100%)"
1778
+ : "translateY(0)",
1779
+ transition: `${modalHeightTransition}, transform ${CHAIN_SELECTOR_CLOSE_MS}ms ease, opacity ${CHAIN_SELECTOR_CLOSE_MS}ms ease`,
1780
+ opacity: isChainSelectorClosing ? 0 : 1,
1781
+ willChange: "height, max-height, transform, opacity",
1782
+ width: "100%",
1783
+ }, children: [_jsx("div", { style: {
1784
+ display: "flex",
1785
+ justifyContent: "center",
1786
+ marginBottom: 8,
1787
+ width: "100%",
1788
+ }, children: _jsx("div", { style: {
1789
+ backgroundColor: "#D8D8D6",
1790
+ borderRadius: "999px",
1791
+ height: 4,
1792
+ width: 32,
1793
+ } }) }), _jsxs("div", { style: {
1794
+ alignItems: "center",
1795
+ display: "flex",
1796
+ gap: 10,
1797
+ marginBottom: 10,
1798
+ }, children: [_jsx("button", { onClick: closeChainSelector, style: {
1799
+ width: 30,
1800
+ height: 30,
1801
+ borderRadius: 8,
1802
+ border: "1px solid #E8E8E7",
1803
+ display: "flex",
1804
+ alignItems: "center",
1805
+ justifyContent: "center",
1806
+ backgroundColor: "#FFFFFE",
1807
+ cursor: "pointer",
1808
+ flexShrink: 0,
1809
+ }, children: _jsx(ChevronDown, { style: {
1810
+ width: 15,
1811
+ height: 15,
1812
+ transform: "rotate(90deg)",
1813
+ } }) }), _jsx("span", { style: {
1814
+ color: "#161615",
1815
+ fontFamily: '"Geist", system-ui, sans-serif',
1816
+ fontSize: 17,
1817
+ fontWeight: 600,
1818
+ }, children: "Select chain" })] }), _jsx("div", { style: { paddingBottom: 10 }, children: _jsxs("div", { style: {
1819
+ display: "flex",
1820
+ alignItems: "center",
1821
+ height: 38,
1822
+ gap: 8,
1823
+ borderRadius: 11,
1824
+ border: `1px solid ${isChainSearchFocused ? "#A8C9FF" : "#E8E8E7"}`,
1825
+ padding: "0 12px",
1826
+ backgroundColor: "#FFFFFE",
1827
+ boxShadow: isChainSearchFocused
1828
+ ? "0 0 0 1px rgba(0,107,244,0.16)"
1829
+ : "none",
1830
+ }, children: [_jsx(Search, { style: {
1831
+ width: 18,
1832
+ height: 18,
1833
+ color: "#848483",
1834
+ flexShrink: 0,
1835
+ } }), _jsx("input", { onBlur: () => setIsChainSearchFocused(false), onChange: (e) => setChainQuery(e.target.value), onFocus: () => setIsChainSearchFocused(true), placeholder: "Search chains", style: {
1836
+ flex: 1,
1837
+ backgroundColor: "transparent",
1838
+ border: "none",
1839
+ outline: "none",
1840
+ fontFamily: '"Geist", system-ui, sans-serif',
1841
+ fontSize: 13,
1842
+ color: "#161615",
1843
+ }, value: chainQuery })] }) }), _jsx("div", { style: {
1844
+ flex: "1 1 auto",
1845
+ marginBottom: 10,
1846
+ minHeight: 0,
1847
+ overflowY: "auto",
1848
+ }, children: _jsxs("div", { style: {
1849
+ border: "1px solid #E8E8E7",
1850
+ borderRadius: 12,
1851
+ overflow: "hidden",
1852
+ backgroundColor: "#FFFFFE",
1853
+ }, children: [_jsxs("button", { onClick: () => {
1854
+ setSelectedChainFilter(null);
1855
+ closeChainSelector();
1856
+ }, style: {
1857
+ width: "100%",
1858
+ display: "flex",
1859
+ alignItems: "center",
1860
+ padding: "8px 14px",
1861
+ backgroundColor: "transparent",
1862
+ border: "none",
1863
+ borderBottom: "1px solid #F0F0EF",
1864
+ cursor: "pointer",
1865
+ boxSizing: "border-box",
1866
+ }, children: [_jsx(RadioDot, { selected: selectedChainFilter === null }), _jsx(Globe, { style: {
1867
+ marginLeft: 10,
1868
+ width: 28,
1869
+ height: 28,
1870
+ color: "#161615",
1871
+ flexShrink: 0,
1872
+ } }), _jsx("span", { style: {
1873
+ fontFamily: '"Geist", system-ui, sans-serif',
1874
+ fontSize: 14,
1875
+ fontWeight: 500,
1876
+ marginLeft: 10,
1877
+ color: "#161615",
1878
+ }, children: "All Chains" })] }), chainOptions
1879
+ .filter((t) => (t.chainName || "")
1880
+ .toLowerCase()
1881
+ .includes(chainQuery.toLowerCase()))
1882
+ .map((t) => (_jsxs("button", { onClick: () => {
1883
+ setSelectedChainFilter(t.chainId);
1884
+ closeChainSelector();
1885
+ }, style: {
1886
+ width: "100%",
1887
+ display: "flex",
1888
+ alignItems: "center",
1889
+ padding: "8px 14px",
1890
+ backgroundColor: "transparent",
1891
+ border: "none",
1892
+ borderBottom: "1px solid #F0F0EF",
1893
+ cursor: "pointer",
1894
+ boxSizing: "border-box",
1895
+ }, children: [_jsx(RadioDot, { selected: selectedChainFilter === t.chainId }), _jsx("img", { alt: t.chainName, src: t.chainLogo, style: {
1896
+ marginLeft: 10,
1897
+ width: 28,
1898
+ height: 28,
1899
+ borderRadius: "999px",
1900
+ objectFit: "cover",
1901
+ } }), _jsx("span", { style: {
1902
+ fontFamily: '"Geist", system-ui, sans-serif',
1903
+ fontSize: 14,
1904
+ fontWeight: 500,
1905
+ marginLeft: 10,
1906
+ color: "#161615",
1907
+ }, children: t.chainName })] }, `chain-${t.chainId}`)))] }) })] })] }));
1908
+ return portalRoot ? createPortal(chainModal, portalRoot) : chainModal;
1909
+ })()] }));
1910
+ }