@xswap-link/sdk 0.15.0 → 0.15.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.
- package/CHANGELOG.md +6 -0
- package/dist/index.global.js +86 -80
- package/dist/index.js +1117 -976
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +809 -668
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/postcss.config.js +2 -0
- package/src/assets/icons/ChevronRightThinIcon.tsx +23 -0
- package/src/assets/icons/InfoThinIcon.tsx +33 -0
- package/src/assets/icons/RedirectThinIcon.tsx +15 -0
- package/src/assets/icons/index.ts +3 -0
- package/src/components/Swap/Header/index.tsx +3 -1
- package/src/components/Swap/HistoryView/ActivityCard/index.tsx +2 -2
- package/src/components/Swap/ReorderButton/ReorderButton.tsx +4 -1
- package/src/components/Swap/SettingsView/Delivery/index.tsx +3 -3
- package/src/components/Swap/SettingsView/InfiniteApproval/index.tsx +3 -3
- package/src/components/Swap/SettingsView/Slippage/index.tsx +5 -3
- package/src/components/Swap/SwapView/ConfirmationView/TokenTiles/index.tsx +5 -2
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/Header/index.tsx +1 -1
- package/src/components/Swap/SwapView/ConfirmationView/TxResult/index.tsx +19 -18
- package/src/components/Swap/SwapView/FeesPanel/Fees/index.tsx +1 -1
- package/src/components/Swap/SwapView/FeesPanel/index.tsx +3 -1
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +9 -0
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/ChainPicker/index.tsx +1 -1
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/NetworkFilter/index.tsx +52 -42
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +1 -1
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +5 -5
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +24 -2
- package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +14 -3
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +14 -2
- package/src/components/Swap/SwapView/WalletPicker/index.tsx +1 -1
- package/src/components/ToggleButton/index.tsx +5 -3
- package/src/components/TxModal/index.tsx +2 -6
- package/src/components/global.css +85 -0
- package/src/config/fonts.ts +15 -2
- package/src/context/TxUIWrapper.tsx +0 -1
- package/tailwind.config.js +3 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xswap-link/sdk",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"description": "JavaScript SDK for XSwap platform",
|
|
5
5
|
"homepage": "https://github.com/xswap-link/xswap-sdk",
|
|
6
6
|
"repository": {
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"@types/react-dom": "^18.2.0",
|
|
82
82
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
|
83
83
|
"@typescript-eslint/parser": "^7.1.0",
|
|
84
|
+
"autoprefixer": "^10.5.4",
|
|
84
85
|
"babel-jest": "^29.7.0",
|
|
85
86
|
"cross-fetch": "^4.1.0",
|
|
86
87
|
"eslint": "^8.57.0",
|
package/postcss.config.js
CHANGED
|
@@ -4,6 +4,8 @@ module.exports = {
|
|
|
4
4
|
require("postcss-import"),
|
|
5
5
|
// Resolves TailwindCSS
|
|
6
6
|
require("tailwindcss")(),
|
|
7
|
+
// Adds vendor prefixes (e.g. -webkit-backdrop-filter for Safari < 18)
|
|
8
|
+
require("autoprefixer"),
|
|
7
9
|
// Removes comments from CSS and redundant whitespaces
|
|
8
10
|
require("postcss-minify"),
|
|
9
11
|
],
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Glyph-tight box (no 16/24px padding frame) so flex gaps around it match
|
|
2
|
+
// the design's spacing exactly. Geometry is Figma's "Vector 8262" chevron
|
|
3
|
+
// rotated to point right.
|
|
4
|
+
export const ChevronRightThinIcon = ({
|
|
5
|
+
className,
|
|
6
|
+
}: {
|
|
7
|
+
className?: string;
|
|
8
|
+
}) => {
|
|
9
|
+
return (
|
|
10
|
+
<svg
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
width="4"
|
|
13
|
+
height="7"
|
|
14
|
+
viewBox="0 0 3.58671 7"
|
|
15
|
+
fill="none"
|
|
16
|
+
stroke="currentColor"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
className={className}
|
|
19
|
+
>
|
|
20
|
+
<path d="M0 6.5H0.500926L2.79382 4.20711C3.18434 3.81658 3.18434 3.18342 2.79382 2.79289L0.500926 0.5H0" />
|
|
21
|
+
</svg>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
|
|
3
|
+
const InfoThinIcon = forwardRef<SVGSVGElement, { className?: string }>(
|
|
4
|
+
({ className }, ref) => {
|
|
5
|
+
return (
|
|
6
|
+
<svg
|
|
7
|
+
ref={ref}
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
width="16"
|
|
10
|
+
height="16"
|
|
11
|
+
viewBox="0 0 16 16"
|
|
12
|
+
fill="none"
|
|
13
|
+
className={className || ""}
|
|
14
|
+
>
|
|
15
|
+
<circle cx="8" cy="8" r="8" fill="currentColor" fillOpacity="0.05" />
|
|
16
|
+
<circle
|
|
17
|
+
cx="8"
|
|
18
|
+
cy="8"
|
|
19
|
+
r="7.5"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeOpacity="0.05"
|
|
22
|
+
/>
|
|
23
|
+
<g opacity="0.6">
|
|
24
|
+
<path d="M8 7.5V10.5" stroke="currentColor" />
|
|
25
|
+
<path d="M8 5.5V6.5" stroke="currentColor" />
|
|
26
|
+
</g>
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
InfoThinIcon.displayName = "InfoThinIcon";
|
|
33
|
+
export { InfoThinIcon };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const RedirectThinIcon = ({ className }: { className?: string }) => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="12"
|
|
6
|
+
height="12"
|
|
7
|
+
viewBox="0 0 12 12"
|
|
8
|
+
fill="currentColor"
|
|
9
|
+
className={className}
|
|
10
|
+
>
|
|
11
|
+
<path d="M10.9226 10.9226H1.07744V1.07744H5.45455L4.3771 0H0V12H12V7.6229L10.9226 6.54545V10.9226Z" />
|
|
12
|
+
<path d="M7.07071 0L8.14815 1.07744H10.1549L9.15825 2.07407L9.92593 2.84175L10.9226 1.84512V3.83838L12 4.91582V0H7.07071Z" />
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -17,6 +17,7 @@ export * from "./CheckIcon";
|
|
|
17
17
|
export * from "./ChevronDownIcon";
|
|
18
18
|
export * from "./ChevronDownThinIcon";
|
|
19
19
|
export * from "./ChevronDownWideThinIcon";
|
|
20
|
+
export * from "./ChevronRightThinIcon";
|
|
20
21
|
export * from "./ChevronsUpDownThinIcon";
|
|
21
22
|
export * from "./ChevronUpIcon";
|
|
22
23
|
export * from "./CircularProgressIcon";
|
|
@@ -34,12 +35,14 @@ export * from "./HistoryGradientBgIcon";
|
|
|
34
35
|
export * from "./HistoryIcon";
|
|
35
36
|
export * from "./HourGlassIcon";
|
|
36
37
|
export * from "./InfoIcon";
|
|
38
|
+
export * from "./InfoThinIcon";
|
|
37
39
|
export * from "./LinkExternalIcon";
|
|
38
40
|
export * from "./LinkExternalThinIcon";
|
|
39
41
|
export * from "./LoadingRingIcon";
|
|
40
42
|
export * from "./PercentageIcon";
|
|
41
43
|
export * from "./ProgressRingThinIcon";
|
|
42
44
|
export * from "./RankIcon";
|
|
45
|
+
export * from "./RedirectThinIcon";
|
|
43
46
|
export * from "./SearchIcon";
|
|
44
47
|
export * from "./SearchThinIcon";
|
|
45
48
|
export * from "./SettingsGradientBgIcon";
|
|
@@ -16,7 +16,9 @@ export const Header = ({ onClose }: Props) => {
|
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
18
|
<div className="flex justify-between items-center">
|
|
19
|
-
<div className="text-2xl leading-8 tracking-[0.01em]">
|
|
19
|
+
<div className="text-2xl font-medium leading-8 tracking-[0.01em]">
|
|
20
|
+
{title}
|
|
21
|
+
</div>
|
|
20
22
|
<Controls onClose={onClose} />
|
|
21
23
|
</div>
|
|
22
24
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
ChevronRightThinIcon,
|
|
3
3
|
LinkExternalThinIcon,
|
|
4
4
|
LoadingRingIcon,
|
|
5
5
|
} from "@src/assets/icons";
|
|
@@ -170,7 +170,7 @@ export const ActivityCard = ({ transaction }: Props) => {
|
|
|
170
170
|
<span>{srcToken?.symbol || "?"}</span>
|
|
171
171
|
{dstToken && (
|
|
172
172
|
<>
|
|
173
|
-
<
|
|
173
|
+
<ChevronRightThinIcon className="shrink-0 opacity-50" />
|
|
174
174
|
<span className="truncate">{dstToken.symbol}</span>
|
|
175
175
|
</>
|
|
176
176
|
)}
|
|
@@ -23,10 +23,13 @@ export const ReorderButton: FC = () => {
|
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<div className="h-0 w-full relative z-[1]">
|
|
26
|
+
{/* Opaque bg on purpose: Chromium ignores backdrop-filter nested
|
|
27
|
+
inside the widget's own backdrop-blur, so a translucent bg would
|
|
28
|
+
let the panel edges show through the button. */}
|
|
26
29
|
<button
|
|
27
30
|
onClick={onReorderClick}
|
|
28
31
|
aria-label="Switch target token with the source token"
|
|
29
|
-
className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-[24px] bg-t_bg_secondary bg-
|
|
32
|
+
className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-[24px] bg-t_bg_secondary bg-gradient-to-r from-t_bg_tertiary/5 to-t_bg_tertiary/5 hover:from-t_bg_tertiary/10 hover:to-t_bg_tertiary/10 transition-colors"
|
|
30
33
|
>
|
|
31
34
|
<div className="flex text-t_text_primary opacity-50">
|
|
32
35
|
<ChevronsUpDownThinIcon />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InfoThinIcon } from "@src/assets/icons";
|
|
2
2
|
import { ToggleButton, Tooltip } from "@src/components";
|
|
3
3
|
import { useSwapContext } from "@src/context";
|
|
4
4
|
import { useRef } from "react";
|
|
@@ -26,9 +26,9 @@ export const Delivery = () => {
|
|
|
26
26
|
triggerRef={tooltipTriggerRef}
|
|
27
27
|
id="express-delivery-tooltip"
|
|
28
28
|
/>
|
|
29
|
-
<
|
|
29
|
+
<InfoThinIcon
|
|
30
30
|
ref={tooltipTriggerRef}
|
|
31
|
-
className="cursor-help
|
|
31
|
+
className="cursor-help text-t_text_primary w-4 h-4 shrink-0"
|
|
32
32
|
/>
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InfoThinIcon } from "@src/assets/icons";
|
|
2
2
|
import { ToggleButton } from "@src/components/ToggleButton";
|
|
3
3
|
import { Tooltip } from "@src/components/Tooltip";
|
|
4
4
|
import { useSwapContext } from "@src/context";
|
|
@@ -27,9 +27,9 @@ export const InfiniteApproval = () => {
|
|
|
27
27
|
triggerRef={tooltipTriggerRef}
|
|
28
28
|
id="infinite-approval-tooltip"
|
|
29
29
|
/>
|
|
30
|
-
<
|
|
30
|
+
<InfoThinIcon
|
|
31
31
|
ref={tooltipTriggerRef}
|
|
32
|
-
className="cursor-help
|
|
32
|
+
className="cursor-help text-t_text_primary w-4 h-4 shrink-0"
|
|
33
33
|
/>
|
|
34
34
|
</div>
|
|
35
35
|
</div>
|
|
@@ -31,7 +31,7 @@ export const Slippage = () => {
|
|
|
31
31
|
<div className="flex flex-col gap-4">
|
|
32
32
|
<div className="flex items-center gap-2">
|
|
33
33
|
<h2 className="text-sm leading-6 tracking-[0.01em] text-t_text_primary">
|
|
34
|
-
|
|
34
|
+
Slippage
|
|
35
35
|
</h2>
|
|
36
36
|
<div className="w-0.5 h-0.5 bg-t_text_primary opacity-25 shrink-0" />
|
|
37
37
|
<p className="text-sm leading-6 tracking-[0.01em] text-t_text_primary text-opacity-50">
|
|
@@ -77,11 +77,13 @@ export const Slippage = () => {
|
|
|
77
77
|
</div>
|
|
78
78
|
</div>
|
|
79
79
|
<div className="flex flex-col gap-3">
|
|
80
|
-
<p className="text-[10px]
|
|
80
|
+
<p className="text-[10px] leading-4 tracking-[0.015em] text-t_text_primary text-opacity-30">
|
|
81
81
|
NOTE.
|
|
82
82
|
</p>
|
|
83
83
|
<p className="text-sm leading-[18px] tracking-[0.01em] text-t_text_primary text-opacity-40">
|
|
84
|
-
{
|
|
84
|
+
{/* The design sets the leading term in Medium, the rest Regular. */}
|
|
85
|
+
<span className="font-medium">Slippage</span>
|
|
86
|
+
{` defines how much a price can change before your
|
|
85
87
|
transfer falls back to USDC. If the price moves more than your
|
|
86
88
|
tolerance allows, you'll receive USDC on your destination chain.`}
|
|
87
89
|
</p>
|
|
@@ -57,9 +57,12 @@ export const TokenTiles = () => {
|
|
|
57
57
|
return (
|
|
58
58
|
<div className="flex flex-col gap-1.5">
|
|
59
59
|
<TokenTile type="source" />
|
|
60
|
-
{/* Zero-height anchor between the tiles, like ReorderButton in SwapView
|
|
60
|
+
{/* Zero-height anchor between the tiles, like ReorderButton in SwapView.
|
|
61
|
+
Opaque bg on purpose: Chromium ignores backdrop-filter nested inside
|
|
62
|
+
the widget's own backdrop-blur, so a translucent bg would let the
|
|
63
|
+
tile edges show through the marker. */}
|
|
61
64
|
<div className="h-0 w-full relative z-[1]">
|
|
62
|
-
<div className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-[24px] bg-t_bg_secondary bg-
|
|
65
|
+
<div className="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-[24px] bg-t_bg_secondary bg-gradient-to-b from-t_bg_tertiary/10 to-t_bg_tertiary/[0.08]">
|
|
63
66
|
<ChevronDownWideThinIcon className="w-6 h-6 text-t_text_primary opacity-50" />
|
|
64
67
|
</div>
|
|
65
68
|
</div>
|
|
@@ -7,7 +7,7 @@ type Props = {
|
|
|
7
7
|
export const Header = ({ onCloseClick }: Props) => {
|
|
8
8
|
return (
|
|
9
9
|
<div className="flex justify-between items-center gap-6">
|
|
10
|
-
<div className="text-2xl leading-8 tracking-[0.01em] text-t_text_primary">
|
|
10
|
+
<div className="text-2xl font-medium leading-8 tracking-[0.01em] text-t_text_primary">
|
|
11
11
|
Confirmation
|
|
12
12
|
</div>
|
|
13
13
|
<button
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CheckCircleThinIcon,
|
|
3
3
|
ErrorCircleThinIcon,
|
|
4
|
-
|
|
4
|
+
RedirectThinIcon,
|
|
5
5
|
} from "@src/assets/icons";
|
|
6
6
|
import { useSwapContext, useTxUIWrapper } from "@src/context";
|
|
7
7
|
import { TokenTiles } from "../TokenTiles";
|
|
@@ -18,7 +18,7 @@ export const TxResult = ({ onCloseClick }: Props) => {
|
|
|
18
18
|
<div className="flex flex-col gap-8 text-t_text_primary">
|
|
19
19
|
<div className="flex flex-col gap-2">
|
|
20
20
|
<div className="flex items-center gap-3">
|
|
21
|
-
<div className="text-2xl leading-8 tracking-[0.01em]">
|
|
21
|
+
<div className="text-2xl font-medium leading-8 tracking-[0.01em]">
|
|
22
22
|
{txError ? "Transaction Error" : "Transaction Success"}
|
|
23
23
|
</div>
|
|
24
24
|
{txError ? (
|
|
@@ -42,22 +42,7 @@ export const TxResult = ({ onCloseClick }: Props) => {
|
|
|
42
42
|
)
|
|
43
43
|
)}
|
|
44
44
|
</div>
|
|
45
|
-
{!txMsg &&
|
|
46
|
-
<div className="flex flex-col gap-4">
|
|
47
|
-
<TokenTiles />
|
|
48
|
-
{!txError && (
|
|
49
|
-
<a
|
|
50
|
-
href={txExplorerUrl}
|
|
51
|
-
target="_blank"
|
|
52
|
-
rel="noreferrer"
|
|
53
|
-
className="flex items-center gap-2 self-start text-sm leading-5 tracking-[0.01em] text-t_text_primary text-opacity-50 hover:text-opacity-80 transition-colors"
|
|
54
|
-
>
|
|
55
|
-
View details on Explorer
|
|
56
|
-
<LinkExternalThinIcon className="w-3 h-3" />
|
|
57
|
-
</a>
|
|
58
|
-
)}
|
|
59
|
-
</div>
|
|
60
|
-
)}
|
|
45
|
+
{!txMsg && <TokenTiles />}
|
|
61
46
|
<div className="h-px w-full gradient-sections-separator-container" />
|
|
62
47
|
<button
|
|
63
48
|
type="button"
|
|
@@ -66,6 +51,22 @@ export const TxResult = ({ onCloseClick }: Props) => {
|
|
|
66
51
|
>
|
|
67
52
|
Continue
|
|
68
53
|
</button>
|
|
54
|
+
{!txMsg && !txError && (
|
|
55
|
+
<div className="w-full flex gap-3 items-center">
|
|
56
|
+
<a
|
|
57
|
+
href={txExplorerUrl}
|
|
58
|
+
target="_blank"
|
|
59
|
+
rel="noreferrer"
|
|
60
|
+
className="flex items-center gap-1.5 text-sm text-t_text_primary text-opacity-25 hover:text-opacity-50 transition-colors"
|
|
61
|
+
>
|
|
62
|
+
View On Explorer{" "}
|
|
63
|
+
<div className="w-6 h-6 bg-t_bg_tertiary bg-opacity-5 rounded-[4px] flex items-center justify-center">
|
|
64
|
+
<RedirectThinIcon className="w-3 h-3" />
|
|
65
|
+
</div>
|
|
66
|
+
</a>
|
|
67
|
+
<div className="flex-1 h-px bg-t_text_primary bg-opacity-10" />
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
69
70
|
</div>
|
|
70
71
|
);
|
|
71
72
|
};
|
|
@@ -200,8 +200,10 @@ export const FeesPanel = () => {
|
|
|
200
200
|
</>
|
|
201
201
|
)}
|
|
202
202
|
</div>
|
|
203
|
+
{/* The 24px glyph has 8px of built-in whitespace on each side; pull the
|
|
204
|
+
right side back so the chevron sits flush with the panel padding. */}
|
|
203
205
|
<div
|
|
204
|
-
className={`flex items-center justify-center w-6 h-6 text-t_text_primary opacity-50 transition-transform ${
|
|
206
|
+
className={`flex items-center justify-center w-6 h-6 -mr-2 text-t_text_primary opacity-50 transition-transform ${
|
|
205
207
|
expanded ? "rotate-180" : ""
|
|
206
208
|
}`}
|
|
207
209
|
>
|
|
@@ -22,6 +22,7 @@ export const AmountPanel = ({ type }: Props) => {
|
|
|
22
22
|
setSrcValue,
|
|
23
23
|
isFetchingRoute,
|
|
24
24
|
bridgeUI,
|
|
25
|
+
isAsyncDataLoaded,
|
|
25
26
|
} = useSwapContext();
|
|
26
27
|
|
|
27
28
|
const onMaxClick = () => {
|
|
@@ -58,6 +59,14 @@ export const AmountPanel = ({ type }: Props) => {
|
|
|
58
59
|
dstChain,
|
|
59
60
|
]);
|
|
60
61
|
|
|
62
|
+
if (!isAsyncDataLoaded) {
|
|
63
|
+
return (
|
|
64
|
+
<div className="flex items-center flex-1 min-w-0">
|
|
65
|
+
<Skeleton className="w-[100px] h-8" />
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
61
70
|
return (
|
|
62
71
|
<div className="flex items-center gap-3 flex-1 min-w-0 overflow-hidden text-left">
|
|
63
72
|
{type === "source" ? (
|
|
@@ -56,7 +56,7 @@ export const ChainPicker = ({
|
|
|
56
56
|
return (
|
|
57
57
|
<>
|
|
58
58
|
<div className="flex justify-between items-center mb-6">
|
|
59
|
-
<div className="text-2xl leading-8 tracking-[0.01em] text-t_text_primary">
|
|
59
|
+
<div className="text-2xl font-medium leading-8 tracking-[0.01em] text-t_text_primary">
|
|
60
60
|
Select Network
|
|
61
61
|
</div>
|
|
62
62
|
<button
|
package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/NetworkFilter/index.tsx
CHANGED
|
@@ -99,58 +99,68 @@ export const NetworkFilter = ({ type, selectedChain, onSelect }: Props) => {
|
|
|
99
99
|
</button>
|
|
100
100
|
|
|
101
101
|
{open && (
|
|
102
|
-
<div className="absolute right-
|
|
103
|
-
<div className="flex items-center gap-2 px-
|
|
104
|
-
<div className="text-t_text_primary opacity-60 [&_svg]:w-
|
|
102
|
+
<div className="absolute -right-2 top-[calc(100%+8px)] z-20 flex flex-col w-[296px] py-5 px-2.5 rounded-xl network-dropdown-surface border border-solid border-t_text_primary border-opacity-5 overflow-hidden">
|
|
103
|
+
<div className="flex items-center gap-2 px-2.5 pb-4">
|
|
104
|
+
<div className="text-t_text_primary opacity-60 [&_svg]:w-4 [&_svg]:h-4 flex items-center">
|
|
105
105
|
<SearchThinIcon />
|
|
106
106
|
</div>
|
|
107
107
|
<input
|
|
108
108
|
value={searchValue}
|
|
109
109
|
onChange={(e) => setSearchValue(e.target.value)}
|
|
110
110
|
placeholder={`Browse ${chains.length} Chains...`}
|
|
111
|
-
className="bg-transparent border-none outline-none w-full text-sm text-t_text_primary placeholder:text-t_text_primary placeholder:text-opacity-40"
|
|
111
|
+
className="bg-transparent border-none outline-none w-full text-sm leading-6 text-t_text_primary placeholder:text-t_text_primary placeholder:text-opacity-40"
|
|
112
112
|
/>
|
|
113
113
|
</div>
|
|
114
|
-
<div className="h-
|
|
114
|
+
<div className="h-px w-full bg-t_bg_tertiary bg-opacity-5 mb-2" />
|
|
115
|
+
{/* The design's list mask is a hard cut 6px into the sixth row —
|
|
116
|
+
that sliver is the hint that the list scrolls. Rows are 40px
|
|
117
|
+
pills around 24px of content, so the cut lands at 214px. */}
|
|
118
|
+
<div className="h-[214px]">
|
|
115
119
|
<Virtuoso
|
|
116
|
-
style={{ height: "
|
|
120
|
+
style={{ height: "214px" }}
|
|
117
121
|
data={[undefined, ...filteredChains] as (Chain | undefined)[]}
|
|
118
|
-
itemContent={(_, chain) =>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
122
|
+
itemContent={(_, chain) => {
|
|
123
|
+
const isSelected =
|
|
124
|
+
(chain?.chainId || "") === (selectedChain?.chainId || "");
|
|
125
|
+
return (
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
onClick={() => {
|
|
129
|
+
onSelect(chain);
|
|
130
|
+
setOpen(false);
|
|
131
|
+
}}
|
|
132
|
+
className={`network-dropdown-row flex items-center gap-2.5 w-full h-10 px-2.5 rounded-lg text-left ${
|
|
133
|
+
isSelected ? "network-dropdown-row-active" : ""
|
|
134
|
+
}`}
|
|
135
|
+
>
|
|
136
|
+
{chain ? (
|
|
137
|
+
<img
|
|
138
|
+
src={chain.image}
|
|
139
|
+
alt={chain.displayName}
|
|
140
|
+
className="w-6 h-6 rounded-full"
|
|
141
|
+
/>
|
|
142
|
+
) : (
|
|
143
|
+
<div className="grid grid-cols-2 gap-0.5 shrink-0">
|
|
144
|
+
{clusterChains.map((clusterChain) => (
|
|
145
|
+
<img
|
|
146
|
+
key={clusterChain.chainId}
|
|
147
|
+
src={clusterChain.image}
|
|
148
|
+
alt={clusterChain.displayName}
|
|
149
|
+
className="w-[11px] h-[11px] rounded-full"
|
|
150
|
+
/>
|
|
151
|
+
))}
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
<p
|
|
155
|
+
className={`text-sm leading-[18px] tracking-[0.01em] text-t_text_primary truncate ${
|
|
156
|
+
isSelected ? "" : "text-opacity-80"
|
|
157
|
+
}`}
|
|
158
|
+
>
|
|
159
|
+
{chain ? chain.displayName : "All Networks"}
|
|
160
|
+
</p>
|
|
161
|
+
</button>
|
|
162
|
+
);
|
|
163
|
+
}}
|
|
154
164
|
/>
|
|
155
165
|
</div>
|
|
156
166
|
</div>
|
|
@@ -139,7 +139,7 @@ export const TokenItem = ({
|
|
|
139
139
|
)
|
|
140
140
|
)}
|
|
141
141
|
{resetWarning ? (
|
|
142
|
-
<div className="flex items-center justify-end gap-1.5 text-[#
|
|
142
|
+
<div className="flex items-center justify-end gap-1.5 text-[#F46F28]">
|
|
143
143
|
<WarningIcon className="w-3.5 h-3.5 shrink-0" />
|
|
144
144
|
<span className="text-xs leading-[18px] tracking-[0.01em] whitespace-nowrap">
|
|
145
145
|
<span className="sm:hidden">
|
|
@@ -480,7 +480,7 @@ export const TokenPicker = ({
|
|
|
480
480
|
return showImportWarning ? (
|
|
481
481
|
<>
|
|
482
482
|
<div className="flex justify-between items-center mb-6">
|
|
483
|
-
<div className="text-2xl leading-8 tracking-[0.01em]">
|
|
483
|
+
<div className="text-2xl font-medium leading-8 tracking-[0.01em]">
|
|
484
484
|
Import token
|
|
485
485
|
</div>
|
|
486
486
|
<button
|
|
@@ -570,7 +570,7 @@ export const TokenPicker = ({
|
|
|
570
570
|
) : (
|
|
571
571
|
<>
|
|
572
572
|
<div className="flex justify-between items-center mb-6">
|
|
573
|
-
<div className="text-2xl leading-8 tracking-[0.01em] text-t_text_primary">
|
|
573
|
+
<div className="text-2xl font-medium leading-8 tracking-[0.01em] text-t_text_primary">
|
|
574
574
|
Select Token
|
|
575
575
|
</div>
|
|
576
576
|
<button
|
|
@@ -626,7 +626,7 @@ export const TokenPicker = ({
|
|
|
626
626
|
<div className="text-sm font-medium text-ellipsis">
|
|
627
627
|
{customTokenData.name}
|
|
628
628
|
</div>
|
|
629
|
-
<div className="text-xs text-t_text_primary !text-opacity-50
|
|
629
|
+
<div className="text-xs text-t_text_primary !text-opacity-50">
|
|
630
630
|
{importChain?.displayName}
|
|
631
631
|
</div>
|
|
632
632
|
</div>
|
|
@@ -648,7 +648,7 @@ export const TokenPicker = ({
|
|
|
648
648
|
<div className="flex flex-col h-full min-h-0">
|
|
649
649
|
{/* Pinned label of the group at the top of the list; sits
|
|
650
650
|
above the scroller, so rows clip on its bottom edge. */}
|
|
651
|
-
<p className="pb-3 text-[10px]
|
|
651
|
+
<p className="pb-3 text-[10px] leading-4 tracking-[0.015em] text-t_text_primary text-opacity-30 text-left shrink-0">
|
|
652
652
|
{pinnedGroupLabel}
|
|
653
653
|
</p>
|
|
654
654
|
<div className="relative flex flex-col flex-1 min-h-0">
|
|
@@ -663,7 +663,7 @@ export const TokenPicker = ({
|
|
|
663
663
|
}
|
|
664
664
|
if (row.kind === "label") {
|
|
665
665
|
return (
|
|
666
|
-
<p className="pt-2 pb-3 text-[10px]
|
|
666
|
+
<p className="pt-2 pb-3 text-[10px] leading-4 tracking-[0.015em] text-t_text_primary text-opacity-30 text-left">
|
|
667
667
|
{row.label}
|
|
668
668
|
</p>
|
|
669
669
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChevronDownThinIcon } from "@src/assets/icons";
|
|
2
|
-
import { TokenLogoWithChain } from "@src/components";
|
|
2
|
+
import { Skeleton, TokenLogoWithChain } from "@src/components";
|
|
3
3
|
import { useSwapContext } from "@src/context";
|
|
4
4
|
import { Chain, Token } from "@src/models";
|
|
5
5
|
import { useMemo } from "react";
|
|
@@ -19,6 +19,7 @@ export const TokenPanel = ({ chain, token, type, className }: Props) => {
|
|
|
19
19
|
srcTokenLocked,
|
|
20
20
|
dstChainLocked,
|
|
21
21
|
setActivePicker,
|
|
22
|
+
isAsyncDataLoaded,
|
|
22
23
|
} = useSwapContext();
|
|
23
24
|
|
|
24
25
|
const integratorLockedDestinationToken = useMemo(
|
|
@@ -43,6 +44,25 @@ export const TokenPanel = ({ chain, token, type, className }: Props) => {
|
|
|
43
44
|
integratorLockedDestinationToken ||
|
|
44
45
|
integratorLockedSourceToken;
|
|
45
46
|
|
|
47
|
+
if (!isAsyncDataLoaded) {
|
|
48
|
+
// Mirror the loaded layout: symbol bar + token circle with the small
|
|
49
|
+
// chain badge, so nothing shifts when the real content arrives.
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
className={`flex items-center shrink-0 min-h-11 ${className || ""}`}
|
|
53
|
+
aria-hidden
|
|
54
|
+
>
|
|
55
|
+
<div className="flex items-center gap-3">
|
|
56
|
+
<Skeleton className="w-12 h-4" />
|
|
57
|
+
<div className="relative flex items-end pr-1 pb-1">
|
|
58
|
+
<Skeleton className="w-10 h-10 rounded-full" />
|
|
59
|
+
<Skeleton className="absolute bottom-0 right-0 w-[18px] h-[18px] rounded-full border border-solid border-t_bg_secondary" />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
46
66
|
return (
|
|
47
67
|
<button
|
|
48
68
|
type="button"
|
|
@@ -86,7 +106,9 @@ export const TokenPanel = ({ chain, token, type, className }: Props) => {
|
|
|
86
106
|
)}
|
|
87
107
|
</div>
|
|
88
108
|
{!locked && (
|
|
89
|
-
|
|
109
|
+
// The 24px glyph has 8px of built-in whitespace on each side; pull the
|
|
110
|
+
// right side back so the chevron sits flush with the panel edge.
|
|
111
|
+
<div className="flex items-center justify-center w-6 h-6 ml-1 -mr-2 text-t_text_primary opacity-50">
|
|
90
112
|
<ChevronDownThinIcon />
|
|
91
113
|
</div>
|
|
92
114
|
)}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChevronDownThinSmallIcon } from "@src/assets/icons";
|
|
2
|
+
import { Skeleton } from "@src/components";
|
|
2
3
|
import { useWallet } from "@src/context/WalletProvider";
|
|
3
4
|
import { useSwapContext } from "@src/context";
|
|
4
5
|
import { SwapPanelType } from "../..";
|
|
@@ -21,8 +22,12 @@ export const WalletPanel = ({ chain, className }: Props) => {
|
|
|
21
22
|
solana,
|
|
22
23
|
sui,
|
|
23
24
|
} = useWallet();
|
|
24
|
-
const {
|
|
25
|
-
|
|
25
|
+
const {
|
|
26
|
+
integrationConfig,
|
|
27
|
+
hasOnConnectWalletCallback,
|
|
28
|
+
onConnectWallet,
|
|
29
|
+
isAsyncDataLoaded,
|
|
30
|
+
} = useSwapContext();
|
|
26
31
|
|
|
27
32
|
const openEvmWalletModal = useCallback(() => {
|
|
28
33
|
// Mirror SwapButton's contract: when the integrator supplied their own
|
|
@@ -68,6 +73,10 @@ export const WalletPanel = ({ chain, className }: Props) => {
|
|
|
68
73
|
return "";
|
|
69
74
|
}, [chain, evm, solana, sui]);
|
|
70
75
|
|
|
76
|
+
if (!isAsyncDataLoaded) {
|
|
77
|
+
return <Skeleton className="w-16 h-6" />;
|
|
78
|
+
}
|
|
79
|
+
|
|
71
80
|
// Reserve the button's height before the chain loads so the card
|
|
72
81
|
// doesn't grow when the wallet button appears.
|
|
73
82
|
if (!chain) {
|
|
@@ -85,7 +94,9 @@ export const WalletPanel = ({ chain, className }: Props) => {
|
|
|
85
94
|
<p className="text-[10px] leading-4 tracking-[0.01em] text-t_text_primary whitespace-nowrap">
|
|
86
95
|
{address ? shortAddress(address) : "Connect"}
|
|
87
96
|
</p>
|
|
88
|
-
|
|
97
|
+
{/* The 16px glyph has 6px of built-in whitespace on each side; pull the
|
|
98
|
+
right side back so the button's visual padding stays symmetric. */}
|
|
99
|
+
<div className="flex items-center justify-center w-4 h-4 -mr-1.5 text-t_text_primary opacity-50">
|
|
89
100
|
<ChevronDownThinSmallIcon />
|
|
90
101
|
</div>
|
|
91
102
|
</button>
|