Skip to content

Commit d1a948d

Browse files
authored
feat: refactor the interactive demo section on the landing page + misc. styling updates (#1482)
1 parent 30c7ad4 commit d1a948d

15 files changed

+871
-530
lines changed

site/docs/components/App.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { type ReactNode, useEffect, useState } from 'react';
55
import { http, WagmiProvider, createConfig } from 'wagmi';
66
import { base, baseSepolia } from 'wagmi/chains';
77
import { coinbaseWallet } from 'wagmi/connectors';
8-
98
import '@coinbase/onchainkit/styles.css';
109

1110
const queryClient = new QueryClient();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Checkout, CheckoutButton } from '@coinbase/onchainkit/checkout';
2+
import App from '../App.tsx';
3+
4+
export const checkoutDemoCode = `
5+
import {
6+
Checkout,
7+
CheckoutButton
8+
} from '@coinbase/onchainkit/checkout';
9+
10+
<Checkout productId='my-product-id' >
11+
<CheckoutButton coinbaseBranded={true}/>
12+
</Checkout>
13+
`;
14+
15+
function CheckoutDemo() {
16+
return (
17+
<App>
18+
<Checkout productId="my-product-id">
19+
<CheckoutButton coinbaseBranded={true} className="w-[180px]" />
20+
</Checkout>
21+
</App>
22+
);
23+
}
24+
25+
export default CheckoutDemo;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import { useEffect, useState } from 'react';
2+
import CopyIcon from '../svg/CopySvg.tsx';
3+
import CheckIcon from '../svg/checkSvg.tsx';
4+
5+
// Demo components and code snippets
6+
import CheckoutDemo, { checkoutDemoCode } from './CheckoutDemo.tsx';
7+
import FundDemo, { fundDemoCode } from './FundDemo.tsx';
8+
import IdentityDemo, { identityDemoCode } from './IdentityDemo.tsx';
9+
import SwapDemo, { swapDemoCode } from './SwapDemo.tsx';
10+
import TransactionDemo, { transactionDemoCode } from './TransactionDemo.tsx';
11+
import WalletDemo, { walletDemoCode } from './WalletDemo.tsx';
12+
13+
type Component = {
14+
name: string;
15+
component: React.ComponentType;
16+
code: string;
17+
description: string;
18+
};
19+
20+
const components: Component[] = [
21+
{
22+
name: 'Wallet',
23+
component: WalletDemo,
24+
code: walletDemoCode,
25+
description: 'Enable users to onboard and log into your app with a wallet.',
26+
},
27+
{
28+
name: 'Swap',
29+
component: SwapDemo,
30+
code: swapDemoCode,
31+
description: 'Enable swaps between different cryptocurrencies.',
32+
},
33+
{
34+
name: 'Checkout',
35+
component: CheckoutDemo,
36+
code: checkoutDemoCode,
37+
description:
38+
'Accept USDC payments with instant user onboarding and onramps.',
39+
},
40+
{
41+
name: 'Transaction',
42+
component: TransactionDemo,
43+
code: transactionDemoCode,
44+
description: 'Trigger onchain transactions and sponsor them with Paymaster',
45+
},
46+
{
47+
name: 'Fund',
48+
component: FundDemo,
49+
code: fundDemoCode,
50+
description: 'Fund wallets with a debit card or a coinbase account.',
51+
},
52+
{
53+
name: 'Identity',
54+
component: IdentityDemo,
55+
code: identityDemoCode,
56+
description:
57+
'Display the Basename, avatar, and address associated with a wallet.',
58+
},
59+
];
60+
61+
function ComponentPreview() {
62+
const [isClient, setIsClient] = useState(false);
63+
const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
64+
const [activeTab, setActiveTab] = useState(0);
65+
const [activeSubTab, setActiveSubTab] = useState<'preview' | 'code'>(
66+
'preview',
67+
);
68+
69+
useEffect(() => {
70+
setIsClient(true);
71+
}, []);
72+
73+
const copyToClipboard = (text: string, index: number) => {
74+
navigator.clipboard.writeText(text).then(() => {
75+
setCopiedIndex(index);
76+
setTimeout(() => setCopiedIndex(null), 600);
77+
});
78+
};
79+
80+
if (!isClient) {
81+
return <div>Loading...</div>;
82+
}
83+
84+
return (
85+
<div className="mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8">
86+
<div className="flex flex-col gap-8 lg:flex-row lg:items-start">
87+
<ComponentList
88+
activeTab={activeTab}
89+
setActiveTab={setActiveTab}
90+
components={components}
91+
/>
92+
<PreviewContainer
93+
activeTab={activeTab}
94+
activeSubTab={activeSubTab}
95+
setActiveSubTab={setActiveSubTab}
96+
copiedIndex={copiedIndex}
97+
copyToClipboard={copyToClipboard}
98+
/>
99+
</div>
100+
</div>
101+
);
102+
}
103+
104+
interface ComponentListProps {
105+
activeTab: number;
106+
setActiveTab: (index: number) => void;
107+
components: Component[];
108+
}
109+
110+
function ComponentList({
111+
activeTab,
112+
setActiveTab,
113+
components,
114+
}: ComponentListProps) {
115+
return (
116+
<div className="w-full md:w-[300px] lg:flex-shrink-0">
117+
<h3 className="pb-4 font-medium text-3xl text-zinc-900 dark:text-zinc-100">
118+
Ready-to-use components
119+
</h3>
120+
<div className="pb-6 text-lg text-zinc-700 dark:text-zinc-500">
121+
Accelerate your time-to-market with prebuilt components.
122+
</div>
123+
<div className="flex flex-col">
124+
{components.map((comp, index) => (
125+
<div key={comp.name} className="mb-4">
126+
<button
127+
type="button"
128+
className={`w-full px-3 py-2 text-left text-base lg:text-lg ${
129+
activeTab === index
130+
? 'rounded-lg bg-zinc-100 font-semibold text-indigo-600 dark:bg-[#0F0F0F] dark:text-indigo-400'
131+
: 'text-zinc-700 hover:rounded-lg hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-[#0F0F0F]'
132+
}`}
133+
onClick={() => setActiveTab(index)}
134+
>
135+
{comp.name}
136+
{activeTab === index && (
137+
<div className="mt-1 font-normal text-sm text-zinc-600 dark:text-zinc-400">
138+
{comp.description}
139+
</div>
140+
)}
141+
</button>
142+
</div>
143+
))}
144+
</div>
145+
</div>
146+
);
147+
}
148+
149+
type PreviewContainerProps = {
150+
activeTab: number;
151+
activeSubTab: 'preview' | 'code';
152+
setActiveSubTab: (subTab: 'preview' | 'code') => void;
153+
copiedIndex: number | null;
154+
copyToClipboard: (text: string, index: number) => void;
155+
};
156+
157+
function PreviewContainer({
158+
activeTab,
159+
activeSubTab,
160+
setActiveSubTab,
161+
copiedIndex,
162+
copyToClipboard,
163+
}: PreviewContainerProps) {
164+
const ActiveComponent = components[activeTab].component;
165+
166+
return (
167+
<div className="w-full flex-shrink-0 lg:w-[640px] xl:w-[720px]">
168+
<div className="rounded-lg border border-zinc-200 dark:border-zinc-900">
169+
<div className="relative w-full overflow-hidden rounded-lg bg-zinc-100 dark:bg-[#0F0F0F]">
170+
<div className="flex items-center justify-between border-zinc-200 border-b dark:border-zinc-900">
171+
<div className="flex">
172+
<button
173+
type="button"
174+
className={`mt-2 ml-2 px-4 py-2 text-sm ${
175+
activeSubTab === 'preview'
176+
? 'border-indigo-600 border-b-2 bg-zinc-100 text-zinc-950 dark:border-indigo-400 dark:bg-[#0F0F0F] dark:text-zinc-50'
177+
: 'text-zinc-700 dark:text-zinc-300'
178+
}`}
179+
onClick={() => setActiveSubTab('preview')}
180+
>
181+
Preview
182+
</button>
183+
<button
184+
type="button"
185+
className={`mt-2 px-4 py-2 text-sm ${
186+
activeSubTab === 'code'
187+
? 'border-indigo-600 border-b-2 text-zinc-950 dark:border-indigo-400 dark:text-zinc-50'
188+
: 'text-zinc-700 dark:text-zinc-300'
189+
}`}
190+
onClick={() => setActiveSubTab('code')}
191+
>
192+
Code
193+
</button>
194+
</div>
195+
{activeSubTab === 'code' && (
196+
<button
197+
type="button"
198+
className="mr-2 rounded-md p-2 hover:bg-zinc-200 dark:hover:bg-zinc-800"
199+
onClick={() =>
200+
copyToClipboard(components[activeTab].code, activeTab)
201+
}
202+
title={
203+
copiedIndex === activeTab ? 'Copied!' : 'Copy to clipboard'
204+
}
205+
>
206+
{copiedIndex === activeTab ? (
207+
<CheckIcon className="h-5 w-5 text-green-500 dark:text-green-400" />
208+
) : (
209+
<CopyIcon className="h-5 w-5 text-zinc-700 dark:text-zinc-300" />
210+
)}
211+
</button>
212+
)}
213+
</div>
214+
<div className="h-[500px] overflow-auto">
215+
<div
216+
className={`${
217+
activeSubTab === 'preview' ? 'flex' : 'hidden'
218+
} h-full w-full items-center justify-center p-4`}
219+
>
220+
<div className="flex w-full max-w-[360px] items-center justify-center lg:max-w-none">
221+
<ActiveComponent />
222+
</div>
223+
</div>
224+
<div
225+
className={`${
226+
activeSubTab === 'code' ? 'block' : 'hidden'
227+
} h-full w-full p-4`}
228+
>
229+
<pre className="h-full w-full overflow-auto bg-zinc-100 text-sm dark:bg-[#0F0F0F]">
230+
<code>{components[activeTab].code}</code>
231+
</pre>
232+
</div>
233+
</div>
234+
</div>
235+
</div>
236+
</div>
237+
);
238+
}
239+
240+
export default ComponentPreview;
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { FundButton } from '@coinbase/onchainkit/fund';
2+
import App from '../App.tsx';
3+
4+
export const fundDemoCode = `
5+
import {
6+
FundButton,
7+
getOnrampBuyUrl
8+
} from '@coinbase/onchainkit/fund';
9+
import { useAccount } from 'wagmi';
10+
11+
const projectId = 'YOUR_CDP_PROJECT_ID';
12+
const { address } = useAccount();
13+
14+
const onrampBuyUrl = getOnrampBuyUrl({
15+
projectId,
16+
addresses: { address: ['base'] },
17+
assets: ['USDC'],
18+
presetFiatAmount: 20,
19+
fiatCurrency: 'USD'
20+
});
21+
22+
<FundButton fundingUrl={onrampBuyUrl} />
23+
`;
24+
25+
function FundDemo() {
26+
return (
27+
<App>
28+
<FundButton className="w-[180px]" />
29+
</App>
30+
);
31+
}
32+
33+
export default FundDemo;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {
2+
Address,
3+
Avatar,
4+
Badge,
5+
Identity,
6+
Name,
7+
} from '@coinbase/onchainkit/identity';
8+
import App from '../App.tsx';
9+
10+
export const identityDemoCode = `
11+
import {
12+
Avatar,
13+
Identity,
14+
Name,
15+
Badge,
16+
Address
17+
} from '@coinbase/onchainkit/identity';
18+
19+
<Identity
20+
address="0x838aD0EAE54F99F1926dA7C3b6bFbF617389B4D9"
21+
schemaId="0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9"
22+
>
23+
<Avatar />
24+
<Name>
25+
<Badge />
26+
</Name>
27+
<Address />
28+
</Identity>
29+
`;
30+
31+
function IdentityDemo() {
32+
return (
33+
<App>
34+
<Identity
35+
schemaId="0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9"
36+
address="0x838aD0EAE54F99F1926dA7C3b6bFbF617389B4D9"
37+
className="rounded-lg bg-gray-100dark:bg-gray-900 px-4 py-2"
38+
>
39+
<Avatar>
40+
<Badge className="badge" />
41+
</Avatar>
42+
<Name />
43+
<Address />
44+
</Identity>
45+
</App>
46+
);
47+
}
48+
49+
export default IdentityDemo;

0 commit comments

Comments
 (0)