@gluwa/connect-kit 0.1.0-next.3 → 0.2.0-next.3

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 (95) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/assets/creditwallet.png +0 -0
  3. package/assets/fonts/Inter-Medium.ttf +0 -0
  4. package/assets/fonts/Inter-Medium.woff2 +0 -0
  5. package/assets/fonts/Inter-Regular.ttf +0 -0
  6. package/assets/fonts/Inter-Regular.woff2 +0 -0
  7. package/assets/fonts/Inter-SemiBold.ttf +0 -0
  8. package/assets/fonts/Inter-SemiBold.woff2 +0 -0
  9. package/assets/graphic.png +0 -0
  10. package/assets/metamask.png +0 -0
  11. package/assets/wc-logo.svg +3 -0
  12. package/assets/wc-search.png +0 -0
  13. package/dist/chunk-EDNRW47A.js +401 -0
  14. package/dist/credit-connect.js +2 -394
  15. package/dist/index.css +2074 -0
  16. package/dist/index.d.ts +53 -33
  17. package/dist/index.js +1404 -1504
  18. package/dist/package.json +16 -10
  19. package/package.json +14 -11
  20. package/src/ConnectKitProvider.tsx +126 -75
  21. package/src/api/account.ts +21 -0
  22. package/src/api/balance.ts +10 -0
  23. package/src/api/chain.ts +17 -0
  24. package/src/api/contract.ts +33 -0
  25. package/src/api/transaction.ts +12 -0
  26. package/src/assets.d.ts +5 -0
  27. package/src/components/actionItem/index.tsx +50 -0
  28. package/src/components/actionItem/style.scss +47 -0
  29. package/src/components/copyLink/index.tsx +16 -0
  30. package/src/components/copyLink/style.scss +16 -0
  31. package/src/components/qrArea/index.tsx +70 -0
  32. package/src/components/qrArea/style.scss +130 -0
  33. package/src/components/searchBar/asset/Search.tsx +20 -0
  34. package/src/components/searchBar/asset/XMarkCircle.tsx +24 -0
  35. package/src/components/searchBar/index.tsx +44 -0
  36. package/src/components/searchBar/style.scss +58 -0
  37. package/src/components/snackbar/index.tsx +25 -0
  38. package/src/components/snackbar/style.scss +51 -0
  39. package/src/components/toggle/index.tsx +27 -0
  40. package/src/components/toggle/style.scss +53 -0
  41. package/src/components/walletGrid/index.tsx +64 -0
  42. package/src/components/walletGrid/style.scss +108 -0
  43. package/src/components/walletItem/index.tsx +49 -0
  44. package/src/components/walletItem/style.scss +70 -0
  45. package/src/connector-meta.ts +12 -7
  46. package/src/core/config.ts +102 -0
  47. package/src/creditConnectConnector.ts +192 -184
  48. package/src/events/account.ts +49 -0
  49. package/src/hooks/useWCState.ts +45 -13
  50. package/src/hooks/useWagmiConnect.ts +13 -9
  51. package/src/index.ts +35 -2
  52. package/src/layout/allWallets/index.tsx +52 -0
  53. package/src/layout/allWallets/style.scss +62 -0
  54. package/src/layout/connectDialog/asset/backIcon.tsx +24 -0
  55. package/src/layout/connectDialog/asset/checkIcon.tsx +28 -0
  56. package/src/layout/connectDialog/asset/closeIcon.tsx +21 -0
  57. package/src/layout/connectDialog/asset/copyIcon.tsx +28 -0
  58. package/src/layout/connectDialog/asset/linkIcon.tsx +24 -0
  59. package/src/layout/connectDialog/asset/spinner.tsx +21 -0
  60. package/src/layout/connectDialog/idle/index.tsx +32 -0
  61. package/src/layout/connectDialog/idle/style.scss +61 -0
  62. package/src/layout/connectDialog/index.tsx +286 -0
  63. package/src/layout/connectDialog/loading/index.tsx +13 -0
  64. package/src/layout/connectDialog/loading/style.scss +19 -0
  65. package/src/layout/connectDialog/metaMask/index.tsx +61 -0
  66. package/src/layout/connectDialog/metaMask/style.scss +65 -0
  67. package/src/layout/connectDialog/qr/index.tsx +33 -0
  68. package/src/layout/connectDialog/style.scss +95 -0
  69. package/src/layout/connectDialog/timeout/index.tsx +31 -0
  70. package/src/layout/connectDialog/timeout/style.scss +64 -0
  71. package/src/layout/connectDialog/walletConnect/index.tsx +45 -0
  72. package/src/layout/connectDialog/walletConnect/style.scss +48 -0
  73. package/src/layout/connectDialog/walletQR/index.tsx +37 -0
  74. package/src/layout/detailPanel/index.tsx +147 -0
  75. package/src/layout/detailPanel/style.scss +64 -0
  76. package/src/layout/selectorPanel/index.tsx +100 -0
  77. package/src/layout/selectorPanel/style.scss +113 -0
  78. package/src/style/color.scss +417 -0
  79. package/src/style/font.scss +28 -0
  80. package/src/style/index.scss +5 -0
  81. package/src/style/mixin.scss +217 -0
  82. package/src/style/reset.scss +110 -0
  83. package/src/style/variables.scss +13 -0
  84. package/src/types.ts +19 -7
  85. package/src/utils/platform.ts +7 -4
  86. package/tsup.config.ts +32 -2
  87. package/dist/chunk-DDA6FP6U.js +0 -7
  88. package/src/ConnectModal.scss +0 -665
  89. package/src/ConnectModal.tsx +0 -559
  90. package/src/components/QRFrame.tsx +0 -43
  91. package/src/views/CreditWalletView.tsx +0 -59
  92. package/src/views/IdleView.tsx +0 -10
  93. package/src/views/MetaMaskView.tsx +0 -64
  94. package/src/views/SwitchChainView.tsx +0 -67
  95. package/src/views/WalletConnectView.tsx +0 -206
@@ -0,0 +1,70 @@
1
+ import { type FC, type CSSProperties } from 'react';
2
+ import { QRCodeSVG } from 'qrcode.react';
3
+ import { CopyLink } from '../copyLink';
4
+ import './style.scss';
5
+
6
+ export type QrAreaStatus = 'loading' | 'ready' | 'timeout';
7
+
8
+ interface QrAreaProps {
9
+ uri?: string | null;
10
+ status: QrAreaStatus;
11
+ logoUrl?: string;
12
+ hasBg?: boolean;
13
+ label?: string;
14
+ onCopy?: () => void;
15
+ }
16
+
17
+ const BG_COLOR = '#ffffff';
18
+ const FG_COLOR = '#000000';
19
+ const FG_GRADIENT = 'url(#ck-qr-gradient)';
20
+
21
+ export const QrArea: FC<QrAreaProps> = ({ uri, status, logoUrl, hasBg = false, label, onCopy }) => {
22
+ const bgColor = hasBg ? BG_COLOR : 'transparent';
23
+ const fgColor = hasBg ? FG_COLOR : FG_GRADIENT;
24
+
25
+ return (
26
+ <div className="ck-qr-area">
27
+ <div className="ck-qr-group">
28
+ <figure className={`ck-qr ck-qr-${status}`} aria-label="QR Code">
29
+ {status === 'loading' && (
30
+ <div className="ck-qr-skeleton" aria-busy="true" aria-label="Loading QR code" />
31
+ )}
32
+ {status === 'ready' && uri && (
33
+ <div
34
+ className="ck-qr-frame"
35
+ style={{ 'background': bgColor, '--ck-qr-bg': bgColor } as CSSProperties}
36
+ >
37
+ {!hasBg && (
38
+ <svg width="0" height="0" style={{ position: 'absolute' }} aria-hidden="true">
39
+ <defs>
40
+ <linearGradient id="ck-qr-gradient" x1="67%" y1="11%" x2="45%" y2="99%">
41
+ <stop offset="0%" stopColor="#7B91FF" />
42
+ <stop offset="100%" stopColor="#61BCFF" />
43
+ </linearGradient>
44
+ </defs>
45
+ </svg>
46
+ )}
47
+ <QRCodeSVG
48
+ value={uri}
49
+ size={288}
50
+ bgColor={bgColor}
51
+ fgColor={fgColor}
52
+ level="M"
53
+ marginSize={0}
54
+ imageSettings={
55
+ logoUrl ? { src: logoUrl, width: 68, height: 68, excavate: true } : undefined
56
+ }
57
+ />
58
+ </div>
59
+ )}
60
+ </figure>
61
+ {(label || onCopy) && (
62
+ <div className="ck-qr-text">
63
+ {label && <p className="ck-qr-label">{label}</p>}
64
+ {onCopy && <CopyLink onCopy={onCopy} />}
65
+ </div>
66
+ )}
67
+ </div>
68
+ </div>
69
+ );
70
+ };
@@ -0,0 +1,130 @@
1
+ @use '../../style/mixin.scss' as *;
2
+
3
+ .ck-qr-area {
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ flex: 1;
8
+ width: 100%;
9
+ padding-bottom: 32px;
10
+ }
11
+
12
+ // .ck-qr-group {
13
+ // display: flex;
14
+ // flex-direction: column;
15
+ // align-items: center;
16
+ // gap: 24px;
17
+ // }
18
+
19
+ // ── Text ──────────────────────────────────────────────────────────────────────
20
+ .ck-qr-text {
21
+ display: flex;
22
+ flex-direction: column;
23
+ align-items: center;
24
+ gap: 6px;
25
+ width: 100%;
26
+ margin-top: 24px;
27
+ }
28
+
29
+ .ck-qr-label {
30
+ @include textToken('subtitle2-bold');
31
+ color: var(--scale-gray-900);
32
+ text-align: center;
33
+ }
34
+
35
+ .ck-qr {
36
+ display: flex;
37
+ flex-direction: column;
38
+ align-items: center;
39
+ margin: 0;
40
+ border-radius: 20px;
41
+ overflow: hidden;
42
+ outline: none;
43
+ }
44
+
45
+ .ck-qr-loading {
46
+ border: 1px solid var(--semantic-divider-divider-1);
47
+ }
48
+
49
+ .ck-qr-timeout {
50
+ border: 1px solid var(--semantic-divider-divider-1);
51
+ }
52
+
53
+ // ── Skeleton ──────────────────────────────────────────────────────────────────
54
+ .ck-qr-skeleton {
55
+ width: 248px;
56
+ height: 248px;
57
+ background: linear-gradient(
58
+ 132deg,
59
+ var(--scale-gray-100) 36%,
60
+ var(--scale-gray-200) 51%,
61
+ var(--scale-gray-100) 66%
62
+ );
63
+ background-size: 200% 100%;
64
+ animation: ck-qr-shimmer 1.5s linear infinite;
65
+
66
+ @include tablet {
67
+ width: 200px;
68
+ height: 200px;
69
+ }
70
+ }
71
+
72
+ @keyframes ck-qr-shimmer {
73
+ 0% { background-position: 200% 0; }
74
+ 100% { background-position: -200% 0; }
75
+ }
76
+
77
+ // ── Ready ─────────────────────────────────────────────────────────────────────
78
+ .ck-qr-frame {
79
+ --ck-qr-bg: transparent;
80
+ padding: 20px;
81
+ border-radius: 20px;
82
+ border: 1px solid var(--semantic-divider-divider-1);
83
+
84
+ svg {
85
+ display: block;
86
+ width: 246px;
87
+ height: 246px;
88
+
89
+ @include tablet {
90
+ width: 198px;
91
+ height: 198px;
92
+ }
93
+ }
94
+ }
95
+
96
+ // ── Timeout ───────────────────────────────────────────────────────────────────
97
+ .ck-qr-timeout {
98
+ width: 248px;
99
+ height: 248px;
100
+ background: var(--scale-gray-100);
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+
105
+ @include tablet {
106
+ width: 200px;
107
+ height: 200px;
108
+ }
109
+ }
110
+
111
+ .ck-qr-timeout-inner {
112
+ display: flex;
113
+ flex-direction: column;
114
+ align-items: center;
115
+ gap: 8px;
116
+ }
117
+
118
+ .ck-qr-timeout-icon {
119
+ display: block;
120
+ width: 32px;
121
+ height: 32px;
122
+ background: var(--scale-gray-400);
123
+ border-radius: 50%;
124
+ }
125
+
126
+ .ck-qr-timeout-text {
127
+ @include textToken('label5-regular');
128
+ color: var(--scale-gray-600);
129
+ text-align: center;
130
+ }
@@ -0,0 +1,20 @@
1
+ import { type FC, type SVGProps } from 'react';
2
+
3
+ export const SearchIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
4
+ return (
5
+ <svg
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ width="20"
8
+ height="19"
9
+ viewBox="0 0 20 19"
10
+ fill="none"
11
+ aria-hidden="true"
12
+ {...props}
13
+ >
14
+ <path
15
+ d="M14.4688 8.03418C14.4688 4.48066 11.5877 1.59961 8.03418 1.59961C4.48068 1.59963 1.59961 4.48067 1.59961 8.03418C1.59963 11.5877 4.48069 14.4688 8.03418 14.4688C11.5877 14.4688 14.4688 11.5877 14.4688 8.03418ZM16.0577 8.44726C15.9732 10.1146 15.3799 11.6473 14.4307 12.8946L19.0674 17.5108L19.1221 17.5713C19.3788 17.8848 19.3608 18.3474 19.0674 18.6397C18.7738 18.9319 18.3091 18.95 17.9942 18.6944L17.9336 18.6397L13.3389 14.0645C12.0201 15.2255 10.3191 15.9628 8.44726 16.0576L8.03418 16.0684C3.59703 16.0684 1.9e-05 12.4714 0 8.03418L0.010742 7.62109C0.225741 3.3759 3.73556 1.8e-05 8.03418 0L8.44726 0.010742C12.6925 0.225746 16.0684 3.73555 16.0684 8.03418L16.0577 8.44726Z"
16
+ fill="currentColor"
17
+ />
18
+ </svg>
19
+ );
20
+ };
@@ -0,0 +1,24 @@
1
+ import { type FC, type SVGProps } from 'react';
2
+
3
+ export const XMarkCircleIcon: FC<SVGProps<SVGSVGElement>> = (props) => {
4
+ return (
5
+ <svg
6
+ width={20}
7
+ height={20}
8
+ viewBox="0 0 20 20"
9
+ fill="none"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ aria-hidden="true"
12
+ focusable="false"
13
+ {...props}
14
+ >
15
+ <circle cx="10" cy="10" r="10" fill="currentColor" />
16
+ <path
17
+ d="M6.5 6.5L13.5 13.5M13.5 6.5L6.5 13.5"
18
+ stroke="var(--semantic-paper-sheet)"
19
+ strokeWidth="1.5"
20
+ strokeLinecap="round"
21
+ />
22
+ </svg>
23
+ );
24
+ };
@@ -0,0 +1,44 @@
1
+ import { type FC, type ChangeEvent } from 'react';
2
+ import './style.scss';
3
+ import { SearchIcon } from './asset/Search';
4
+ import { XMarkCircleIcon } from './asset/XMarkCircle';
5
+
6
+ interface SearchBarProps {
7
+ value: string;
8
+ placeholder?: string;
9
+ onChange: (value: string) => void;
10
+ onClear: () => void;
11
+ }
12
+
13
+ export const SearchBar: FC<SearchBarProps> = ({
14
+ value,
15
+ placeholder = 'Search wallet',
16
+ onChange,
17
+ onClear,
18
+ }) => {
19
+ const handleChange = (e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value);
20
+
21
+ return (
22
+ <div className="ck-search">
23
+ <SearchIcon className="ck-search-icon" />
24
+ <input
25
+ className="ck-search-input"
26
+ type="search"
27
+ value={value}
28
+ placeholder={placeholder}
29
+ onChange={handleChange}
30
+ aria-label="Search wallet"
31
+ />
32
+ <button
33
+ type="button"
34
+ className="ck-search-clear"
35
+ onClick={onClear}
36
+ aria-label="Clear search"
37
+ aria-hidden={!value || undefined}
38
+ tabIndex={value ? 0 : -1}
39
+ >
40
+ <XMarkCircleIcon />
41
+ </button>
42
+ </div>
43
+ );
44
+ };
@@ -0,0 +1,58 @@
1
+ @use '../../style/mixin.scss' as *;
2
+
3
+ .ck-root .ck-search {
4
+ display: flex;
5
+ align-items: center;
6
+ gap: 6px;
7
+ flex: 1;
8
+ padding: 12px 16px;
9
+ border-radius: 100px;
10
+ background: var(--scale-gray-100);
11
+ border: 1px solid transparent;
12
+ transition: border-color 0.15s ease;
13
+
14
+ &:focus-within {
15
+ border-color: var(--semantic-brand-trugi-primary);
16
+ }
17
+ }
18
+
19
+ .ck-search-icon {
20
+ display: block;
21
+ flex-shrink: 0;
22
+ color: var(--scale-gray-500);
23
+ width: 20px;
24
+ height: 19px;
25
+ }
26
+
27
+ .ck-root .ck-search-input {
28
+ flex: 1;
29
+ @include textToken('body4-l2-regular');
30
+ color: var(--scale-gray-900);
31
+ caret-color: var(--semantic-brand-trugi-primary);
32
+ background: none;
33
+ min-width: 0;
34
+
35
+ &::placeholder {
36
+ color: var(--scale-gray-500);
37
+ }
38
+
39
+ &::-webkit-search-cancel-button {
40
+ display: none;
41
+ }
42
+ }
43
+
44
+ .ck-root .ck-search-clear {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ width: 20px;
49
+ height: 20px;
50
+ flex-shrink: 0;
51
+ color: var(--scale-gray-500);
52
+ cursor: pointer;
53
+ visibility: hidden;
54
+
55
+ &:not([aria-hidden]) {
56
+ visibility: visible;
57
+ }
58
+ }
@@ -0,0 +1,25 @@
1
+ import { type FC } from 'react';
2
+ import './style.scss';
3
+ import { CloseIcon } from '../../layout/connectDialog/asset/closeIcon';
4
+ import { CheckIcon } from '../../layout/connectDialog/asset/checkIcon';
5
+
6
+ interface SnackbarProps {
7
+ message: string;
8
+ onClose?: () => void;
9
+ }
10
+
11
+ export const Snackbar: FC<SnackbarProps> = ({ message, onClose }) => {
12
+ return (
13
+ <div className="ck-snackbar" role="status" aria-live="polite">
14
+ <div className="ck-snackbar-content">
15
+ <CheckIcon className="ck-snackbar-check" />
16
+ <p className="ck-snackbar-message">{message}</p>
17
+ </div>
18
+ {onClose && (
19
+ <button type="button" className="ck-snackbar-close" onClick={onClose} aria-label="Close">
20
+ <CloseIcon width={20} height={20} />
21
+ </button>
22
+ )}
23
+ </div>
24
+ );
25
+ };
@@ -0,0 +1,51 @@
1
+ @use '../../style/mixin.scss' as *;
2
+
3
+ .ck-snackbar {
4
+ display: flex;
5
+ align-items: center;
6
+ gap: 16px;
7
+ padding: 12px 16px;
8
+ border-radius: 8px;
9
+ background: var(--semantic-paper-sheet);
10
+ border: 1px solid var(--semantic-divider-divider-1);
11
+ width: 100%;
12
+ max-width: 400px;
13
+
14
+ @include tablet {
15
+ max-width: none;
16
+ }
17
+ }
18
+
19
+ .ck-snackbar-content {
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 8px;
23
+ flex: 1;
24
+ min-width: 0;
25
+ }
26
+
27
+ .ck-snackbar-check {
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ width: 24px;
32
+ height: 24px;
33
+ flex-shrink: 0;
34
+ color: var(--semantic-information-success);
35
+ }
36
+
37
+ .ck-snackbar-message {
38
+ @include textToken('body3-l1-medium');
39
+ color: var(--scale-gray-900);
40
+ flex: 1;
41
+ min-width: 0;
42
+ }
43
+
44
+ .ck-root .ck-snackbar-close {
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ flex-shrink: 0;
49
+ color: var(--scale-gray-700);
50
+ cursor: pointer;
51
+ }
@@ -0,0 +1,27 @@
1
+ import './style.scss';
2
+ import { type FC } from 'react';
3
+
4
+ interface ToggleProps {
5
+ checked: boolean;
6
+ label: string;
7
+ onChange: (checked: boolean) => void;
8
+ }
9
+
10
+ export const Toggle: FC<ToggleProps> = ({ checked, label, onChange }) => {
11
+ return (
12
+ <label className="ck-toggle">
13
+ <input
14
+ className="ck-toggle-input"
15
+ type="checkbox"
16
+ role="switch"
17
+ checked={checked}
18
+ onChange={(e) => onChange(e.target.checked)}
19
+ aria-label={label}
20
+ />
21
+ <span className="ck-toggle-track" aria-hidden="true">
22
+ <span className="ck-toggle-thumb" />
23
+ </span>
24
+ <span className="ck-toggle-label">{label}</span>
25
+ </label>
26
+ );
27
+ };
@@ -0,0 +1,53 @@
1
+ @use '../../style/mixin.scss' as *;
2
+
3
+ .ck-toggle {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: 6px;
7
+ cursor: pointer;
8
+ flex-shrink: 0;
9
+ }
10
+
11
+ .ck-toggle-input {
12
+ position: absolute;
13
+ opacity: 0;
14
+ width: 0;
15
+ height: 0;
16
+ pointer-events: none;
17
+ }
18
+
19
+ .ck-toggle-track {
20
+ position: relative;
21
+ width: 40px;
22
+ height: 24px;
23
+ border-radius: 100px;
24
+ background: var(--scale-gray-400);
25
+ transition: background 0.2s;
26
+ flex-shrink: 0;
27
+
28
+ .ck-toggle-input:checked + & {
29
+ background: var(--semantic-brand-trugi-primary);
30
+ }
31
+ }
32
+
33
+ .ck-toggle-thumb {
34
+ position: absolute;
35
+ top: 3px;
36
+ left: 2.4px;
37
+ width: 19.2px;
38
+ height: 19.2px;
39
+ border-radius: 50%;
40
+ background: var(--static-white-main);
41
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
42
+ transition: transform 0.2s;
43
+
44
+ .ck-toggle-input:checked ~ .ck-toggle-track & {
45
+ transform: translateX(16px);
46
+ }
47
+ }
48
+
49
+ .ck-toggle-label {
50
+ @include textToken('body4-l2-regular');
51
+ color: var(--scale-gray-700);
52
+ white-space: nowrap;
53
+ }
@@ -0,0 +1,64 @@
1
+ import { type FC } from 'react';
2
+ import './style.scss';
3
+ import { type WCWallet } from '../../types';
4
+ import wcBadge from '../../../assets/wc-search.png';
5
+
6
+ interface WalletGridProps {
7
+ wallets: WCWallet[];
8
+ isLoading: boolean;
9
+ onSelect: (wallet: WCWallet) => void;
10
+ }
11
+
12
+ export const WalletGrid: FC<WalletGridProps> = ({ wallets, isLoading, onSelect }) => {
13
+ if (isLoading) {
14
+ return (
15
+ <div className="ck-wallet-grid-scroll">
16
+ <ul className="ck-wallet-grid ck-wallet-grid--loading" role="list" aria-busy="true">
17
+ {Array.from({ length: 8 }).map((_, i) => (
18
+ <li key={i} className="ck-wallet-grid-item">
19
+ <span className="ck-wallet-grid-icon ck-wallet-grid-icon--skeleton" />
20
+ <span className="ck-wallet-grid-name ck-wallet-grid-name--skeleton" />
21
+ </li>
22
+ ))}
23
+ </ul>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ return (
29
+ <div className="ck-wallet-grid-scroll">
30
+ <ul className="ck-wallet-grid" role="list">
31
+ {wallets.map((wallet) => (
32
+ <li key={wallet.id}>
33
+ <button type="button" className="ck-wallet-grid-item" onClick={() => onSelect(wallet)}>
34
+ {wallet.imageUrl ? (
35
+ <img
36
+ className="ck-wallet-grid-icon"
37
+ src={wallet.imageUrl}
38
+ alt=""
39
+ aria-hidden="true"
40
+ />
41
+ ) : (
42
+ <span className="ck-wallet-grid-icon" aria-hidden="true" />
43
+ )}
44
+ <span className="ck-wallet-grid-name-wrap">
45
+ <span className="ck-wallet-grid-name">{wallet.name}</span>
46
+ {(wallet.mobileNative ||
47
+ wallet.mobileUniversal ||
48
+ wallet.desktopNative ||
49
+ wallet.desktopUniversal) && (
50
+ <img
51
+ className="ck-wallet-grid-wc-badge"
52
+ src={wcBadge}
53
+ alt=""
54
+ aria-hidden="true"
55
+ />
56
+ )}
57
+ </span>
58
+ </button>
59
+ </li>
60
+ ))}
61
+ </ul>
62
+ </div>
63
+ );
64
+ };
@@ -0,0 +1,108 @@
1
+ @use '../../style/mixin.scss' as *;
2
+
3
+ .ck-wallet-grid-scroll {
4
+ flex: 1;
5
+ min-height: 0;
6
+ overflow-y: auto;
7
+ overflow-x: hidden;
8
+ width: 100%;
9
+ @include themedScrollbar;
10
+ }
11
+
12
+ .ck-wallet-grid {
13
+ display: grid;
14
+ grid-template-columns: repeat(4, minmax(0, 1fr));
15
+ grid-auto-rows: auto;
16
+ column-gap: 16px;
17
+ row-gap: 16px;
18
+ list-style: none;
19
+ margin: 0;
20
+ padding: 0;
21
+ width: 100%;
22
+
23
+ @include small-mobile {
24
+ row-gap: 3px;
25
+ }
26
+ @include max-width(392px) {
27
+ grid-template-columns: repeat(3, minmax(0, 1fr));
28
+ }
29
+ }
30
+
31
+ .ck-root .ck-wallet-grid-item {
32
+ display: flex;
33
+ flex-direction: column;
34
+ align-items: center;
35
+ justify-content: center;
36
+ gap: 6px;
37
+ padding: 20px 12px;
38
+ width: 100%;
39
+ cursor: pointer;
40
+ }
41
+
42
+ .ck-wallet-grid-icon {
43
+ width: 54px;
44
+ height: 54px;
45
+ max-width: none;
46
+ border-radius: 12px;
47
+ flex-shrink: 0;
48
+ background: var(--scale-gray-200);
49
+ overflow: hidden;
50
+ cursor: pointer;
51
+
52
+ img {
53
+ width: 100%;
54
+ height: 100%;
55
+ object-fit: cover;
56
+ display: block;
57
+ }
58
+ }
59
+
60
+ .ck-wallet-grid-name-wrap {
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ gap: 2px;
65
+ width: 100%;
66
+ min-width: 0;
67
+ }
68
+
69
+ .ck-wallet-grid-name {
70
+ @include textToken('label6-bold');
71
+ color: var(--scale-gray-900);
72
+ text-align: center;
73
+ overflow: hidden;
74
+ text-overflow: ellipsis;
75
+ white-space: nowrap;
76
+ min-width: 0;
77
+ flex-shrink: 1;
78
+ }
79
+
80
+ .ck-wallet-grid-wc-badge {
81
+ width: 16px;
82
+ height: 16px;
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ // ── Skeleton ──────────────────────────────────────────────────────────────────
87
+ .ck-wallet-grid-loading .ck-wallet-grid-item {
88
+ cursor: default;
89
+ }
90
+
91
+ .ck-wallet-grid-icon-skeleton {
92
+ background: var(--scale-gray-100);
93
+ animation: ck-grid-pulse 1.5s ease-in-out infinite;
94
+ }
95
+
96
+ .ck-wallet-grid-name-skeleton {
97
+ height: 12px;
98
+ border-radius: 4px;
99
+ background: var(--scale-gray-100);
100
+ width: 60%;
101
+ margin: 0 auto;
102
+ animation: ck-grid-pulse 1.5s ease-in-out infinite;
103
+ }
104
+
105
+ @keyframes ck-grid-pulse {
106
+ 0%, 100% { opacity: 1; }
107
+ 50% { opacity: 0.4; }
108
+ }