Skip to content

Commit 9f0e69e

Browse files
author
Aleksandar Cakalic
authored
Merge pull request #195 from argentlabs/feat/snreact-wrapper
Feat: starknet-react wrapper component
2 parents e345d6a + d66108c commit 9f0e69e

File tree

16 files changed

+1154
-315
lines changed

16 files changed

+1154
-315
lines changed

package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@
8181
"types": "./dist/window.d.ts",
8282
"import": "./dist/window.js",
8383
"require": "./dist/window.cjs"
84+
},
85+
"./starknet-react": {
86+
"types": "./dist/starknet-react.d.ts",
87+
"import": "./dist/starknet-react.js",
88+
"require": "./dist/starknet-react.cjs"
8489
}
8590
},
8691
"main": "./dist/starknetkit.cjs",
@@ -107,6 +112,8 @@
107112
"eventemitter3": "^5.0.1",
108113
"events": "^3.3.0",
109114
"lodash-es": "^4.17.21",
115+
"react": "^18.2.0",
116+
"react-dom": "^18.2.0",
110117
"svelte-forms": "^2.3.1",
111118
"trpc-browser": "^1.3.2"
112119
},
@@ -119,8 +126,11 @@
119126
"@types/async-retry": "^1.4.5",
120127
"@types/lodash-es": "^4.17.8",
121128
"@types/node": "^20.5.7",
122-
"@typescript-eslint/parser": "^8.8.1",
129+
"@types/react": "^18.2.0",
130+
"@types/react-dom": "^18.2.0",
123131
"@typescript-eslint/eslint-plugin": "^8.0.0",
132+
"@typescript-eslint/parser": "^8.8.1",
133+
"@vitejs/plugin-react": "^4.3.4",
124134
"@walletconnect/ethereum-provider": "^2.11.0",
125135
"@walletconnect/jsonrpc-provider": "^1.0.13",
126136
"@walletconnect/signer-connection": "^2.11.0",

pnpm-lock.yaml

Lines changed: 547 additions & 308 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/connectors/argent/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class Argent
4646
this.argentX = new ArgentX({
4747
icon: injectedWalletIcons.argentX,
4848
...settings?.extension,
49+
shouldEmit: true,
4950
})
5051
this.argentMobile = new ArgentMobileBaseConnector({
5152
...settings.mobile,

src/connectors/connector.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface ConnectorEvents {
3030
connect(data: ConnectorData): void
3131
/** Emitted when connection is lost. */
3232
disconnect(): void
33+
/** Emitted when `shouldEmit` is true, used for StarknetReactWrapper */
34+
connectionStatus(s: "init" | "success" | "fail" | "fallback"): void
3335
}
3436

3537
export type ConnectArgs = {

src/connectors/injected/index.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export interface InjectedConnectorOptions {
3535
name?: string
3636
/** Wallet icons. */
3737
icon?: ConnectorIcons
38+
/** Used for StarknetReactWrapper/Argent connector + sn-react */
39+
shouldEmit?: boolean
3840
}
3941

4042
// Icons used when the injected wallet is not installed
@@ -62,7 +64,8 @@ export const injectedWalletIcons = {
6264
},
6365
fordefi: {
6466
dark: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzEzNDk0XzY2MjU0KSI+CjxwYXRoIGQ9Ik0xMC44NzY5IDE1LjYzNzhIMS41VjE4LjM5OUMxLjUgMTkuODAxMyAyLjYzNDQ3IDIwLjkzOCA0LjAzMzkyIDIwLjkzOEg4LjI0OTkyTDEwLjg3NjkgMTUuNjM3OFoiIGZpbGw9IiM3OTk0RkYiLz4KPHBhdGggZD0iTTEuNSA5Ljc3NTUxSDE5LjA1MTZMMTcuMDEzOSAxMy44NzExSDEuNVY5Ljc3NTUxWiIgZmlsbD0iIzQ4NkRGRiIvPgo8cGF0aCBkPSJNNy42NTk5NiAzSDEuNTI0NDFWOC4wMDcwNEgyMi40NjEyVjNIMTYuMzI1NlY2LjczOTQ0SDE1LjA2MDZWM0g4LjkyNTAyVjYuNzM5NDRINy42NTk5NlYzWiIgZmlsbD0iIzVDRDFGQSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzEzNDk0XzY2MjU0Ij4KPHJlY3Qgd2lkdGg9IjIxIiBoZWlnaHQ9IjE4IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS41IDMpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==",
65-
light: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzEzNDk0XzY2MjU0KSI+CjxwYXRoIGQ9Ik0xMC44NzY5IDE1LjYzNzhIMS41VjE4LjM5OUMxLjUgMTkuODAxMyAyLjYzNDQ3IDIwLjkzOCA0LjAzMzkyIDIwLjkzOEg4LjI0OTkyTDEwLjg3NjkgMTUuNjM3OFoiIGZpbGw9IiM3OTk0RkYiLz4KPHBhdGggZD0iTTEuNSA5Ljc3NTUxSDE5LjA1MTZMMTcuMDEzOSAxMy44NzExSDEuNVY5Ljc3NTUxWiIgZmlsbD0iIzQ4NkRGRiIvPgo8cGF0aCBkPSJNNy42NTk5NiAzSDEuNTI0NDFWOC4wMDcwNEgyMi40NjEyVjNIMTYuMzI1NlY2LjczOTQ0SDE1LjA2MDZWM0g4LjkyNTAyVjYuNzM5NDRINy42NTk5NlYzWiIgZmlsbD0iIzVDRDFGQSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzEzNDk0XzY2MjU0Ij4KPHJlY3Qgd2lkdGg9IjIxIiBoZWlnaHQ9IjE4IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS41IDMpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==",
67+
light:
68+
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzEzNDk0XzY2MjU0KSI+CjxwYXRoIGQ9Ik0xMC44NzY5IDE1LjYzNzhIMS41VjE4LjM5OUMxLjUgMTkuODAxMyAyLjYzNDQ3IDIwLjkzOCA0LjAzMzkyIDIwLjkzOEg4LjI0OTkyTDEwLjg3NjkgMTUuNjM3OFoiIGZpbGw9IiM3OTk0RkYiLz4KPHBhdGggZD0iTTEuNSA5Ljc3NTUxSDE5LjA1MTZMMTcuMDEzOSAxMy44NzExSDEuNVY5Ljc3NTUxWiIgZmlsbD0iIzQ4NkRGRiIvPgo8cGF0aCBkPSJNNy42NTk5NiAzSDEuNTI0NDFWOC4wMDcwNEgyMi40NjEyVjNIMTYuMzI1NlY2LjczOTQ0SDE1LjA2MDZWM0g4LjkyNTAyVjYuNzM5NDRINy42NTk5NlYzWiIgZmlsbD0iIzVDRDFGQSIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzEzNDk0XzY2MjU0Ij4KPHJlY3Qgd2lkdGg9IjIxIiBoZWlnaHQ9IjE4IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS41IDMpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==",
6669
},
6770
}
6871

@@ -169,16 +172,29 @@ export class InjectedConnector extends Connector {
169172
throw new ConnectorNotFoundError()
170173
}
171174

175+
/**
176+
* @dev This emit ensures compatibility of Argent connector with starknet-react
177+
*/
178+
if (this._options.shouldEmit) {
179+
this.emit("connectionStatus", "init")
180+
}
181+
172182
let accounts: string[]
173183
try {
174184
accounts = await this.request({
175185
type: "wallet_requestAccounts",
176186
})
177187
} catch {
188+
if (this._options.shouldEmit) {
189+
this.emit("connectionStatus", "fail")
190+
}
178191
throw new UserRejectedRequestError()
179192
}
180193

181194
if (!accounts) {
195+
if (this._options.shouldEmit) {
196+
this.emit("connectionStatus", "fail")
197+
}
182198
throw new UserRejectedRequestError()
183199
}
184200

@@ -199,6 +215,12 @@ export class InjectedConnector extends Connector {
199215
* @dev This emit ensures compatibility with starknet-react
200216
*/
201217
this.emit("connect", { account, chainId })
218+
/**
219+
* @dev This emit ensures compatibility of Argent connector with starknet-react
220+
*/
221+
if (this._options.shouldEmit) {
222+
this.emit("connectionStatus", "success")
223+
}
202224

203225
return {
204226
account,

src/helpers/connector.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,41 @@ import {
44
StarknetkitConnector,
55
} from "../connectors"
66

7+
export function isCompoundConnector(
8+
connector: Connector | StarknetkitConnector | StarknetkitCompoundConnector,
9+
): boolean {
10+
return (connector as StarknetkitCompoundConnector).isCompoundConnector
11+
}
12+
713
export function extractConnector(
814
connector: Connector | StarknetkitConnector | StarknetkitCompoundConnector,
915
useFallback: boolean = false,
1016
) {
11-
if ((connector as StarknetkitCompoundConnector).isCompoundConnector) {
17+
const isCompound = isCompoundConnector(connector)
18+
19+
if (isCompound) {
1220
return useFallback
1321
? (connector as StarknetkitCompoundConnector).fallbackConnector
1422
: (connector as StarknetkitCompoundConnector).connector
1523
}
1624
return connector as StarknetkitConnector
1725
}
1826

27+
export function getConnectorMeta(
28+
connector: Connector | StarknetkitConnector | StarknetkitCompoundConnector,
29+
) {
30+
const isCompound = isCompoundConnector(connector)
31+
const _connector = extractConnector(connector)
32+
33+
const icon = isCompound ? connector.icon : _connector?.icon || ""
34+
const name = isCompound ? connector.name : _connector?.name || ""
35+
36+
return {
37+
icon,
38+
name,
39+
}
40+
}
41+
1942
export function findConnectorById(
2043
connectors: (
2144
| Connector

src/modal/Modal.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
1616
import { isInArgentMobileAppBrowser } from "../connectors/argent/helpers"
1717
import { extractConnector } from "../helpers/connector"
18-
import { StarknetkitCompoundConnector } from "../connectors"
18+
import type { StarknetkitCompoundConnector } from "../connectors"
1919
import { ArgentX } from "../connectors/injected/argentX"
2020
import { Braavos } from "../connectors/injected/braavos"
2121
import { getModalWallet } from "../helpers/mapModalWallets"
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
type FallbackConnectorProps = {
2+
handleClick: () => void
3+
}
4+
5+
export function FallbackConnector({ handleClick }: FallbackConnectorProps) {
6+
return (
7+
<footer className="flex flex-col gap-4 w-full">
8+
<hr className="border-stroke-default w-full" />
9+
10+
<button
11+
onClick={handleClick}
12+
onKeyUp={(e) => {
13+
if (e.key === "Enter") {
14+
handleClick()
15+
}
16+
}}
17+
rel="noreferrer noopener"
18+
role="button"
19+
className={`
20+
flex justify-center items-center w-full px-3 h-[60px] rounded-[12px] cursor-pointer shadow-list-item
21+
dark:shadow-none bg-surface-elevated focus:outline-none focus:ring-2 text-primary
22+
focus:ring-neutral-200 dark:focus:ring-neutral-700 transition-colors
23+
`}
24+
>
25+
<div className="flex flex-row-reverse items-center gap-2">
26+
<span className="text-primary">Connect to Argent mobile instead</span>
27+
28+
<svg
29+
xmlns="http://www.w3.org/2000/svg"
30+
width="27"
31+
height="48"
32+
viewBox="0 0 27 48"
33+
fill="none"
34+
>
35+
<path
36+
d="M10.9682 45.7174L3.14321 13.0668C2.75958 11.4692 3.43225 9.79803 4.82488 8.91515L15.5876 2.07812C16.2077 1.68398 16.9539 1.72077 17.5162 2.06761C16.3548 0.81687 14.8098 0.795849 14.1319 1.33714L2.86994 8.48948C1.26184 9.50899 0.484071 11.4377 0.925509 13.2875L8.40368 44.5034C8.78205 46.0905 12.014 47.6303 13.3751 46.7264V46.7159C12.4449 47.2782 11.2257 46.779 10.9682 45.7069V45.7174Z"
37+
fill="#8C8C8C"
38+
stroke="#0F0F0F"
39+
strokeWidth="0.262761"
40+
strokeMiterlimit="10"
41+
/>
42+
<path
43+
d="M12.8282 43.2841L14.0632 46.164L13.3748 46.7315C12.0137 47.6354 8.78696 46.0957 8.40333 44.5086L7.67285 41.4658L12.8282 43.2841Z"
44+
fill="#0F0F0F"
45+
stroke="#0F0F0F"
46+
strokeWidth="0.315313"
47+
strokeMiterlimit="10"
48+
/>
49+
<path
50+
d="M26.0828 35.1019L18.3313 3.18697C18.037 1.97827 16.6391 1.41596 15.5881 2.07812L4.82542 8.91515C3.43805 9.79802 2.76012 11.4639 3.14375 13.0668L10.9635 45.7174C11.2263 46.8105 12.4928 47.3097 13.4335 46.6896L24.3958 39.4111C25.8095 38.4705 26.4822 36.752 26.0828 35.1019Z"
51+
fill="#8C8C8C"
52+
stroke="#0F0F0F"
53+
strokeWidth="0.262761"
54+
strokeMiterlimit="10"
55+
/>
56+
<path
57+
d="M25.3354 34.6552L18.1252 4.10667C17.852 2.95052 16.5644 2.40398 15.6027 3.03986L14.2627 3.92274L13.7739 5.66747C13.6846 5.98803 13.4849 6.26131 13.2064 6.44524L8.79724 9.39867C8.4031 9.66143 7.90386 9.70872 7.47293 9.51954L6.44291 9.0781L5.72294 9.55632C4.44593 10.3972 3.83106 11.9895 4.18842 13.524L11.4669 44.782C11.7139 45.8278 12.8805 46.3113 13.7424 45.7174L23.8061 38.7858C25.1042 37.8924 25.719 36.2423 25.3459 34.6657L25.3354 34.6552Z"
58+
fill="#FF875B"
59+
stroke="#0F0F0F"
60+
strokeWidth="0.315313"
61+
strokeMiterlimit="10"
62+
/>
63+
<path
64+
d="M9.23809 7.71168C9.26962 7.80627 9.37998 7.82729 9.48508 7.75372L12.6802 5.60434C12.7854 5.53602 12.8432 5.39938 12.8116 5.30479C12.7801 5.2102 12.6697 5.18917 12.5646 5.26275L9.36947 7.41213C9.26436 7.48045 9.20655 7.61708 9.23809 7.71168Z"
65+
fill="#0F0F0F"
66+
/>
67+
<path
68+
d="M8.51836 8.563C8.68127 8.55249 8.80214 8.38432 8.78637 8.19513C8.77061 8.00069 8.62872 7.85354 8.47106 7.86406C8.30815 7.87457 8.18728 8.04273 8.20304 8.23192C8.21881 8.42636 8.3607 8.57351 8.51836 8.563Z"
69+
fill="#0F0F0F"
70+
/>
71+
<path
72+
d="M8.93919 33.951L10.6629 8.14795L8.79204 9.39869C8.4452 9.62992 8.01427 9.69298 7.62013 9.57737L6.46924 23.3723L8.93393 33.9563L8.93919 33.951Z"
73+
fill="#FFCBB8"
74+
/>
75+
<path
76+
d="M5.69775 20.0089L5.97103 21.1755L7.01681 9.31975L6.68048 9.17261L5.69775 20.0036V20.0089Z"
77+
fill="#FFCBB8"
78+
/>
79+
<path
80+
d="M25.3354 34.6552L18.1252 4.10667C17.852 2.95052 16.5644 2.40398 15.6027 3.03986L14.2627 3.92274L13.7739 5.66747C13.6846 5.98803 13.4849 6.26131 13.2064 6.44524L8.79724 9.39867C8.4031 9.66143 7.90385 9.70872 7.47293 9.51954L6.44291 9.0781L5.72294 9.55632C4.44593 10.3972 3.83106 11.9895 4.18842 13.524L11.4669 44.782C11.7139 45.8278 12.8805 46.3113 13.7424 45.7174L23.8061 38.7858C25.1042 37.8924 25.719 36.2423 25.3459 34.6657L25.3354 34.6552Z"
81+
stroke="#0F0F0F"
82+
strokeWidth="0.262761"
83+
strokeMiterlimit="10"
84+
/>
85+
<path
86+
d="M7.28408 39.5635L9.71724 40.2257L3.94702 16.4038L1.56641 15.9729L7.28408 39.5688V39.5635Z"
87+
fill="#0F0F0F"
88+
stroke="#0F0F0F"
89+
strokeWidth="0.315313"
90+
strokeMiterlimit="10"
91+
/>
92+
<path
93+
d="M2.99527 13.1299C2.6747 11.7741 3.01629 10.3447 4.1304 9.25683L2.51705 8.94678C1.32411 9.93476 0.824868 11.4167 1.00355 12.8514L2.99527 13.1299Z"
94+
fill="#F8F8F8"
95+
/>
96+
<path
97+
d="M14.7094 20.855L13.5585 21.4804C13.5217 21.5014 13.5007 21.5592 13.5165 21.6065C13.9422 23.0149 13.7898 24.6492 13.0698 26.1207C13.0488 26.168 13.0593 26.2258 13.0961 26.2573L14.2207 27.0929C14.2575 27.1192 14.31 27.1087 14.3363 27.0614C14.783 26.147 15.0563 25.1748 15.1509 24.2026C15.7867 24.7911 16.5487 25.2273 17.3948 25.4796C17.4369 25.4901 17.4789 25.4585 17.4894 25.406L17.7259 23.8347C17.7312 23.7821 17.7049 23.7296 17.6629 23.7191C16.3385 23.3092 15.3033 22.2949 14.8145 20.9075C14.7988 20.8602 14.7515 20.834 14.7147 20.855H14.7094Z"
98+
fill="white"
99+
/>
100+
</svg>
101+
</div>
102+
</button>
103+
</footer>
104+
)
105+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { IconButton } from "./IconButton"
2+
3+
type HeaderProps = {
4+
title: string
5+
subtitle: string
6+
showBackButton: boolean
7+
showCloseButton: boolean
8+
handleBack: () => void
9+
handleClose: () => void
10+
}
11+
12+
export function Header({
13+
title,
14+
subtitle,
15+
showBackButton,
16+
showCloseButton,
17+
handleBack,
18+
handleClose,
19+
}: HeaderProps) {
20+
return (
21+
<header className={`flex items-center justify-center flex-col relative`}>
22+
{showBackButton && (
23+
<IconButton handleClick={handleBack} className="absolute top-0 left-0">
24+
<svg
25+
xmlns="http://www.w3.org/2000/svg"
26+
width="12"
27+
height="12"
28+
viewBox="0 0 12 12"
29+
fill="none"
30+
>
31+
<path
32+
fillRule="evenodd"
33+
clipRule="evenodd"
34+
d="M8.55383 1.24376C8.83406 1.54808 8.81134 2.01905 8.50309 2.2957L4.37567 6L8.50309 9.7043C8.81134 9.98095 8.83406 10.4519 8.55383 10.7562C8.2736 11.0606 7.79654 11.083 7.48828 10.8063L2.74691 6.55102C2.58965 6.40989 2.5 6.20981 2.5 6C2.5 5.79019 2.58965 5.59012 2.74691 5.44898L7.48828 1.19367C7.79654 0.917013 8.2736 0.93944 8.55383 1.24376Z"
35+
fill="currentColor"
36+
/>
37+
</svg>
38+
</IconButton>
39+
)}
40+
<hgroup>
41+
<h2 className={`text-p3 text-secondary font-semibold`}>{subtitle}</h2>
42+
{!!title && (
43+
<h1
44+
className={`text-[24px] text-primary font-semibold max-w-[240px] overflow-hidden whitespace-nowrap text-ellipsis`}
45+
>
46+
{title}
47+
</h1>
48+
)}
49+
</hgroup>
50+
{showCloseButton && (
51+
<IconButton
52+
handleClick={handleClose}
53+
className="absolute top-0 right-0"
54+
>
55+
<svg
56+
width="12"
57+
height="12"
58+
viewBox="0 0 12 12"
59+
fill="none"
60+
xmlns="http://www.w3.org/2000/svg"
61+
>
62+
<path
63+
d="M9.77275 3.02275C9.99242 2.80308 9.99242 2.44692 9.77275 2.22725C9.55308 2.00758 9.19692 2.00758 8.97725 2.22725L6 5.20451L3.02275 2.22725C2.80308 2.00758 2.44692 2.00758 2.22725 2.22725C2.00758 2.44692 2.00758 2.80308 2.22725 3.02275L5.20451 6L2.22725 8.97725C2.00758 9.19692 2.00758 9.55308 2.22725 9.77275C2.44692 9.99242 2.80308 9.99242 3.02275 9.77275L6 6.79549L8.97725 9.77275C9.19692 9.99242 9.55308 9.99242 9.77275 9.77275C9.99242 9.55308 9.99242 9.19692 9.77275 8.97725L6.79549 6L9.77275 3.02275Z"
64+
fill="currentColor"
65+
/>
66+
</svg>
67+
</IconButton>
68+
)}
69+
</header>
70+
)
71+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { PropsWithChildren } from "react"
2+
3+
type IconButtonProps = PropsWithChildren<{
4+
handleClick: () => void
5+
className?: string
6+
}>
7+
8+
export function IconButton({
9+
children,
10+
handleClick,
11+
className = "",
12+
}: IconButtonProps) {
13+
return (
14+
<button
15+
onClick={handleClick}
16+
onKeyUp={(e) => {
17+
if (e.key === "Enter") {
18+
handleClick()
19+
}
20+
}}
21+
className={`
22+
p-2 cursor-pointer text-primary rounded-full bg-surface-elevated-web
23+
focus:outline-none focus:ring-2 focus:ring-neutral-200 dark:focus:ring-neutral-700
24+
transition-colors ${className}
25+
`}
26+
rel="noreferrer noopener"
27+
role="button"
28+
>
29+
{children}
30+
</button>
31+
)
32+
}

0 commit comments

Comments
 (0)