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

UI: Header component #13

Merged
merged 25 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
74ed827
Created base for Header component
kpyszkowski Nov 30, 2023
9571039
Implement `Logo` component
kpyszkowski Nov 30, 2023
be0b381
Implement `Header` component
kpyszkowski Nov 30, 2023
e7d6465
Adjust Header's UserPanel spacing
kpyszkowski Dec 1, 2023
0e687e1
Merge branch 'main-cleanup' into ui/header-component
kpyszkowski Dec 5, 2023
474a5f4
Merge branch 'main-cleanup' into ui/header-component
kpyszkowski Dec 11, 2023
42aa0c1
Remove redundant export directives
kpyszkowski Dec 5, 2023
533e129
Add Header fixed width container
kpyszkowski Dec 27, 2023
4de9b63
Adjust Button component theme
kpyszkowski Dec 27, 2023
c105104
Restyle `SelectWalletModal` component
kpyszkowski Dec 28, 2023
a69046a
Adjust Header's `UserPanel` component
kpyszkowski Dec 28, 2023
639785d
Merge branch 'main' into ui/header-component
kpyszkowski Jan 15, 2024
67b2549
Add `Header`'s `UserPanel` menu
kpyszkowski Jan 18, 2024
f113c2e
Remove redundant changes in `.env.example` file
kpyszkowski Jan 22, 2024
3c9f8aa
Finetune margin value for `NavigationMenu`
kpyszkowski Jan 22, 2024
838dcc3
Rename `Logo` prop
kpyszkowski Jan 22, 2024
3a23252
Refactor component definitions
kpyszkowski Jan 22, 2024
b9ed545
Exported props definitions
kpyszkowski Jan 22, 2024
7329cef
Remove redundant boolean casting
kpyszkowski Jan 22, 2024
5252f89
Update Chakra theme config
kpyszkowski Jan 22, 2024
2f62eb9
Implement responsive Navigation Menu
kpyszkowski Jan 23, 2024
d2ce507
Restructure `Header` component namespace
kpyszkowski Jan 23, 2024
7bb9859
Change casing of SVG properties to resolve warning
kpyszkowski Jan 24, 2024
3b161e4
Revert "Exported props definitions"
kpyszkowski Jan 24, 2024
764b0c9
Merge branch 'main' into ui/header-component
michalsmiarowski Jan 25, 2024
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
13 changes: 7 additions & 6 deletions .env.example
michalsmiarowski marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
REACT_APP_SUPPORTED_CHAIN_ID=1337
REACT_APP_ETH_HOSTNAME_HTTP=http://localhost:8545
REACT_APP_ETH_HOSTNAME_WS=ws://localhost:8545
REACT_APP_SUPPORTED_CHAIN_ID=5
REACT_APP_ETH_HOSTNAME_HTTP=https://goerli.infura.io/v3/437bf9d604844b58846d1bff0b21872c
REACT_APP_ETH_HOSTNAME_WS=ws://goerli.infura.io/v3/437bf9d604844b58846d1bff0b21872c
REACT_APP_ELECTRUM_PROTOCOL=wss
REACT_APP_MULTICALL_ADDRESS=$MULTICALL_ADDRESS
REACT_APP_DAPP_DEVELOPMENT_TESTNET_CONTRACTS=$DAPP_DEVELOPMENT_TESTNET_CONTRACTS

Expand All @@ -13,8 +14,8 @@ REACT_APP_FEATURE_FLAG_LEDGER_LIVE=true
REACT_APP_SENTRY_DSN=$SENTRY_DSN

REACT_APP_ELECTRUM_PROTOCOL=$ELECTRUM_PROTOCOL
REACT_APP_ELECTRUM_HOST=$ELECTRUM_HOST
REACT_APP_ELECTRUM_PORT=$ELECTRUM_PORT
REACT_APP_ELECTRUM_HOST=electrumx-server.test.tbtc.network
REACT_APP_ELECTRUM_PORT=8443
REACT_APP_MOCK_BITCOIN_CLIENT=true

REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_PROJECT_ID
REACT_APP_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_PROJECT_ID
27 changes: 16 additions & 11 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import {
useSubscribeToRedemptionRequestedEvent,
} from "./hooks/tbtc"
import { useSentry } from "./hooks/sentry"
import { Header } from "./components/Header"
import { VStack } from "@threshold-network/components"

const Web3EventHandlerComponent = () => {
useSubscribeToERC20TransferEvent(Token.TBTCV2)
Expand Down Expand Up @@ -99,19 +101,22 @@ const AppBody = () => {

const Layout = () => {
return (
<Box display="flex">
<Sidebar />
<Box
// 100% - 80px is to account for the sidebar
w={{ base: "100%", md: "calc(100% - 80px)" }}
bg={useColorModeValue("transparent", "gray.900")}
>
<Navbar />
<Box as="main" data-cy="app-container">
<Outlet />
<VStack alignItems="normal" spacing="0">
<Header />
<Box display="flex">
<Sidebar />
<Box
// 100% - 80px is to account for the sidebar
w={{ base: "100%", md: "calc(100% - 80px)" }}
bg={useColorModeValue("transparent", "gray.900")}
>
<Navbar />
<Box as="main" data-cy="app-container">
<Outlet />
</Box>
</Box>
</Box>
</Box>
</VStack>
)
}

Expand Down
77 changes: 51 additions & 26 deletions src/components/DotsLoadingIndicator/index.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,54 @@
import { FC } from "react"
import {
Box,
HStack,
StackProps,
ThemingProps,
useStyleConfig,
} from "@threshold-network/components"
import { Box, StackProps, HStack } from "@threshold-network/components"
import { motion } from "framer-motion"

type DotsLoadingIndicatorProps = StackProps & Omit<ThemingProps, "orientation">
const Dot: FC = () => (
<motion.div
variants={{
initial: {
y: "50%",
opacity: 0.85,
},
animate: {
y: "-50%",
opacity: 0,
},
}}
transition={{
duration: 0.8,
repeat: Infinity,
repeatType: "reverse",
ease: "easeInOut",
}}
>
<Box w={1} h={1} rounded="full" bg="white" />
</motion.div>
)

export const DotsLoadingIndicator: FC<DotsLoadingIndicatorProps> = ({
colorScheme = "brand",
size = "sm",
...restProps
}) => {
const styles = useStyleConfig("DotsLoadingIndicator", {
colorScheme,
size,
})

return (
<HStack spacing="4" {...restProps}>
<Box __css={styles} />
<Box __css={styles} />
<Box __css={styles} />
</HStack>
)
}
export const DotsLoadingIndicator: FC<StackProps> = (props) => (
<HStack
spacing={0.5}
w={5}
h={5}
as={motion.div}
variants={{
initial: {
transition: {
staggerChildren: 0.2,
},
},
animate: {
transition: {
staggerChildren: 0.2,
},
},
}}
initial="initial"
animate="animate"
{...props}
>
<Dot />
<Dot />
<Dot />
</HStack>
)
184 changes: 184 additions & 0 deletions src/components/Header/Header.children.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import {
As,
BodyMd,
Box,
Button,
Flex,
HStack,
Icon,
Link,
List,
ListItem,
Menu,
MenuButton,
MenuItem,
MenuList,
SystemStyleObject,
} from "@threshold-network/components"
import { NavLink } from "react-router-dom"
import { FaCogs as TestnetIcon } from "react-icons/fa"
import shortenAddress from "../../utils/shortenAddress"
import { spacing } from "@chakra-ui/theme/foundations/spacing"
import { HiOutlinePlus as PlusIcon } from "react-icons/hi"
import { InlineTokenBalance } from "../TokenBalance"
import chainIdToNetworkName from "../../utils/chainIdToNetworkName"
import { EthereumDark } from "../../static/icons/EthereumDark"
import { ChainID } from "../../enums"
import Identicon from "../Identicon"

const activeLinkIndicatorStyles: SystemStyleObject = {
position: "relative",
"&.active": {
"&:before": {
content: '""',
position: "absolute",
bottom: 0,
left: 5,
width: `calc(100% - 2 * ${spacing[5]})`, // To account for container's padding
height: 0.5,
bg: "#53D2FF",
},
},
}

const networkIconMap = new Map<ChainID, As<unknown>>([
[ChainID.Ethereum, EthereumDark],
[ChainID.Goerli, TestnetIcon],
])

interface NavigationMenuItemProps {
/** The label of the menu item */
label: string
/** The route to navigate to when the menu item is clicked */
to: string
}

function NavigationMenuItem(props: NavigationMenuItemProps) {
const { label, to } = props
return (
<ListItem>
<Link
as={NavLink}
to={to}
display={"inline-flex"}
alignItems={"center"}
h={"full"}
px={5}
sx={activeLinkIndicatorStyles}
fontWeight={"black"}
textTransform={"uppercase"}
_hover={{ textDecoration: "none" }}
>
{label}
</Link>
</ListItem>
)
}

interface NavigationMenuProps {
/** The menu items to display */
items: NavigationMenuItemProps[]
}

export function NavigationMenu(props: NavigationMenuProps) {
const { items } = props
return (
<Flex as={List} alignSelf={"stretch"} ml={146 - 20} mr={"auto"}>
Copy link
Contributor

Choose a reason for hiding this comment

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

Why we use 146-20 value in ml? Where this value comes from? 🤔

Looking at designs it looks like the navigation menu will always be in line with the actual page layout (from the left side). Please look at the screen below:

image

The red line shows the positioning of navigation bar to the page content. As you can see it's start in the same position on the left side. It looks similar in all pages.

Considering that we will probably use a Container for the page layout below (but correct me if I'm wrong) then maybe somehow we should use a Container at the navigation bar? Or maybe we could use a grid to properly do that? I don't have a quick solution for that, but we can discuss this issue more if needed.

Copy link
Contributor Author

@kpyszkowski kpyszkowski Jan 22, 2024

Choose a reason for hiding this comment

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

It should be 142 - 20. Fixed.
As you can see on screenshot I added horizontal padding for NavigationItems to make it more accessible - easier to click by enlarging the area. To keep it pixel perfect the margin between Logo and NavigationMenu must be reduced by the item padding so spacing is now 122px of menu margin + 20px of item padding which results as 142px as it states in designs.
image

I've added comment to explain the math behind the operation.
Ref commit: 3c9f8aa

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still not convinced about this solution. It depende on how we want the menu and the content to behave on different resolutions. I would assume that when the width of the screen decreases, then the space between logo and navigation should decrease too so that we keep the navigation inline with the content (see my screenshot in the first comment).

For example, this is how out current tbtc implementation behaves when decreasing the width of the screen:

image image

As you can see, the left space between the content and the left navigation decreases. I assume Bitcoin on Base view will behave the same so we would have to consider this when implementing the navigation. Otherwise it might a little bit off.

We can take a look at it later when we implement the layout. It will be easier to evaluate how it should behave.

{items.map((item) => (
<NavigationMenuItem {...item} key={item.to} />
))}
</Flex>
)
}
michalsmiarowski marked this conversation as resolved.
Show resolved Hide resolved

interface UserPanelProps {
/** Whether the user is connected to a wallet */
isConnected: boolean
/** The address of the connected wallet */
accountAddress?: Nullable<string>
/** The balance of the connected wallet */
balance: number | string
/** The identifier of the chain */
chainId?: Nullable<number>
/** The callback to invoke when the user clicks the disconnect button */
onDisconnectClick: () => void
/** The callback to invoke when the user clicks the connect button */
onConnectClick: () => void
}

export function UserPanel(props: UserPanelProps) {
const {
isConnected,
accountAddress,
balance,
chainId,
onDisconnectClick,
onConnectClick,
} = props
return (
<HStack spacing={6} alignSelf={"stretch"}>
{isConnected && !!accountAddress && !!chainId ? (
<>
<Box as="p">
<InlineTokenBalance
fontWeight="medium"
tokenAmount={balance}
color="hsla(0, 0%, 100%, 90%)"
/>
&nbsp;
<BodyMd as="span" color="hsla(0, 0%, 100%, 50%)">
tBTC
</BodyMd>
</Box>
<HStack spacing={3}>
<Button
size="sm"
variant="outline"
color="white"
leftIcon={<Icon as={networkIconMap.get(chainId)} />}
>
{chainIdToNetworkName(chainId)}
</Button>
<Menu placement="bottom-end">
<MenuButton
as={Button}
size="sm"
variant="outline"
leftIcon={<Identicon address={accountAddress} />}
>
{shortenAddress(accountAddress)}
</MenuButton>
<MenuList
bgGradient="radial(circle at bottom right, #0A1616, #090909)"
border="1px solid"
borderColor="border.50"
rounded="lg"
>
<MenuItem
onClick={onDisconnectClick}
_hover={{
bg: "whiteAlpha.100",
}}
_active={{
bg: "whiteAlpha.200",
}}
>
Disconnect
</MenuItem>
michalsmiarowski marked this conversation as resolved.
Show resolved Hide resolved
</MenuList>
</Menu>
</HStack>
</>
) : (
<Button
variant="outline"
leftIcon={<PlusIcon />}
onClick={onConnectClick}
size="sm"
>
Connect Wallet
</Button>
)}
</HStack>
)
}
52 changes: 52 additions & 0 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Box, Flex } from "@threshold-network/components"
import { useWeb3React } from "@web3-react/core"
import { BigNumber } from "ethers"
import { ModalType, Token } from "../../enums"
import { useModal } from "../../hooks/useModal"
import { useToken } from "../../hooks/useToken"
import { Logo } from "../Logo"
import { NavigationMenu, UserPanel } from "./Header.children"

// TODO: Load new fonts
Copy link
Contributor

Choose a reason for hiding this comment

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

What fonts do we want to load here? Is it something we want to address in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's reasonable to do it in separate PR to prevent blocking this PR.
I wasn't able to make it - not sure how Chakra theming works in terms of font setting.
https://discord.com/channels/893441201628405760/1181551917332185148/1181634992409944204

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already applied font changes. Ref PR: #30


function Header() {
const {
active: isConnected,
deactivate: handleWalletDisconnection,
account: accountAddress,
chainId,
} = useWeb3React()
const { openModal } = useModal()
const handleWalletConnection = () => openModal(ModalType.SelectWallet)
const { balance } = useToken(Token.TBTCV2)

return (
<Box
bg={"black"}
color={"white"}
borderBottom={"1px solid"}
borderColor={"border.100"}
>
<Flex maxW={"1920px"} mx={"auto"} alignItems={"center"} px={10} h={24}>
michalsmiarowski marked this conversation as resolved.
Show resolved Hide resolved
<Logo />
<NavigationMenu
items={[
{ label: "Bridge", to: "/tBTC/mint" },
{ label: "Collect", to: "/collect" },
{ label: "Earn", to: "/earn" },
]}
/>
<UserPanel
isConnected={isConnected}
accountAddress={accountAddress}
balance={balance}
chainId={chainId}
onConnectClick={handleWalletConnection}
onDisconnectClick={handleWalletDisconnection}
/>
</Flex>
</Box>
)
}

export default Header
1 change: 1 addition & 0 deletions src/components/Header/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Header } from "./Header"
Loading