Feature/simple trading drawer (#435)
* feat(simple-trading-app): add drawer component to ui-toolkit * feat(simple-trading-app): use drawer component and add navigation routes * feat(simple-trading-app): move drawer out of ui-toolkit and into simple trading * feat(simple-trading-app): remove suspense for now as nothing gets lazy loaded * feat(simple-trading-app): add simple market list into routes
This commit is contained in:
parent
497d3738ce
commit
d9fccb137e
@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
@ -9,14 +9,14 @@ import {
|
||||
VegaManageDialog,
|
||||
VegaWalletProvider,
|
||||
} from '@vegaprotocol/wallet';
|
||||
// import { DealTicketContainer } from './components/deal-ticket';
|
||||
import { VegaWalletConnectButton } from './components/vega-wallet-connect-button';
|
||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Connectors } from './lib/vega-connectors';
|
||||
import '../styles.scss';
|
||||
import { AppLoader } from './components/app-loader';
|
||||
import SimpleMarketList from './components/simple-market-list';
|
||||
import { Main } from './components/main';
|
||||
import { DrawerToggle, DRAWER_TOGGLE_VARIANTS } from './components/drawer';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
function App() {
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
@ -27,13 +27,28 @@ function App() {
|
||||
|
||||
const client = useMemo(() => createClient(DATA_SOURCES.dataNodeUrl), []);
|
||||
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const onToggle = () => setMenuOpen(!menuOpen);
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
setMenuOpen(false);
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<ApolloProvider client={client}>
|
||||
<VegaWalletProvider>
|
||||
<AppLoader>
|
||||
<div className="h-full max-h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid md:grid-rows-[min-content_1fr_min-content] lg:grid-cols-[375px_1fr] md:grid-cols-[200px_1fr] sm:grid-rows-[min-content_min-content_1fr_min-content]">
|
||||
<div className="flex items-stretch border-b-[7px] border-vega-yellow md:col-span-3">
|
||||
<div className="h-full dark:bg-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
|
||||
<div className="flex items-stretch border-b-[7px] border-vega-yellow">
|
||||
<DrawerToggle
|
||||
onToggle={onToggle}
|
||||
variant={DRAWER_TOGGLE_VARIANTS.OPEN}
|
||||
className="xs:py-32 xs:px-16"
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-4 ml-auto mr-8">
|
||||
<VegaWalletConnectButton
|
||||
setConnectDialog={(open) =>
|
||||
@ -47,24 +62,8 @@ function App() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside className="md:col-start-1 md:col-end-1 md:row-start-2 md:row-end-2">
|
||||
<ul>
|
||||
<li>{t('Markets')}</li>
|
||||
<li>{t('Trade')}</li>
|
||||
<li>{t('Liquid')}</li>
|
||||
<li>{t('Markets')}</li>
|
||||
</ul>
|
||||
</aside>
|
||||
<div className="md:col-start-2 md:col-end-2 md:row-start-2 md:row-end-2 overflow-auto">
|
||||
<SimpleMarketList />
|
||||
{/*<DealTicketContainer
|
||||
marketId={
|
||||
'0e4c4e0ce6626ea5c6bf5b5b510afadb3c91627aa9ff61e4c7e37ef8394f2c6f'
|
||||
}
|
||||
/>*/}
|
||||
</div>
|
||||
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
|
||||
|
||||
<footer className="md:col-span-3">®</footer>
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={vegaWallet.connect}
|
||||
|
@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactElement | ReactElement[];
|
||||
}
|
||||
|
||||
export const DrawerContainer = ({ children }: Props) => (
|
||||
<div className="w-full md:w-3/4 grow-1">{children}</div>
|
||||
);
|
||||
|
||||
export const DrawerWrapper = ({ children }: Props) => (
|
||||
<div className="flex">{children}</div>
|
||||
);
|
@ -0,0 +1,38 @@
|
||||
import * as React from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ButtonProps } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
type MenuItem = {
|
||||
label: string;
|
||||
component: ReactElement | ReactElement[];
|
||||
onClick(): void;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Menu items passed as an array
|
||||
*/
|
||||
menuItems: MenuItem[];
|
||||
}
|
||||
|
||||
export const NavigationDrawerMenu = ({ menuItems }: Props) => {
|
||||
return (
|
||||
<ul>
|
||||
{menuItems.map((item, index) => {
|
||||
const btnProps = {
|
||||
variant: item.active ? 'accent' : 'primary',
|
||||
className: 'w-full mb-8',
|
||||
onClick: item.onClick,
|
||||
} as ButtonProps;
|
||||
|
||||
return (
|
||||
<li key={index}>
|
||||
<Button {...btnProps}>{item.label}</Button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
import * as React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import type { IconName } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const enum DRAWER_TOGGLE_VARIANTS {
|
||||
OPEN = 'open',
|
||||
CLOSE = 'close',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onToggle: React.MouseEventHandler<HTMLButtonElement>;
|
||||
variant: DRAWER_TOGGLE_VARIANTS.OPEN | DRAWER_TOGGLE_VARIANTS.CLOSE;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const DrawerToggle = ({
|
||||
onToggle,
|
||||
variant = DRAWER_TOGGLE_VARIANTS.CLOSE,
|
||||
className,
|
||||
}: Props) => {
|
||||
const [iconName, setIconName] = useState(IconNames.MENU);
|
||||
const classes = classNames('md:hidden', {
|
||||
[className as string]: className,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (variant === DRAWER_TOGGLE_VARIANTS.OPEN) {
|
||||
setIconName(IconNames.MENU);
|
||||
} else {
|
||||
setIconName(IconNames.CROSS);
|
||||
}
|
||||
}, [variant]);
|
||||
|
||||
return (
|
||||
<Button variant="inline" className={classes} onClick={onToggle}>
|
||||
<Icon name={iconName as IconName} />
|
||||
</Button>
|
||||
);
|
||||
};
|
66
apps/simple-trading-app/src/app/components/drawer/drawer.tsx
Normal file
66
apps/simple-trading-app/src/app/components/drawer/drawer.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import * as React from 'react';
|
||||
import { theme } from '@vegaprotocol/tailwindcss-config';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
|
||||
interface Props {
|
||||
children?: ReactElement | ReactElement[];
|
||||
isMenuOpen?: boolean;
|
||||
onToggle(): void;
|
||||
}
|
||||
|
||||
export const NavigationDrawer = ({
|
||||
isMenuOpen = false,
|
||||
onToggle,
|
||||
children,
|
||||
}: Props) => {
|
||||
const [windowSize, setWindowSize] = React.useState({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
const mobileScreenWidth = parseInt(theme.screens.md);
|
||||
const isMobile = windowSize.width <= mobileScreenWidth;
|
||||
const timeout = React.useRef(0);
|
||||
|
||||
const handleResize = () => {
|
||||
if (timeout.current) {
|
||||
window.cancelAnimationFrame(timeout.current);
|
||||
}
|
||||
|
||||
// Setup the new requestAnimationFrame()
|
||||
timeout.current = window.requestAnimationFrame(function () {
|
||||
setWindowSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
window.cancelAnimationFrame(timeout.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const drawerRootClasses = {
|
||||
root: 'w-3/4 md:w-1/4 shrink-0',
|
||||
paper: 'p-16 w-3/4 md:w-1/4 box-border',
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
classes={drawerRootClasses}
|
||||
variant={isMobile ? 'temporary' : 'permanent'}
|
||||
open={isMobile ? isMenuOpen : true}
|
||||
onClose={onToggle}
|
||||
ModalProps={{
|
||||
keepMounted: true, // Better open performance on mobile.
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
@ -0,0 +1,4 @@
|
||||
export * from './drawer';
|
||||
export * from './drawer-menu';
|
||||
export * from './drawer-toggle';
|
||||
export * from './drawer-container';
|
62
apps/simple-trading-app/src/app/components/main/index.tsx
Normal file
62
apps/simple-trading-app/src/app/components/main/index.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import { useRoutes, NavLink } from 'react-router-dom';
|
||||
import {
|
||||
NavigationDrawer,
|
||||
DrawerWrapper,
|
||||
DrawerContainer,
|
||||
DrawerToggle,
|
||||
DRAWER_TOGGLE_VARIANTS,
|
||||
} from '../drawer';
|
||||
import { routerConfig } from '../../routes/router-config';
|
||||
|
||||
export interface RouteChildProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const AppRouter = () => {
|
||||
const routes = useRoutes(routerConfig);
|
||||
|
||||
return <main className="p-20 overflow-hidden">{routes}</main>;
|
||||
};
|
||||
|
||||
export const Menu = () => (
|
||||
<nav>
|
||||
{routerConfig.map((r) => (
|
||||
<NavLink
|
||||
key={r.name}
|
||||
to={r.path}
|
||||
className={({ isActive }) =>
|
||||
`text-h5 block mb-8 px-8 hover:bg-vega-yellow hover:text-black ${
|
||||
isActive && 'bg-vega-yellow text-black'
|
||||
}`
|
||||
}
|
||||
>
|
||||
{r.text}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
|
||||
interface Props {
|
||||
onToggle(): void;
|
||||
|
||||
isMenuOpen: boolean;
|
||||
}
|
||||
|
||||
export const Main = ({ onToggle, isMenuOpen }: Props) => {
|
||||
return (
|
||||
<DrawerWrapper>
|
||||
<NavigationDrawer onToggle={onToggle} isMenuOpen={isMenuOpen}>
|
||||
<DrawerToggle
|
||||
onToggle={onToggle}
|
||||
variant={DRAWER_TOGGLE_VARIANTS.CLOSE}
|
||||
className="self-end p-16"
|
||||
/>
|
||||
<Menu />
|
||||
</NavigationDrawer>
|
||||
<DrawerContainer>
|
||||
<AppRouter />
|
||||
</DrawerContainer>
|
||||
</DrawerWrapper>
|
||||
);
|
||||
};
|
@ -1 +1 @@
|
||||
export { default } from './simple-market-list';
|
||||
export { default as SimpleMarketList } from './simple-market-list';
|
||||
|
1
apps/simple-trading-app/src/app/routes/index.ts
Normal file
1
apps/simple-trading-app/src/app/routes/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './router-config';
|
50
apps/simple-trading-app/src/app/routes/router-config.tsx
Normal file
50
apps/simple-trading-app/src/app/routes/router-config.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { DealTicketContainer } from '../components/deal-ticket';
|
||||
import { SimpleMarketList } from '../components/simple-market-list';
|
||||
|
||||
export const ROUTES = {
|
||||
HOME: '/',
|
||||
MARKETS: 'markets',
|
||||
TRADING: 'trading',
|
||||
LIQUIDITY: 'liquidity',
|
||||
PORTFOLIO: 'portfolio',
|
||||
};
|
||||
|
||||
export const routerConfig = [
|
||||
{
|
||||
path: ROUTES.HOME,
|
||||
name: 'Home',
|
||||
text: t('Home'),
|
||||
element: <div>Home</div>,
|
||||
},
|
||||
{
|
||||
path: ROUTES.MARKETS,
|
||||
name: 'Markets',
|
||||
text: t('Markets'),
|
||||
element: <SimpleMarketList />,
|
||||
},
|
||||
{
|
||||
path: ROUTES.TRADING,
|
||||
name: 'Trading',
|
||||
text: t('Trading'),
|
||||
element: (
|
||||
<DealTicketContainer
|
||||
marketId={
|
||||
'41013c28d53a72225c07cf2660cdd415d9dd0e9317ec4574e77592332db35596'
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ROUTES.LIQUIDITY,
|
||||
name: 'Liquidity',
|
||||
text: t('Liquidity'),
|
||||
element: <div>Liquidity</div>,
|
||||
},
|
||||
{
|
||||
path: ROUTES.PORTFOLIO,
|
||||
name: 'Portfolio',
|
||||
text: t('Portfolio'),
|
||||
element: <div>Portfolio</div>,
|
||||
},
|
||||
];
|
Loading…
Reference in New Issue
Block a user