Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add account input component #6

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/www/__registry__/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import * as React from "react"

export const Index: Record<string, any> = {
"default": {
"account-input": {
name: "account-input",
type: "components:buidl",
registryDependencies: ["block-explorer-link"],
component: React.lazy(() => import("@/registry/default/buidl/account-input")),
files: ["registry/default/buidl/account-input.tsx"],
},
"address": {
name: "address",
type: "components:buidl",
Expand Down Expand Up @@ -299,6 +306,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/address-demo")),
files: ["registry/default/example/address-demo.tsx"],
},
"account-input-demo": {
name: "account-input-demo",
type: "components:example",
registryDependencies: ["account-input"],
component: React.lazy(() => import("@/registry/default/example/account-input-demo")),
files: ["registry/default/example/account-input-demo.tsx"],
},
"balance-demo": {
name: "balance-demo",
type: "components:example",
Expand Down
5 changes: 5 additions & 0 deletions apps/www/config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ export const docsConfig: DocsConfig = {
{
title: "Primitives",
items: [
{
title: "Account Input",
href: "/docs/components/account-input",
items: [],
},
{
title: "Address",
href: "/docs/components/address",
Expand Down
62 changes: 62 additions & 0 deletions apps/www/content/docs/components/account-input.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: Account Input
description: Input for Ethereum account or ENS name
component: true
wagmi:
link: https://wagmi.sh/react/hooks/useEnsName
---

<ComponentPreview
name="account-input-demo"
className="[&_.preview>[data-orientation=vertical]]:sm:max-w-[70%]"
/>

## Installation

<Tabs defaultValue="cli">

<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>

<TabsContent value="cli">

```bash
npx buidl-cli@latest add account-input
```

</TabsContent>

<TabsContent value="manual">

<Steps>

<Step>Install the following components:</Step>

- [ErrorMessage](/docs/components/error-message)

<Step>Install the following shadcn/ui components:</Step>

- [Input](https://ui.shadcn.com/docs/components/input)
- [Skeleton](https://ui.shadcn.com/docs/components/skeleton)

<Step>Copy and paste the following code into your project.</Step>

<ComponentSource name="account-input" />

</Steps>

</TabsContent>

</Tabs>

## Usage

```tsx
import { AccountInput } from "@/registry/default/buidl/account-input"
```

```tsx
<AccountInput />
```
13 changes: 13 additions & 0 deletions apps/www/public/registry/index.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
[
{
"name": "account-input",
"dependencies": [
"wagmi"
],
"registryDependencies": [
"block-explorer-link"
],
"files": [
"buidl/account-input.tsx"
],
"type": "components:buidl"
},
{
"name": "address",
"dependencies": [
Expand Down
16 changes: 16 additions & 0 deletions apps/www/public/registry/styles/default/account-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "account-input",
"dependencies": [
"wagmi"
],
"registryDependencies": [
"block-explorer-link"
],
"files": [
{
"name": "account-input.tsx",
"content": "import { InputHTMLAttributes, forwardRef, useMemo } from \"react\"\nimport { isAddress, type Address } from \"viem\"\nimport { useEnsAddress, useEnsName } from \"wagmi\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport { Input } from \"../ui/input\"\nimport { BlockExplorerLink } from \"./block-explorer-link\"\nimport { EnsAvatar } from \"./ens-avatar\"\n\ninterface AccountInputProps extends InputHTMLAttributes<HTMLInputElement> {\n value?: string\n}\n\nconst AccountInput = forwardRef<HTMLInputElement, AccountInputProps>(\n ({ value, className, ...props }, ref) => {\n const isValidAddress = useMemo(\n () => typeof value === \"string\" && isAddress(value),\n [value]\n )\n\n const { data: ensAddress } = useEnsAddress({\n name: value,\n // Resolve ENS only on mainnet\n chainId: 1,\n })\n\n const { data: ensName } = useEnsName({\n address: value as Address | undefined,\n chainId: 1,\n })\n\n return (\n <div\n className={cn(\n \"relative flex w-full items-center justify-between rounded-full border border-stone-300\",\n isValidAddress && \"border-r-0\",\n className\n )}\n >\n <Input\n ref={ref}\n className={cn(\n \"h-16 w-full rounded-full border-none pl-5 pr-12 text-base focus:ring-0 focus:ring-offset-0 focus-visible:ring-0 focus-visible:ring-offset-0 dark:focus:ring-0 dark:focus:ring-offset-0\",\n isValidAddress && \"pr-2\"\n )}\n {...props}\n />\n {(isValidAddress || ensAddress) && value && (\n <div className=\"w-fit rounded-full border border-stone-300 p-0.5\">\n <BlockExplorerLink\n data={ensAddress ? ensAddress : (value as Address)}\n >\n <div className=\"relative h-14 w-14 rounded-full\">\n <EnsAvatar\n className=\"h-14 w-14 rounded-full object-cover\"\n name={ensAddress ? value : undefined}\n address={value as Address}\n />\n </div>\n </BlockExplorerLink>\n </div>\n )}\n {ensAddress && (\n <span className=\"absolute bottom-1 left-6 text-xs font-light text-neutral-500\">\n {ensAddress.slice(0, 8)}...{ensAddress.slice(-6)}\n </span>\n )}\n {ensName && (\n <span className=\"absolute bottom-1 left-6 text-xs font-light text-neutral-500\">\n {ensName}\n </span>\n )}\n </div>\n )\n }\n)\n\nAccountInput.displayName = \"AccountInput\"\nexport { AccountInput }\n"
}
],
"type": "components:buidl"
}
80 changes: 80 additions & 0 deletions apps/www/registry/default/buidl/account-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { InputHTMLAttributes, forwardRef, useMemo } from "react"
import { isAddress, type Address } from "viem"
import { useEnsAddress, useEnsName } from "wagmi"

import { cn } from "@/lib/utils"

import { Input } from "../ui/input"
import { BlockExplorerLink } from "./block-explorer-link"
import { EnsAvatar } from "./ens-avatar"

interface AccountInputProps extends InputHTMLAttributes<HTMLInputElement> {
value?: string
}

const AccountInput = forwardRef<HTMLInputElement, AccountInputProps>(
({ value, className, ...props }, ref) => {
const isValidAddress = useMemo(
() => typeof value === "string" && isAddress(value),
[value]
)

const { data: ensAddress } = useEnsAddress({
name: value,
// Resolve ENS only on mainnet
chainId: 1,
})

const { data: ensName } = useEnsName({
address: value as Address | undefined,
chainId: 1,
})

return (
<div
className={cn(
"relative flex w-full items-center justify-between rounded-full border border-stone-300",
isValidAddress && "border-r-0",
className
)}
>
<Input
ref={ref}
className={cn(
"h-16 w-full rounded-full border-none pl-5 pr-12 text-base focus:ring-0 focus:ring-offset-0 focus-visible:ring-0 focus-visible:ring-offset-0 dark:focus:ring-0 dark:focus:ring-offset-0",
isValidAddress && "pr-2"
)}
{...props}
/>
{(isValidAddress || ensAddress) && value && (
<div className="w-fit rounded-full border border-stone-300 p-0.5">
<BlockExplorerLink
data={ensAddress ? ensAddress : (value as Address)}
>
<div className="relative h-14 w-14 rounded-full">
<EnsAvatar
className="h-14 w-14 rounded-full object-cover"
name={ensAddress ? value : undefined}
address={value as Address}
/>
</div>
</BlockExplorerLink>
</div>
)}
{ensAddress && (
<span className="absolute bottom-1 left-6 text-xs font-light text-neutral-500">
{ensAddress.slice(0, 8)}...{ensAddress.slice(-6)}
</span>
)}
{ensName && (
<span className="absolute bottom-1 left-6 text-xs font-light text-neutral-500">
{ensName}
</span>
)}
</div>
)
}
)

AccountInput.displayName = "AccountInput"
export { AccountInput }
42 changes: 42 additions & 0 deletions apps/www/registry/default/example/account-input-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useState, useRef, useEffect } from "react"
import { AccountInput } from "@/registry/default/buidl/account-input"


export const ADDRESS_EXAMPLE = "0x761d584f1C2d43cBc3F42ECd739701a36dFFAa31"

export default function AccountInputDemo() {
const inputRefEnsName = useRef<HTMLInputElement>(null)
const inputRefEnsAddress = useRef<HTMLInputElement>(null)

const [address, setAddress] = useState<string>()

useEffect(() => {
if(inputRefEnsName.current) {
inputRefEnsName.current.value = "vitalik.eth"
}
if(inputRefEnsAddress.current) {
inputRefEnsAddress.current.value = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
}
}, []);
return (
<div className="flex flex-col items-center gap-4 text-center">
<h3 className="text-lg font-bold">Default</h3>
<AccountInput

value={address}
onChange={(e) => setAddress(e.target.value)}
/>

<h3 className="text-lg font-bold">Ens Name Resolution</h3>
<AccountInput
ref={inputRefEnsName}
value="vitalik.eth"
/>

<h3 className="text-lg font-bold">Ens Address Resolution</h3>
<AccountInput
ref={inputRefEnsAddress}
value="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" />
</div>
)
}
13 changes: 13 additions & 0 deletions apps/www/registry/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ const ui: Registry = [
]

const buidl: Registry = [
{
name: "account-input",
type: "components:buidl",
dependencies: ["wagmi"],
registryDependencies: ["block-explorer-link"],
files: ["buidl/account-input.tsx"],
},
{
name: "address",
type: "components:buidl",
Expand Down Expand Up @@ -294,6 +301,12 @@ const example: Registry = [
registryDependencies: ["address"],
files: ["example/address-demo.tsx"],
},
{
name: "account-input-demo",
type: "components:example",
registryDependencies: ["account-input"],
files: ["example/account-input-demo.tsx"],
},
{
name: "balance-demo",
type: "components:example",
Expand Down
Loading