@zerodev/wallet-react-ui 0.0.12 → 0.0.13
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/dist/_types/auth/authStoreSlice.d.ts +31 -0
- package/dist/_types/auth/authStoreSlice.d.ts.map +1 -1
- package/dist/_types/auth/components/WalletSheet/index.d.ts.map +1 -1
- package/dist/_types/auth/hooks/useAuth.d.ts +5 -0
- package/dist/_types/auth/hooks/useAuth.d.ts.map +1 -1
- package/dist/_types/auth/index.d.ts.map +1 -1
- package/dist/_types/auth/pages/SignUp/InstalledWallets.d.ts.map +1 -1
- package/dist/_types/auth/pages/SignUp/MoreWallets.d.ts.map +1 -1
- package/dist/_types/auth/pages/SignUp/Wallet.d.ts.map +1 -1
- package/dist/_types/auth/pages/Verifying.d.ts.map +1 -1
- package/dist/_types/auth/pages/WalletConnecting.d.ts +11 -0
- package/dist/_types/auth/pages/WalletConnecting.d.ts.map +1 -0
- package/dist/_types/auth/types.d.ts +1 -1
- package/dist/_types/auth/types.d.ts.map +1 -1
- package/dist/_types/auth/utils/isCancellationError.d.ts +4 -0
- package/dist/_types/auth/utils/isCancellationError.d.ts.map +1 -1
- package/dist/_types/auth/utils/isResourceUnavailableError.d.ts +12 -0
- package/dist/_types/auth/utils/isResourceUnavailableError.d.ts.map +1 -0
- package/dist/_types/connector.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +872 -723
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/auth/authStoreSlice.ts +72 -3
- package/src/auth/components/WalletSheet/index.tsx +9 -18
- package/src/auth/hooks/useAuth.ts +7 -0
- package/src/auth/index.tsx +3 -0
- package/src/auth/pages/SignUp/InstalledWallets.tsx +18 -28
- package/src/auth/pages/SignUp/MoreWallets.tsx +14 -26
- package/src/auth/pages/SignUp/Wallet.tsx +11 -27
- package/src/auth/pages/Verifying.tsx +28 -8
- package/src/auth/pages/WalletConnecting.tsx +237 -0
- package/src/auth/types.ts +1 -0
- package/src/auth/utils/isCancellationError.ts +20 -1
- package/src/auth/utils/isResourceUnavailableError.ts +29 -0
- package/src/connector.ts +13 -4
- package/src/shared/components/StatusScreen/index.tsx +1 -1
- package/dist/_types/shared/components/TokenSummary/index.d.ts +0 -18
- package/dist/_types/shared/components/TokenSummary/index.d.ts.map +0 -1
- package/src/shared/components/TokenSummary/index.tsx +0 -50
|
@@ -15,6 +15,7 @@ export function Verifying() {
|
|
|
15
15
|
useAuth()
|
|
16
16
|
const [code] = useState<string | null>(getCodeFromUrl)
|
|
17
17
|
const [error, setError] = useState<Error | null>(null)
|
|
18
|
+
const [sessionMissing, setSessionMissing] = useState(false)
|
|
18
19
|
|
|
19
20
|
// ref to prevent useEffect firing twice in dev's StrictMode
|
|
20
21
|
const hasVerifiedRef = useRef(false)
|
|
@@ -35,14 +36,15 @@ export function Verifying() {
|
|
|
35
36
|
if (hasVerifiedRef.current || !code) return
|
|
36
37
|
hasVerifiedRef.current = true
|
|
37
38
|
|
|
38
|
-
// No active OTP session —
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// already-authenticated
|
|
39
|
+
// No active OTP session — the link was already used, expired, or opened
|
|
40
|
+
// in a browser that never started the email flow. Skip the (doomed)
|
|
41
|
+
// mutation and show the expired-link error below instead of silently
|
|
42
|
+
// dropping the step: a blank screen gives the user nothing to act on.
|
|
43
|
+
// The already-authenticated re-tap case is covered too — hosts route
|
|
44
|
+
// connected users away on their own (wagmi reconnect), so the error only
|
|
45
|
+
// stays up for users who genuinely can't be verified.
|
|
43
46
|
if (!otpId || !otpEncryptionTargetBundle) {
|
|
44
|
-
|
|
45
|
-
goToStep(null)
|
|
47
|
+
setSessionMissing(true)
|
|
46
48
|
return
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -58,7 +60,25 @@ export function Verifying() {
|
|
|
58
60
|
</StatusScreen>
|
|
59
61
|
)}
|
|
60
62
|
|
|
61
|
-
{!error &&
|
|
63
|
+
{!error && sessionMissing && (
|
|
64
|
+
<>
|
|
65
|
+
<StatusScreen imageName="error" title="Link Expired">
|
|
66
|
+
This verification link has expired or was already used.
|
|
67
|
+
<br />
|
|
68
|
+
Please sign in again to request a new one.
|
|
69
|
+
</StatusScreen>
|
|
70
|
+
<Button
|
|
71
|
+
action="primary"
|
|
72
|
+
onClick={() => {
|
|
73
|
+
stripMagicLinkCodeFromUrl()
|
|
74
|
+
goToStep('sign-up')
|
|
75
|
+
}}
|
|
76
|
+
text="Choose another sign-in method"
|
|
77
|
+
/>
|
|
78
|
+
</>
|
|
79
|
+
)}
|
|
80
|
+
|
|
81
|
+
{!error && !sessionMissing && !code && !isVerificationLoading && (
|
|
62
82
|
<>
|
|
63
83
|
<StatusScreen imageName="error" title="Invalid Link">
|
|
64
84
|
This verification link is invalid or incomplete.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { type Config, connect } from '@wagmi/core'
|
|
2
|
+
import { Button, PoweredBy } from '@zerodev/react-ui'
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
import { useConfig, useConnectors } from 'wagmi'
|
|
5
|
+
import { StatusScreen } from '../../shared/components/StatusScreen'
|
|
6
|
+
import { useKitStore } from '../../shared/hooks/useKitStore'
|
|
7
|
+
import type { ConnectFailure } from '../authStoreSlice'
|
|
8
|
+
import { useAuth } from '../hooks/useAuth'
|
|
9
|
+
import { isCancellationError } from '../utils/isCancellationError'
|
|
10
|
+
import { isResourceUnavailableError } from '../utils/isResourceUnavailableError'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Connector uids with a connect() still open, per wagmi config. Outside React
|
|
14
|
+
* because the request outlives this component, and keyed by connector because
|
|
15
|
+
* several wallets can be left unanswered at once.
|
|
16
|
+
*/
|
|
17
|
+
const openRequests = new WeakMap<Config, Set<string>>()
|
|
18
|
+
|
|
19
|
+
function openFor(config: Config): Set<string> {
|
|
20
|
+
let uids = openRequests.get(config)
|
|
21
|
+
if (!uids) {
|
|
22
|
+
uids = new Set()
|
|
23
|
+
openRequests.set(config, uids)
|
|
24
|
+
}
|
|
25
|
+
return uids
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function describeConnectError(
|
|
29
|
+
err: unknown,
|
|
30
|
+
walletName: string,
|
|
31
|
+
): ConnectFailure {
|
|
32
|
+
if (isCancellationError(err)) {
|
|
33
|
+
return {
|
|
34
|
+
title: 'Request declined',
|
|
35
|
+
message: `You declined the connection request in ${walletName}.`,
|
|
36
|
+
pending: false,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (isResourceUnavailableError(err)) {
|
|
40
|
+
// Usually nothing failed: the wallet still has the first request open
|
|
41
|
+
// and will not prompt again until it is answered there. But -32002 is
|
|
42
|
+
// only "resource unavailable", so suggest the outstanding request rather
|
|
43
|
+
// than assert it. Not the wallet's own message: MetaMask's names RPC
|
|
44
|
+
// methods and the origin ("Request of type 'wallet_requestPermissions'
|
|
45
|
+
// already pending for origin…") — Reown shows its own copy here too.
|
|
46
|
+
return {
|
|
47
|
+
title: `Check ${walletName}`,
|
|
48
|
+
message:
|
|
49
|
+
`Open ${walletName} and check for an outstanding request. ` +
|
|
50
|
+
'If one is waiting, approve or dismiss it, then try again.',
|
|
51
|
+
pending: true,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Wallets throw raw JSON-RPC objects, viem throws Errors with
|
|
55
|
+
// `shortMessage`. Never render "[object Object]": fall back to a fixed
|
|
56
|
+
// sentence plus the code, the only actionable detail such an object has.
|
|
57
|
+
let message: string | undefined
|
|
58
|
+
let code: number | undefined
|
|
59
|
+
if (typeof err === 'string' && err.length > 0) {
|
|
60
|
+
message = err
|
|
61
|
+
} else if (typeof err === 'object' && err !== null) {
|
|
62
|
+
const e = err as {
|
|
63
|
+
shortMessage?: unknown
|
|
64
|
+
message?: unknown
|
|
65
|
+
code?: unknown
|
|
66
|
+
}
|
|
67
|
+
const text = e.shortMessage ?? e.message
|
|
68
|
+
if (typeof text === 'string' && text.length > 0) message = text
|
|
69
|
+
if (typeof e.code === 'number') code = e.code
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
title: 'Couldn’t connect',
|
|
73
|
+
message:
|
|
74
|
+
message ??
|
|
75
|
+
`Something went wrong while connecting to ${walletName}. Please try again.` +
|
|
76
|
+
(code !== undefined ? ` (code ${code})` : ''),
|
|
77
|
+
pending: false,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The `wallet-connecting` step: shown from picking an external wallet until it
|
|
83
|
+
* answers, which a locked or dismissed wallet never does — so leaving must
|
|
84
|
+
* always be possible.
|
|
85
|
+
*
|
|
86
|
+
* This page owns the connect() call, since React Query drops per-call
|
|
87
|
+
* callbacks when the sign-up page unmounts on the step change. The wallet
|
|
88
|
+
* button only records intent (`startWalletConnection`).
|
|
89
|
+
*/
|
|
90
|
+
export function WalletConnecting() {
|
|
91
|
+
const {
|
|
92
|
+
pendingWallet,
|
|
93
|
+
connectError,
|
|
94
|
+
setConnectError,
|
|
95
|
+
clearPendingWallet,
|
|
96
|
+
goToStep,
|
|
97
|
+
} = useAuth()
|
|
98
|
+
const connectors = useConnectors()
|
|
99
|
+
const config = useConfig()
|
|
100
|
+
// Read outside React: the wallet can answer long after this unmounts.
|
|
101
|
+
const store = useKitStore()
|
|
102
|
+
|
|
103
|
+
const connector = pendingWallet
|
|
104
|
+
? connectors.find((c) => c.uid === pendingWallet.connectorUid)
|
|
105
|
+
: undefined
|
|
106
|
+
const walletName = pendingWallet?.name ?? 'your wallet'
|
|
107
|
+
|
|
108
|
+
// Pop, don't push: pushing would leave `wallet-connecting` in the history,
|
|
109
|
+
// and sign-up's back arrow would remount this page and connect() again.
|
|
110
|
+
const leave = () => {
|
|
111
|
+
const auth = store.getState().auth
|
|
112
|
+
if (auth.stepHistory.length > 0) auth.goBack()
|
|
113
|
+
else auth.goToStep('sign-up')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const close = () => {
|
|
117
|
+
clearPendingWallet()
|
|
118
|
+
goToStep(null)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const attempt = () => {
|
|
122
|
+
// Starting an attempt means waiting again: drop the previous outcome so
|
|
123
|
+
// Try again shows the wallet rather than the failure it replaces.
|
|
124
|
+
setConnectError(null)
|
|
125
|
+
|
|
126
|
+
// Single-flight per wallet: a wallet rejects a second request rather than
|
|
127
|
+
// re-prompting, so re-adopt the open one — its promise still drives this
|
|
128
|
+
// screen.
|
|
129
|
+
if (connector && openFor(config).has(connector.uid)) return
|
|
130
|
+
|
|
131
|
+
// Connector gone from the config: nothing to connect to.
|
|
132
|
+
if (!connector) {
|
|
133
|
+
clearPendingWallet()
|
|
134
|
+
leave()
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
// Already the current connection: connect() would throw rather than
|
|
138
|
+
// prompt (its exact guard), so there is nothing to wait for. A connector
|
|
139
|
+
// that is connected but not current falls through on purpose — connect()
|
|
140
|
+
// then resolves silently and promotes it to current.
|
|
141
|
+
if (config.state.current === connector.uid) {
|
|
142
|
+
close()
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// @wagmi/core's connect(), not useConnect's mutate: a promise keeps its
|
|
147
|
+
// handlers across remounts (Strict Mode) and after unmount, so a wallet
|
|
148
|
+
// answered once the user left still closes the widget.
|
|
149
|
+
const open = openFor(config)
|
|
150
|
+
open.add(connector.uid)
|
|
151
|
+
connect(config, { connector }).then(
|
|
152
|
+
() => {
|
|
153
|
+
open.delete(connector.uid)
|
|
154
|
+
close()
|
|
155
|
+
},
|
|
156
|
+
(err: unknown) => {
|
|
157
|
+
open.delete(connector.uid)
|
|
158
|
+
// The abandoned prompt is often dismissed much later, by which point
|
|
159
|
+
// the user has moved on. Act only while the flow is still waiting on
|
|
160
|
+
// THIS wallet. (Approval is not gated: the wallet is then connected,
|
|
161
|
+
// so ending the flow is right wherever they are.)
|
|
162
|
+
const auth = store.getState().auth
|
|
163
|
+
if (
|
|
164
|
+
auth.step !== 'wallet-connecting' ||
|
|
165
|
+
auth.pendingWallet?.connectorUid !== connector.uid
|
|
166
|
+
) {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
setConnectError(describeConnectError(err, walletName))
|
|
170
|
+
},
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// wagmi can connect without our connect() promise resolving: after a reload
|
|
175
|
+
// the wallet's queued request belongs to a dead page, and approving it
|
|
176
|
+
// authorises the site through the connector's own events. Watch the store so
|
|
177
|
+
// the flow still closes.
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (!connector) return
|
|
180
|
+
const uid = connector.uid
|
|
181
|
+
return config.subscribe(
|
|
182
|
+
(state) => (state.status === 'connected' ? state.current : null),
|
|
183
|
+
(currentUid) => {
|
|
184
|
+
if (!currentUid) return
|
|
185
|
+
const connected = config.state.connections.get(currentUid)?.connector
|
|
186
|
+
if (connected?.uid !== uid) return
|
|
187
|
+
const auth = store.getState().auth
|
|
188
|
+
auth.clearPendingWallet()
|
|
189
|
+
auth.goToStep(null)
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
}, [connector, config, store])
|
|
193
|
+
|
|
194
|
+
// Once on mount; the ref guards Strict Mode's double effect run.
|
|
195
|
+
const started = useRef(false)
|
|
196
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: mount-only kick; `attempt` is intentionally the mount-time closure
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (started.current) return
|
|
199
|
+
started.current = true
|
|
200
|
+
attempt()
|
|
201
|
+
}, [])
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<>
|
|
205
|
+
<div className="zd:flex-1 zd:flex zd:flex-col zd:gap-8 zd:items-center zd:justify-center">
|
|
206
|
+
{connectError === null ? (
|
|
207
|
+
<StatusScreen imageName="loading" title={`Waiting for ${walletName}`}>
|
|
208
|
+
Approve or reject the request in your wallet.
|
|
209
|
+
<br />
|
|
210
|
+
Nothing showing? Open the wallet, the request may be waiting behind
|
|
211
|
+
its unlock screen.
|
|
212
|
+
</StatusScreen>
|
|
213
|
+
) : (
|
|
214
|
+
<StatusScreen
|
|
215
|
+
imageName={connectError.pending ? 'loading' : 'error'}
|
|
216
|
+
title={connectError.title}
|
|
217
|
+
>
|
|
218
|
+
{connectError.message}
|
|
219
|
+
</StatusScreen>
|
|
220
|
+
)}
|
|
221
|
+
{/* EIP-1193 has no cancel, so the label promises only what it does. */}
|
|
222
|
+
<div className="zd:flex zd:flex-col zd:gap-1">
|
|
223
|
+
{connectError !== null && (
|
|
224
|
+
<Button action="primary" text="Try again" onClick={attempt} />
|
|
225
|
+
)}
|
|
226
|
+
<Button
|
|
227
|
+
action="secondary"
|
|
228
|
+
text="Choose another sign-in method"
|
|
229
|
+
onClick={leave}
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<PoweredBy className="zd:self-center zd:pt-4 zd:pb-6" />
|
|
235
|
+
</>
|
|
236
|
+
)
|
|
237
|
+
}
|
package/src/auth/types.ts
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The user cancelled, rather than something failed: passkey prompt dismissed,
|
|
3
|
+
* OAuth popup closed, or a wallet connection request rejected.
|
|
4
|
+
*/
|
|
1
5
|
export function isCancellationError(err: unknown): boolean {
|
|
6
|
+
// EIP-1193 `4001` "user rejected", on any object: viem's
|
|
7
|
+
// UserRejectedRequestError, a wallet's own ProviderRpcError (an Error with
|
|
8
|
+
// `code`), or wagmi's rethrown raw JSON-RPC object. Walk `.cause`.
|
|
9
|
+
let current: unknown = err
|
|
10
|
+
for (let depth = 0; depth < 5 && isObject(current); depth++) {
|
|
11
|
+
if (current.code === 4001) return true
|
|
12
|
+
current = current.cause
|
|
13
|
+
}
|
|
14
|
+
|
|
2
15
|
if (!(err instanceof Error)) return false
|
|
3
16
|
// WebAuthn / passkey
|
|
4
17
|
if (err.name === 'AbortError' || err.name === 'NotAllowedError') return true
|
|
5
|
-
//
|
|
18
|
+
// viem's rejection error when `code` was dropped
|
|
6
19
|
if (err.name === 'UserRejectedRequestError') return true
|
|
7
20
|
// OAuth (existing logic, message-based)
|
|
8
21
|
const msg = err.message.toLowerCase()
|
|
9
22
|
return msg.includes('oauth popup was closed')
|
|
10
23
|
}
|
|
24
|
+
|
|
25
|
+
function isObject(
|
|
26
|
+
value: unknown,
|
|
27
|
+
): value is { code?: unknown; cause?: unknown } {
|
|
28
|
+
return typeof value === 'object' && value !== null
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EIP-1474 `-32002` "Resource unavailable". Injected wallets send it when a
|
|
3
|
+
* request for this origin is already open (popup closed without answering,
|
|
4
|
+
* then clicked again) and they will not prompt again until it is answered —
|
|
5
|
+
* wagmi's injected connector reads the code the same way ("prompt is already
|
|
6
|
+
* open").
|
|
7
|
+
*
|
|
8
|
+
* viem wraps it as `ResourceUnavailableRpcError`; wagmi's injected connector
|
|
9
|
+
* rethrows the raw JSON-RPC object. Accept both and walk `.cause`.
|
|
10
|
+
*/
|
|
11
|
+
export function isResourceUnavailableError(err: unknown): boolean {
|
|
12
|
+
let current: unknown = err
|
|
13
|
+
for (let depth = 0; depth < 5 && isObject(current); depth++) {
|
|
14
|
+
if (
|
|
15
|
+
current.code === -32002 ||
|
|
16
|
+
current.name === 'ResourceUnavailableRpcError'
|
|
17
|
+
) {
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
current = current.cause
|
|
21
|
+
}
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isObject(
|
|
26
|
+
value: unknown,
|
|
27
|
+
): value is { code?: unknown; name?: unknown; cause?: unknown } {
|
|
28
|
+
return typeof value === 'object' && value !== null
|
|
29
|
+
}
|
package/src/connector.ts
CHANGED
|
@@ -106,16 +106,25 @@ export function zeroDevWallet(
|
|
|
106
106
|
},
|
|
107
107
|
|
|
108
108
|
async setup() {
|
|
109
|
+
// Restore a persisted OTP session BEFORE base setup so an email flow
|
|
110
|
+
// survives a reload. The restore is a synchronous localStorage read;
|
|
111
|
+
// base setup does async work (wallet creation, session validation)
|
|
112
|
+
// that can be slow or fail. Magic-link verification races on this —
|
|
113
|
+
// `Verifying` reads the session once on mount, and gating the restore
|
|
114
|
+
// behind base setup made it see an empty session and strip the code
|
|
115
|
+
// from the URL. Restoring first runs synchronously inside wagmi's
|
|
116
|
+
// createConfig, before any React effect can observe the store.
|
|
117
|
+
if (typeof window !== 'undefined') {
|
|
118
|
+
store.getState().auth.initialize()
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
await connector.setup?.()
|
|
110
122
|
|
|
111
123
|
// Everything below is browser-only. Skip during SSR — getProvider()
|
|
112
124
|
// touches `window` for EIP-6963 discovery and would crash on the
|
|
113
|
-
// server
|
|
125
|
+
// server.
|
|
114
126
|
if (typeof window === 'undefined') return
|
|
115
127
|
|
|
116
|
-
// Restore a persisted OTP session so an email flow survives a reload.
|
|
117
|
-
store.getState().auth.initialize()
|
|
118
|
-
|
|
119
128
|
// Signing is pinned to background mode: the prompt-mode confirmation UI
|
|
120
129
|
// is not part of this package's public surface, so requests always pass
|
|
121
130
|
// through without gating. The wrapping logic below is retained but
|
|
@@ -34,7 +34,7 @@ export function StatusScreen({
|
|
|
34
34
|
<img
|
|
35
35
|
src={STATE_IMAGES[imageName]}
|
|
36
36
|
alt={imageName}
|
|
37
|
-
className="zd:w-[118px] zd:h-[118px] zd:bg-transparent"
|
|
37
|
+
className="zd:w-[118px] zd:h-[118px] zd:object-contain zd:bg-transparent"
|
|
38
38
|
/>
|
|
39
39
|
<div className="zd:flex zd:flex-col zd:gap-4 zd:items-center">
|
|
40
40
|
<Text className="zd:text-h2 zd:text-center zd:whitespace-pre-wrap">
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export interface TokenSummaryProps {
|
|
2
|
-
/** Token logo URL, shown in the tile floating over the card's top edge.
|
|
3
|
-
* The tile renders empty (neutral background) when omitted. */
|
|
4
|
-
tokenLogoUrl?: string;
|
|
5
|
-
/** Pre-formatted fiat value, e.g. `"+$170.27"`. */
|
|
6
|
-
fiatValue: string;
|
|
7
|
-
/** Pre-formatted token amount, e.g. `"0.0652 ETH"`. */
|
|
8
|
-
cryptoAmount: string;
|
|
9
|
-
className?: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Portfolio-value hero (Figma "Portfolio Value", `15873:58532`): a card
|
|
13
|
-
* headed by a token logo tile that overhangs the top edge, with the fiat
|
|
14
|
-
* value large and the crypto amount beneath. The overhang headroom is part
|
|
15
|
-
* of the component, so consumers can stack it without clipping the tile.
|
|
16
|
-
*/
|
|
17
|
-
export declare function TokenSummary({ tokenLogoUrl, fiatValue, cryptoAmount, className, }: TokenSummaryProps): import("react").JSX.Element;
|
|
18
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/shared/components/TokenSummary/index.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,GACV,EAAE,iBAAiB,+BAyBnB"}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { cn, Text, Wrapper } from '@zerodev/react-ui'
|
|
2
|
-
|
|
3
|
-
export interface TokenSummaryProps {
|
|
4
|
-
/** Token logo URL, shown in the tile floating over the card's top edge.
|
|
5
|
-
* The tile renders empty (neutral background) when omitted. */
|
|
6
|
-
tokenLogoUrl?: string
|
|
7
|
-
/** Pre-formatted fiat value, e.g. `"+$170.27"`. */
|
|
8
|
-
fiatValue: string
|
|
9
|
-
/** Pre-formatted token amount, e.g. `"0.0652 ETH"`. */
|
|
10
|
-
cryptoAmount: string
|
|
11
|
-
className?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Portfolio-value hero (Figma "Portfolio Value", `15873:58532`): a card
|
|
16
|
-
* headed by a token logo tile that overhangs the top edge, with the fiat
|
|
17
|
-
* value large and the crypto amount beneath. The overhang headroom is part
|
|
18
|
-
* of the component, so consumers can stack it without clipping the tile.
|
|
19
|
-
*/
|
|
20
|
-
export function TokenSummary({
|
|
21
|
-
tokenLogoUrl,
|
|
22
|
-
fiatValue,
|
|
23
|
-
cryptoAmount,
|
|
24
|
-
className,
|
|
25
|
-
}: TokenSummaryProps) {
|
|
26
|
-
return (
|
|
27
|
-
<div className={cn('zd:relative zd:w-full zd:pt-7', className)}>
|
|
28
|
-
<Wrapper
|
|
29
|
-
variant="ghost"
|
|
30
|
-
className="zd:flex zd:w-full zd:flex-col zd:items-center zd:gap-2 zd:rounded-2xl zd:px-2 zd:pt-16 zd:pb-2"
|
|
31
|
-
>
|
|
32
|
-
<Text className="zd:whitespace-nowrap zd:text-h1">{fiatValue}</Text>
|
|
33
|
-
<Text className="zd:whitespace-nowrap zd:text-center zd:text-h3">
|
|
34
|
-
{cryptoAmount}
|
|
35
|
-
</Text>
|
|
36
|
-
</Wrapper>
|
|
37
|
-
{/* Token tile — straddles the card's top edge, centred. */}
|
|
38
|
-
<div className="zd:absolute zd:top-0 zd:left-1/2 zd:flex zd:size-18.5 zd:-translate-x-1/2 zd:items-center zd:justify-center zd:rounded-3xl zd:border zd:border-white/20 zd:bg-offWhite/80 zd:backdrop-blur-[15px]">
|
|
39
|
-
{tokenLogoUrl && (
|
|
40
|
-
<img
|
|
41
|
-
src={tokenLogoUrl}
|
|
42
|
-
alt=""
|
|
43
|
-
aria-hidden
|
|
44
|
-
className="zd:size-10.5 zd:object-contain"
|
|
45
|
-
/>
|
|
46
|
-
)}
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
)
|
|
50
|
-
}
|