Skip to content

Commit

Permalink
Add icon and ChainLogo components
Browse files Browse the repository at this point in the history
Rename StatusTimeline to MessageTimeline
Modify MessageTimeline to build
Add utils required by MessageTimeline
Setup Tailwind and use config from Explorer
Add vscode settings
Add necessary type deps
  • Loading branch information
jmrossy committed Jan 19, 2023
1 parent f9c90c0 commit 93287f2
Show file tree
Hide file tree
Showing 25 changed files with 2,136 additions and 35 deletions.
3 changes: 1 addition & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
node_modules
dist
coverage
src/types
hardhat.config.ts
tailwind.config.js
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"tabWidth": 2,
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all",
"importOrder": ["^@hyperlane-xyz/(.*)$", "^../(.*)$", "^./(.*)$"],
Expand Down
41 changes: 41 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"search.exclude": {
"**/node_modules/**": true
},
"files.exclude": {
"**/*.js.map": true,
"**/*.js": {"when": "$(basename).ts"},
"**/*.map": {"when": "$(basename).map"},
},
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true
},
"typescript.updateImportsOnFileMove.enabled": "always",
"[typescript]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.tabSize": 2,
"editor.detectIndentation": false,
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ yarn add @hyperlane-xyz/widgets

### Peer dependencies

This package requires the `@hyperlane-xyz/sdk`, `react`, and `react-dom`.
This package requires `@hyperlane-xyz/sdk`, `react`, and `react-dom`.

## Learn more

Expand Down
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hyperlane-xyz/widgets",
"description": "Common react components for projects using Hyperlane",
"description": "Common react components for Hyperlane projects",
"version": "1.0.0-beta1",
"peerDependencies": {
"@hyperlane-xyz/sdk": "1.0.0",
Expand All @@ -10,13 +10,17 @@
"devDependencies": {
"@hyperlane-xyz/sdk": "1.0.0",
"@trivago/prettier-plugin-sort-imports": "^3.2.0",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.5.0",
"prettier": "^2.4.1",
"react": "^18",
"react-dom": "^18",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.2.4",
"ts-node": "^10.8.0",
"typescript": "^4.7.2"
},
Expand All @@ -39,7 +43,9 @@
"url": "https://github.com/hyperlane-xyz/hyperlane-widgets"
},
"scripts": {
"build": "tsc",
"build": "yarn build:ts && yarn build:css",
"build:ts": "tsc",
"build:css": "tailwindcss -i ./src/styles/styles.css -o ./dist/styles/styles.css --minify",
"clean": "rm -rf dist cache",
"lint": "eslint . --ext .ts",
"prettier": "prettier --write ./src"
Expand Down
30 changes: 30 additions & 0 deletions src/icons/Airplane.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { memo } from 'react';

import { Color } from '../styles/Color';

interface Props {
width?: string | number;
height?: string | number;
color?: string;
classes?: string;
}

// Paper airplane shape
function _AirplaneIcon({ width, height, color, classes }: Props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
width={width}
height={height}
className={classes}
>
<path
d="M15.964.686a.5.5 0 0 0-.65-.65L.767 5.855H.766l-.452.18a.5.5 0 0 0-.082.887l.41.26.001.002 4.995 3.178 3.178 4.995.002.002.26.41a.5.5 0 0 0 .886-.083l6-15Zm-1.833 1.89L6.637 10.07l-.215-.338a.5.5 0 0 0-.154-.154l-.338-.215 7.494-7.494 1.178-.471-.47 1.178Z"
fill={color || Color.Blue}
/>
</svg>
);
}

export const AirplaneIcon = memo(_AirplaneIcon);
104 changes: 104 additions & 0 deletions src/icons/ChainLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { memo } from 'react';

import { chainIdToMetadata, chainMetadata } from '@hyperlane-xyz/sdk';
import ArbitrumMono from '@hyperlane-xyz/sdk/logos/black/arbitrum.svg';
import AvalancheMono from '@hyperlane-xyz/sdk/logos/black/avalanche.svg';
import BscMono from '@hyperlane-xyz/sdk/logos/black/bsc.svg';
import CeloMono from '@hyperlane-xyz/sdk/logos/black/celo.svg';
import EthereumMono from '@hyperlane-xyz/sdk/logos/black/ethereum.svg';
import MoonbeamMono from '@hyperlane-xyz/sdk/logos/black/moonbeam.svg';
import OptimismMono from '@hyperlane-xyz/sdk/logos/black/optimism.svg';
import PolygonMono from '@hyperlane-xyz/sdk/logos/black/polygon.svg';
import ArbitrumColor from '@hyperlane-xyz/sdk/logos/color/arbitrum.svg';
import AvalancheColor from '@hyperlane-xyz/sdk/logos/color/avalanche.svg';
import BscColor from '@hyperlane-xyz/sdk/logos/color/bsc.svg';
import CeloColor from '@hyperlane-xyz/sdk/logos/color/celo.svg';
import EthereumColor from '@hyperlane-xyz/sdk/logos/color/ethereum.svg';
import MoonbeamColor from '@hyperlane-xyz/sdk/logos/color/moonbeam.svg';
import OptimismColor from '@hyperlane-xyz/sdk/logos/color/optimism.svg';
import PolygonColor from '@hyperlane-xyz/sdk/logos/color/polygon.svg';

import { QuestionMarkIcon } from './QuestionMark';

// Keep up to date as new chains are added or
// icon will fallback to default (question mark)
const CHAIN_TO_MONOCHROME_ICON = {
[chainMetadata.alfajores.id]: CeloMono,
[chainMetadata.arbitrum.id]: ArbitrumMono,
[chainMetadata.arbitrumgoerli.id]: ArbitrumMono,
[chainMetadata.avalanche.id]: AvalancheMono,
[chainMetadata.bsc.id]: BscMono,
[chainMetadata.bsctestnet.id]: BscMono,
[chainMetadata.celo.id]: CeloMono,
[chainMetadata.ethereum.id]: EthereumMono,
[chainMetadata.fuji.id]: AvalancheMono,
[chainMetadata.goerli.id]: EthereumMono,
[chainMetadata.moonbasealpha.id]: MoonbeamMono,
[chainMetadata.moonbeam.id]: MoonbeamMono,
[chainMetadata.mumbai.id]: PolygonMono,
[chainMetadata.optimism.id]: OptimismMono,
[chainMetadata.optimismgoerli.id]: OptimismMono,
[chainMetadata.polygon.id]: PolygonMono,
};

const CHAIN_TO_COLOR_ICON = {
[chainMetadata.alfajores.id]: CeloColor,
[chainMetadata.arbitrum.id]: ArbitrumColor,
[chainMetadata.arbitrumgoerli.id]: ArbitrumColor,
[chainMetadata.avalanche.id]: AvalancheColor,
[chainMetadata.bsc.id]: BscColor,
[chainMetadata.bsctestnet.id]: BscColor,
[chainMetadata.celo.id]: CeloColor,
[chainMetadata.ethereum.id]: EthereumColor,
[chainMetadata.fuji.id]: AvalancheColor,
[chainMetadata.goerli.id]: EthereumColor,
[chainMetadata.moonbasealpha.id]: MoonbeamColor,
[chainMetadata.moonbeam.id]: MoonbeamColor,
[chainMetadata.mumbai.id]: PolygonColor,
[chainMetadata.optimism.id]: OptimismColor,
[chainMetadata.optimismgoerli.id]: OptimismColor,
[chainMetadata.polygon.id]: PolygonColor,
};

interface Props {
chainId?: number;
size?: number;
color?: boolean;
background?: boolean;
}

function _ChainLogo({ chainId, size = 32, color = true, background = false }: Props) {
const iconSet = color ? CHAIN_TO_COLOR_ICON : CHAIN_TO_MONOCHROME_ICON;
const hasIcon = !!(chainId && iconSet[chainId]);
const imageSrc = hasIcon ? iconSet[chainId] : '';
const title = getChainDisplayName(chainId);

if (background) {
const iconSize = Math.floor(size / 1.8);
return (
<div
style={{ width: `${size}px`, height: `${size}px` }}
className="flex items-center justify-center rounded-full bg-gray-100 transition-all"
title={title}
>
{hasIcon ? (
<img src={imageSrc} alt="" width={iconSize} height={iconSize} />
) : (
<QuestionMarkIcon width={iconSize} height={iconSize} />
)}
</div>
);
} else if (hasIcon) {
return <img src={imageSrc} alt="" width={size} height={size} title={title} />;
} else {
return <QuestionMarkIcon width={size} height={size} />;
}
}

function getChainDisplayName(chainId?: number, shortName = false) {
if (!chainId || !chainIdToMetadata[chainId]) return 'Unknown';
const metadata = chainIdToMetadata[chainId];
return shortName ? metadata.displayNameShort || metadata.displayName : metadata.displayName;
}

export const ChainLogo = memo(_ChainLogo);
34 changes: 34 additions & 0 deletions src/icons/Envelope.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { memo } from 'react';

import { Color } from '../styles/Color';

interface Props {
width?: string | number;
height?: string | number;
color?: string;
classes?: string;
}

// Envelope with checkmark
function _EnvelopeIcon({ width, height, color, classes }: Props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
width={width}
height={height}
className={classes}
>
<path
d="M.05 3.555A2 2 0 0 1 2 2h12a2 2 0 0 1 1.95 1.555L8 8.414.05 3.555ZM0 4.697v7.104l5.803-3.558L0 4.697ZM6.761 8.83l-6.57 4.026A2 2 0 0 0 2 14h6.256A4.493 4.493 0 0 1 8 12.5a4.49 4.49 0 0 1 1.606-3.446l-.367-.225L8 9.586l-1.239-.757ZM16 4.697v4.974A4.491 4.491 0 0 0 12.5 8a4.49 4.49 0 0 0-1.965.45l-.338-.207L16 4.697Z"
fill={color || Color.Blue}
/>
<path
d="M16 12.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Zm-1.993-1.679a.5.5 0 0 0-.686.172l-1.17 1.95-.547-.547a.5.5 0 0 0-.708.708l.774.773a.75.75 0 0 0 1.174-.144l1.335-2.226a.5.5 0 0 0-.172-.686Z"
fill={color || Color.Blue}
/>
</svg>
);
}

export const EnvelopeIcon = memo(_EnvelopeIcon);
29 changes: 29 additions & 0 deletions src/icons/Lock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { memo } from 'react';

import { Color } from '../styles/Color';

interface Props {
width?: string | number;
height?: string | number;
color?: string;
classes?: string;
}

function _LockIcon({ width, height, color, classes }: Props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 15 18"
width={width}
height={height}
className={classes}
>
<path
d="M7.14 1.13c.76 0 1.49.23 2.02.65.54.43.84 1 .84 1.6v4.5H4.29v-4.5c0-.6.3-1.17.83-1.6a3.29 3.29 0 0 1 2.02-.66Zm4.29 6.75v-4.5c0-.9-.45-1.76-1.26-2.4C9.37.37 8.28 0 7.14 0 6.01 0 4.92.36 4.11.99c-.8.63-1.25 1.49-1.25 2.38v4.5c-.76 0-1.49.24-2.02.66-.54.43-.84 1-.84 1.6v5.62c0 .6.3 1.17.84 1.6.53.41 1.26.65 2.02.65h8.57c.76 0 1.48-.24 2.02-.66.53-.42.84-1 .84-1.59v-5.63c0-.6-.3-1.16-.84-1.59a3.29 3.29 0 0 0-2.02-.65Z"
fill={color || Color.Blue}
/>
</svg>
);
}

export const LockIcon = memo(_LockIcon);
23 changes: 23 additions & 0 deletions src/icons/QuestionMark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { memo } from 'react';

import { Color } from '../styles/Color';

interface Props {
width?: string | number;
height?: string | number;
color?: string;
classes?: string;
}

function _QuestionMarkIcon({ width, height, color, classes }: Props) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width={width} height={height} className={classes}>
<path
d="M21.55 31.5q.05-3.6.825-5.25.775-1.65 2.925-3.6 2.1-1.9 3.225-3.525t1.125-3.475q0-2.25-1.5-3.75t-4.2-1.5q-2.6 0-4 1.475T17.9 14.95l-4.2-1.85q1.1-2.95 3.725-5.025T23.95 6q5 0 7.7 2.775t2.7 6.675q0 2.4-1.025 4.35-1.025 1.95-3.275 4.1-2.45 2.35-2.95 3.6t-.55 4Zm2.4 12.5q-1.45 0-2.475-1.025Q20.45 41.95 20.45 40.5q0-1.45 1.025-2.475Q22.5 37 23.95 37q1.45 0 2.475 1.025Q27.45 39.05 27.45 40.5q0 1.45-1.025 2.475Q25.4 44 23.95 44Z"
fill={color || Color.Black}
/>
</svg>
);
}

export const QuestionMarkIcon = memo(_QuestionMarkIcon);
31 changes: 31 additions & 0 deletions src/icons/Shield.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { memo } from 'react';

import { Color } from '../styles/Color';

interface Props {
width?: string | number;
height?: string | number;
color?: string;
classes?: string;
}

// Shield with checkmark
function _ShieldIcon({ width, height, color, classes }: Props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
width={width}
height={height}
className={classes}
>
<path
fill-rule="evenodd"
d="M8 0c-.69 0-1.843.265-2.928.56-1.11.3-2.229.655-2.887.87a1.54 1.54 0 0 0-1.044 1.262c-.596 4.477.787 7.795 2.465 9.99a11.777 11.777 0 0 0 2.517 2.453c.386.273.744.482 1.048.625.28.132.581.24.829.24s.548-.108.829-.24a7.159 7.159 0 0 0 1.048-.625 11.775 11.775 0 0 0 2.517-2.453c1.678-2.195 3.061-5.513 2.465-9.99a1.541 1.541 0 0 0-1.044-1.263 62.467 62.467 0 0 0-2.887-.87C9.843.266 8.69 0 8 0zm2.146 5.146a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 7.793l2.646-2.647z"
fill={color || Color.Blue}
/>
</svg>
);
}

export const ShieldIcon = memo(_ShieldIcon);
Loading

0 comments on commit 93287f2

Please sign in to comment.