Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/dry-feet-go.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@namehash/namehash-ui": minor
---

Populates `namehash-ui` package with shared UI components.
2 changes: 2 additions & 0 deletions packages/ensnode-sdk/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { DEFAULT_EVM_CHAIN_ID } from "../ens";
*
* Represents a unique identifier for a chain.
* Guaranteed to be a positive integer.
*
* Chain id standards are organized by the Ethereum Community @ https://github.com/ethereum-lists/chains
**/
export type ChainId = number;

Expand Down
4 changes: 2 additions & 2 deletions packages/namehash-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ For UI component libraries intended for the general public, we recommend [ensnod
## Installation

```bash
npm install @namehash/namehash-ui @ensnode/ensnode-react
npm install @namehash/namehash-ui @ensnode/ensnode-react sonner
```

Note: `@ensnode/ensnode-react` is necessary only for some components. It might happen that you won't need it.
Note: `@ensnode/ensnode-react` and `sonner` are package's peer dependencies. The former is necessary only for some components and same goes for `sonner` which is only necessary for `CopyButton` component. It might happen that you won't need these installed, depending on which components you want to use.

## Setup

Expand Down
4 changes: 1 addition & 3 deletions packages/namehash-ui/biome.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
"$schema": "https://biomejs.dev/schemas/2.3.2/schema.json",
"extends": "//",
"files": {
"includes": [
"!src/styles.css"
]
"includes": ["**/*", "!src/styles.css"]
}
}
21 changes: 21 additions & 0 deletions packages/namehash-ui/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": "nhui"
},
"aliases": {
"components": "@/components",
"utils": "@/utils/cn",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
26 changes: 21 additions & 5 deletions packages/namehash-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,36 @@
"peerDependencies": {
"@ensnode/ensnode-react": "workspace:*",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"sonner": "^2.0.3"
},
"devDependencies": {
"@ensnode/shared-configs": "workspace:*",
"@tailwindcss/postcss": "^4.1.18",
"@testing-library/react": "catalog:",
"@types/node": "catalog:",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"postcss": "^8.5.6",
"react": "19.2.1",
"tailwindcss": "catalog:",
"tsup": "^8.3.6",
"typescript": "catalog:",
"@tailwindcss/postcss": "^4.1.18",
"postcss": "^8.5.6",
"tailwindcss": "^4.1.18"
"viem": "catalog:"
},
"dependencies": {}
"dependencies": {
"@ensnode/datasources": "workspace:*",
"@ensnode/ensnode-sdk": "workspace:*",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.8",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, so we need sonner on top of these other tooltip libraries?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lightwalker-eth Yes, these are separate functionalities. (See these docs for more info).

"boring-avatars": "^2.0.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "catalog:",
"lucide-react": "catalog:",
"tailwind-merge": "catalog:",
"tailwindcss-animate": "catalog:",
"tw-animate-css": "^1.4.0"
}
}
70 changes: 70 additions & 0 deletions packages/namehash-ui/src/components/chains/ChainIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
arbitrum,
arbitrumSepolia,
base,
baseSepolia,
linea,
lineaSepolia,
mainnet,
optimism,
optimismSepolia,
scroll,
scrollSepolia,
sepolia,
} from "viem/chains";

import { ensTestEnvL1Chain } from "@ensnode/datasources";
import type { ChainId } from "@ensnode/ensnode-sdk";

import { ArbitrumIcon } from "./icons/ArbitrumIcon.tsx";
import { ArbitrumTestnetIcon } from "./icons/ArbitrumTestnetIcon.tsx";
import { BaseIcon } from "./icons/BaseIcon.tsx";
import { BaseTestnetIcon } from "./icons/BaseTestnetIcon.tsx";
import { EthereumIcon } from "./icons/EthereumIcon.tsx";
import { EthereumLocalIcon } from "./icons/EthereumLocalIcon.tsx";
import { EthereumTestnetIcon } from "./icons/EthereumTestnetIcon.tsx";
import { LineaIcon } from "./icons/LineaIcon.tsx";
import { LineaTestnetIcon } from "./icons/LineaTestnetIcon.tsx";
import { OptimismIcon } from "./icons/OptimismIcon.tsx";
import { OptimismTestnetIcon } from "./icons/OptimismTestnetIcon.tsx";
import { ScrollIcon } from "./icons/ScrollIcon.tsx";
import { ScrollTestnetIcon } from "./icons/ScrollTestnetIcon.tsx";
import { UnrecognizedChainIcon } from "./icons/UnrecognizedChainIcon.tsx";

export interface ChainIconProps {
chainId: ChainId;
width?: number;
height?: number;
}

/**
* Mapping of {@link ChainId} to chain icon.
*/
const chainIcons = new Map<number, React.ComponentType<React.SVGProps<SVGSVGElement>>>([
// mainnet
[mainnet.id, EthereumIcon],
[base.id, BaseIcon],
[linea.id, LineaIcon],
[optimism.id, OptimismIcon],
[arbitrum.id, ArbitrumIcon],
[scroll.id, ScrollIcon],

// sepolia
[sepolia.id, EthereumTestnetIcon],
[baseSepolia.id, BaseTestnetIcon],
[lineaSepolia.id, LineaTestnetIcon],
[optimismSepolia.id, OptimismTestnetIcon],
[arbitrumSepolia.id, ArbitrumTestnetIcon],
[scrollSepolia.id, ScrollTestnetIcon],

// ens-test-env
[ensTestEnvL1Chain.id, EthereumLocalIcon],
]);

/**
* Renders an icon for the provided chain ID.
*/
export function ChainIcon({ chainId, width = 20, height = 20 }: ChainIconProps) {
const Icon = chainIcons.get(chainId) || UnrecognizedChainIcon;
return <Icon width={width} height={height} />;
}
15 changes: 15 additions & 0 deletions packages/namehash-ui/src/components/chains/ChainName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ChainId } from "@ensnode/ensnode-sdk";

import { getChainName } from "@/utils/chains.ts";

export interface ChainNameProps {
chainId: ChainId;
className: string;
}

/**
* Renders a prettified chain name for the provided {@link ChainId}.
*/
export const ChainName = ({ chainId, className }: ChainNameProps) => (
<p className={className}>{getChainName(chainId)}</p>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type React from "react";

export const ArbitrumIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="18"
height="20"
viewBox="0 0 18 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<mask id="mask0_1105_4046" maskUnits="userSpaceOnUse" x="0" y="0" width="18" height="20">
<path d="M18 0H0V20H18V0Z" fill="white" />
</mask>
<g mask="url(#mask0_1105_4046)">
<path
d="M10.5066 11.5199L9.52179 14.1748C9.49274 14.2484 9.49274 14.3302 9.52179 14.4039L11.2129 18.9734L13.17 17.8606L10.8224 11.5199C10.7683 11.3726 10.5606 11.3726 10.5066 11.5199Z"
fill="#1B4ADD"
/>
<path
d="M12.4786 7.05258C12.4246 6.90533 12.2169 6.90533 12.1629 7.05258L11.1781 9.70751C11.149 9.7812 11.149 9.86301 11.1781 9.93664L13.9495 17.4188L15.9066 16.306L12.4786 7.05258Z"
fill="#1B4ADD"
/>
<path
d="M9 1.23952C9.04984 1.23952 9.09559 1.25179 9.14127 1.27634L16.6039 5.51851C16.6912 5.5676 16.7452 5.6576 16.7452 5.75578V14.2401C16.7452 14.3383 16.6912 14.4283 16.6039 14.4774L9.14127 18.7237C9.09974 18.7482 9.04984 18.7605 9 18.7605C8.95016 18.7605 8.90441 18.7482 8.85873 18.7237L1.40028 14.4815C1.31302 14.4324 1.259 14.3424 1.259 14.2443V5.75578C1.259 5.6576 1.31302 5.5676 1.40028 5.51851L8.86289 1.27634C8.90441 1.25179 8.95432 1.23952 9 1.23952ZM9 0C8.7341 0 8.46813 0.0695437 8.23128 0.204541L0.768699 4.44672C0.295014 4.71671 0 5.21579 0 5.75578V14.2401C0 14.7801 0.295014 15.2833 0.768699 15.5533L8.23128 19.7954C8.46813 19.9304 8.7341 20 9 20C9.2659 20 9.53187 19.9304 9.76872 19.7954L17.2313 15.5533C17.7091 15.2833 18 14.7842 18 14.2401V5.75578C18 5.21579 17.705 4.71262 17.2313 4.44263L9.77288 0.204541C9.53187 0.0695437 9.2659 0 9 0Z"
fill="#1B4ADD"
/>
<path
d="M4.07031 17.4269L4.75591 15.5779L6.13541 16.7069L4.84732 17.8687L4.07031 17.4269Z"
fill="#1B4ADD"
/>
<path
d="M8.37115 5.15039H6.48059C6.33932 5.15039 6.21046 5.2363 6.16477 5.3672L2.10938 16.3142L4.06644 17.4269L8.53321 5.3713C8.57057 5.26493 8.49162 5.15039 8.37115 5.15039Z"
fill="#1B4ADD"
/>
<path
d="M11.6831 5.15039H9.79251C9.65125 5.15039 9.52239 5.2363 9.47671 5.3672L4.84375 17.8687L6.80079 18.9815L11.841 5.37538C11.8825 5.26493 11.7995 5.15039 11.6831 5.15039Z"
fill="#1B4ADD"
/>
</g>
</svg>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type React from "react";

export const ArbitrumTestnetIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="18"
height="20"
viewBox="0 0 18 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<mask id="mask0_1105_4086" maskUnits="userSpaceOnUse" x="0" y="0" width="18" height="20">
<path d="M18 0H0V20H18V0Z" fill="white" />
</mask>
<g mask="url(#mask0_1105_4086)">
<path
d="M10.5066 11.5199L9.52179 14.1748C9.49274 14.2484 9.49274 14.3302 9.52179 14.4039L11.2129 18.9734L13.17 17.8606L10.8224 11.5199C10.7683 11.3726 10.5606 11.3726 10.5066 11.5199Z"
fill="#FFB300"
/>
<path
d="M12.4786 7.05258C12.4246 6.90533 12.2169 6.90533 12.1629 7.05258L11.1781 9.70751C11.149 9.7812 11.149 9.86301 11.1781 9.93664L13.9495 17.4188L15.9066 16.306L12.4786 7.05258Z"
fill="#FFB300"
/>
<path
d="M9 1.23952C9.04984 1.23952 9.09559 1.25179 9.14127 1.27634L16.6039 5.51851C16.6912 5.5676 16.7452 5.6576 16.7452 5.75578V14.2401C16.7452 14.3383 16.6912 14.4283 16.6039 14.4774L9.14127 18.7237C9.09974 18.7482 9.04984 18.7605 9 18.7605C8.95016 18.7605 8.90441 18.7482 8.85873 18.7237L1.40028 14.4815C1.31302 14.4324 1.259 14.3424 1.259 14.2443V5.75578C1.259 5.6576 1.31302 5.5676 1.40028 5.51851L8.86289 1.27634C8.90441 1.25179 8.95432 1.23952 9 1.23952ZM9 0C8.7341 0 8.46813 0.0695437 8.23128 0.204541L0.768699 4.44672C0.295014 4.71671 0 5.21579 0 5.75578V14.2401C0 14.7801 0.295014 15.2833 0.768699 15.5533L8.23128 19.7954C8.46813 19.9304 8.7341 20 9 20C9.2659 20 9.53187 19.9304 9.76872 19.7954L17.2313 15.5533C17.7091 15.2833 18 14.7842 18 14.2401V5.75578C18 5.21579 17.705 4.71262 17.2313 4.44263L9.77288 0.204541C9.53187 0.0695437 9.2659 0 9 0Z"
fill="#FFB300"
/>
<path
d="M4.07031 17.4269L4.75591 15.5779L6.13541 16.7069L4.84732 17.8687L4.07031 17.4269Z"
fill="#FFB300"
/>
<path
d="M8.37115 5.15039H6.48059C6.33932 5.15039 6.21046 5.2363 6.16477 5.3672L2.10938 16.3142L4.06644 17.4269L8.53321 5.3713C8.57057 5.26493 8.49162 5.15039 8.37115 5.15039Z"
fill="#FFB300"
/>
<path
d="M11.6831 5.15039H9.79251C9.65125 5.15039 9.52239 5.2363 9.47671 5.3672L4.84375 17.8687L6.80079 18.9815L11.841 5.37538C11.8825 5.26493 11.7995 5.15039 11.6831 5.15039Z"
fill="#FFB300"
/>
</g>
</svg>
);
18 changes: 18 additions & 0 deletions packages/namehash-ui/src/components/chains/icons/BaseIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type React from "react";

export const BaseIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="146"
height="146"
viewBox="0 0 146 146"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<circle cx="73" cy="73" r="73" fill="#0052FF" />
<path
d="M73.323 123.729C101.617 123.729 124.553 100.832 124.553 72.5875C124.553 44.343 101.617 21.4463 73.323 21.4463C46.4795 21.4463 24.4581 42.0558 22.271 68.2887H89.9859V76.8864H22.271C24.4581 103.119 46.4795 123.729 73.323 123.729Z"
fill="white"
/>
</svg>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type React from "react";

export const BaseTestnetIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g clipPath="url(#clip0_75_249)">
<path
d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z"
fill="#FFB300"
/>
<path
d="M10.0442 16.9491C13.9201 16.9491 17.062 13.8125 17.062 9.94339C17.062 6.07428 13.9201 2.93774 10.0442 2.93774C6.36701 2.93774 3.35038 5.76096 3.05078 9.35451H12.3268V10.5323H3.05078C3.35038 14.1258 6.36701 16.9491 10.0442 16.9491Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_75_249">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type React from "react";

export const EthereumIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="12"
height="20"
viewBox="0 0 12 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M6.12793 19.2568L0.425781 11.3174L6.12793 14.6455L11.834 11.3174L6.12793 19.2568ZM11.8311 10.0029L6.12891 13.333L6.12793 13.332V13.333L0.425781 10.0029L6.12793 0.900391V0.904297L6.12891 0.900391L11.8311 10.0029Z"
fill="#272727"
/>
</svg>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type React from "react";

export const EthereumLocalIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M10.1285 0.900879L10.0039 1.31915V13.2105L10.1285 13.3333L15.8311 10.0031L10.1285 0.900879Z"
fill="#B22222"
/>
<path
d="M10.1285 0.900879L4.42578 10.0031L10.1285 13.3333L10.1285 7.68823V0.900879Z"
fill="#FA8072"
/>
<path
d="M10.1288 14.6459L10.0586 14.7305V19.054L10.1288 19.2566L15.8349 11.3174L10.1288 14.6459Z"
fill="#DC143C"
/>
<path d="M10.1285 19.2566V14.6459L4.42578 11.3174L10.1285 19.2566Z" fill="#FA8072" />
<path d="M10.1289 13.3333L15.8315 10.0032L10.1289 7.68799V13.3333Z" fill="#8B0000" />
<path d="M4.42578 10.0032L10.1284 13.3333V7.68799L4.42578 10.0032Z" fill="#CD5C5C" />
</svg>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type React from "react";

export const EthereumTestnetIcon = (props: React.SVGProps<SVGSVGElement>) => (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M10.1285 0.900879L10.0039 1.31915V13.2105L10.1285 13.3333L15.8311 10.0031L10.1285 0.900879Z"
fill="#FFD700"
/>
<path
d="M10.1285 0.900879L4.42578 10.0031L10.1285 13.3333L10.1285 7.68823V0.900879Z"
fill="#FFEA70"
/>
<path
d="M10.1288 14.6459L10.0586 14.7305V19.054L10.1288 19.2566L15.8349 11.3174L10.1288 14.6459Z"
fill="#FFC300"
/>
<path d="M10.1285 19.2566V14.6459L4.42578 11.3174L10.1285 19.2566Z" fill="#FFEA70" />
<path d="M10.1289 13.3333L15.8315 10.0032L10.1289 7.68799V13.3333Z" fill="#FFB300" />
<path d="M4.42578 10.0032L10.1284 13.3333V7.68799L4.42578 10.0032Z" fill="#FFD54F" />
</svg>
);
Loading