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 22 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
2 changes: 1 addition & 1 deletion .env.example
michalsmiarowski marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ REACT_APP_ELECTRUM_HOST=$ELECTRUM_HOST
REACT_APP_ELECTRUM_PORT=$ELECTRUM_PORT
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>
)
62 changes: 62 additions & 0 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Box, BoxProps, Flex } from "@threshold-network/components"
import { useWeb3React } from "@web3-react/core"
import { FC } from "react"
import { ModalType, Token } from "../../enums"
import { useModal } from "../../hooks/useModal"
import { useToken } from "../../hooks/useToken"
import { Logo } from "../Logo"
import { NavigationMenu } from "./NavigationMenu"
import { UserPanel } from "./UserPanel"

// 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


export interface HeaderProps extends BoxProps {}

const Header: FC<HeaderProps> = (props) => {
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={"whiteAlpha.350"}
{...props}
>
<Flex
maxW={"1920px"}
mx={"auto"}
alignItems={"center"}
px={{ base: 2, lg: 10 }}
h={{ base: 16, lg: 24 }}
>
<Logo zIndex="popover" />
<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
199 changes: 199 additions & 0 deletions src/components/Header/NavigationMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import { spacing } from "@chakra-ui/theme/foundations/spacing"
import {
Button,
Drawer,
DrawerContent,
DrawerOverlay,
HStack,
Icon,
Link,
List,
ListItem,
ListItemProps,
StackDivider,
StackProps,
SystemStyleObject,
useDisclosure,
VisuallyHidden,
VStack,
} from "@threshold-network/components"
import { motion } from "framer-motion"
import { FC, useRef } from "react"
import { NavLink } from "react-router-dom"
import useChakraBreakpoint from "../../hooks/useChakraBreakpoint"

const NavigationMenuMobileContainer: FC<StackProps> = (props) => (
<VStack
divider={<StackDivider />}
{...props}
spacing={0}
position="fixed"
inset={0}
h="100vh"
py={16}
bgGradient="radial(circle at bottom right, #0A1616, #090909)"
borderLeft="1px solid"
borderColor="whiteAlpha.250"
alignItems="stretch"
/>
)

const NavigationMenuDesktopContainer: FC<StackProps> = (props) => (
<HStack
{...props}
spacing={0}
alignSelf="stretch"
alignItems="stretch"
ml={{
lg: `calc(${spacing[16]} - ${spacing[5]})`,
xl: `calc(142px - ${spacing[5]})`,
}}
mr="auto"
/>
)

const HamburgerIcon: FC<{ isToggled: boolean }> = ({ isToggled }) => (
<Icon
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
w={5}
h={5}
fill="none"
stroke="white"
stroke-width={1}
stroke-linecap="round"
stroke-linejoin="round"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be:

Suggested change
stroke-width={1}
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth={1}
strokeLinecap="round"
strokeLinejoin="round"

The actual implementation produces warnings in the console.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ref commit: 7bb9859

>
<motion.path
initial={false}
animate={{
rotate: isToggled ? -45 : 0,
y: isToggled ? "25%" : 0,
}}
d="M3 6h18"
/>
<motion.path
initial={false}
animate={{
scaleX: isToggled ? 0 : 1,
}}
d="M3 12h18"
/>
<motion.path
initial={false}
animate={{
rotate: isToggled ? 45 : 0,
y: isToggled ? "-25%" : 0,
}}
d="M3 18h18"
/>
</Icon>
)

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

export type NavigationMenuItemType = {
/** The label of the menu item */
label: string
/** The route to navigate to when the menu item is clicked */
to: string
}
interface NavigationMenuItemProps
extends ListItemProps,
NavigationMenuItemType {}

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

const renderNavigationMenuItems = (items: NavigationMenuItemType[]) =>
items.map((item) => <NavigationMenuItem {...item} key={item.to} />)

interface NavigationMenuProps extends StackProps {
/** The menu items to display */
items: NavigationMenuItemType[]
}

export const NavigationMenu: FC<NavigationMenuProps> = ({
items,
...restProps
}) => {
const { isOpen, onOpen, onClose } = useDisclosure()
const isMobile = useChakraBreakpoint("lg")
const buttonRef = useRef<HTMLButtonElement>(null)

return (
<>
{isMobile ? (
<Button
ref={buttonRef}
variant="unstyled"
order={-1}
onClick={isOpen ? onClose : onOpen}
zIndex="popover"
mr={2}
>
<VisuallyHidden>
{isOpen ? "Close" : "Open"} navigation menu
</VisuallyHidden>
<HamburgerIcon isToggled={isOpen} />
</Button>
) : null}
{isMobile ? (
<Drawer
isOpen={isOpen}
placement="right"
onClose={onClose}
size="sm"
finalFocusRef={buttonRef}
>
<DrawerOverlay backdropFilter="auto" backdropBlur="lg" />
<DrawerContent>
<NavigationMenuMobileContainer as={List} {...restProps}>
{renderNavigationMenuItems(items)}
</NavigationMenuMobileContainer>
</DrawerContent>
</Drawer>
) : (
<NavigationMenuDesktopContainer as={List} {...restProps}>
{renderNavigationMenuItems(items)}
</NavigationMenuDesktopContainer>
)}
</>
)
}
Loading