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 { ApolloProvider } from '@apollo/client';
|
||||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||||
@ -9,14 +9,14 @@ import {
|
|||||||
VegaManageDialog,
|
VegaManageDialog,
|
||||||
VegaWalletProvider,
|
VegaWalletProvider,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
// import { DealTicketContainer } from './components/deal-ticket';
|
|
||||||
import { VegaWalletConnectButton } from './components/vega-wallet-connect-button';
|
import { VegaWalletConnectButton } from './components/vega-wallet-connect-button';
|
||||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
|
||||||
import { Connectors } from './lib/vega-connectors';
|
import { Connectors } from './lib/vega-connectors';
|
||||||
import '../styles.scss';
|
import '../styles.scss';
|
||||||
import { AppLoader } from './components/app-loader';
|
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() {
|
function App() {
|
||||||
const [theme, toggleTheme] = useThemeSwitcher();
|
const [theme, toggleTheme] = useThemeSwitcher();
|
||||||
@ -27,13 +27,28 @@ function App() {
|
|||||||
|
|
||||||
const client = useMemo(() => createClient(DATA_SOURCES.dataNodeUrl), []);
|
const client = useMemo(() => createClient(DATA_SOURCES.dataNodeUrl), []);
|
||||||
|
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const onToggle = () => setMenuOpen(!menuOpen);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMenuOpen(false);
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={theme}>
|
<ThemeContext.Provider value={theme}>
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<VegaWalletProvider>
|
<VegaWalletProvider>
|
||||||
<AppLoader>
|
<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="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 md:col-span-3">
|
<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">
|
<div className="flex items-center gap-4 ml-auto mr-8">
|
||||||
<VegaWalletConnectButton
|
<VegaWalletConnectButton
|
||||||
setConnectDialog={(open) =>
|
setConnectDialog={(open) =>
|
||||||
@ -47,24 +62,8 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside className="md:col-start-1 md:col-end-1 md:row-start-2 md:row-end-2">
|
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
|
||||||
<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>
|
|
||||||
|
|
||||||
<footer className="md:col-span-3">®</footer>
|
|
||||||
<VegaConnectDialog
|
<VegaConnectDialog
|
||||||
connectors={Connectors}
|
connectors={Connectors}
|
||||||
dialogOpen={vegaWallet.connect}
|
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