ghost-portal/packages/apps/src/SideBar/index.tsx
2020-04-01 15:30:08 +02:00

291 lines
7.2 KiB
TypeScript

// Copyright 2017-2020 @polkadot/apps authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { SIDEBAR_MENU_THRESHOLD } from '../constants';
import React, { useCallback, useState } from 'react';
import styled from 'styled-components';
import { Responsive } from 'semantic-ui-react';
import routing from '@polkadot/apps-routing';
import { Button, ChainImg, Icon, Menu, media } from '@polkadot/react-components';
import { classes } from '@polkadot/react-components/util';
import NetworkModal from '../modals/Network';
import { useTranslation } from '../translate';
import ChainInfo from './ChainInfo';
import Item from './Item';
import NodeInfo from './NodeInfo';
interface Props {
className?: string;
collapse: () => void;
handleResize: () => void;
isCollapsed: boolean;
isMenuOpen: boolean;
toggleMenu: () => void;
}
function SideBar ({ className, collapse, handleResize, isCollapsed, isMenuOpen, toggleMenu }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [modals, setModals] = useState<Record<string, boolean>>(
routing.routes.reduce((result: Record<string, boolean>, route): Record<string, boolean> => {
if (route && route.Modal) {
result[route.name] = false;
}
return result;
}, { network: false })
);
const _toggleModal = useCallback(
(name: string): () => void =>
(): void => setModals((modals: Record<string, boolean>) => ({
...modals,
[name]: !modals[name]
})),
[]
);
return (
<Responsive
className={classes(className, 'apps--SideBar-Wrapper', isCollapsed ? 'collapsed' : 'expanded')}
onUpdate={handleResize}
>
<ChainImg
className={`toggleImg ${isMenuOpen ? 'closed' : 'open delayed'}`}
onClick={toggleMenu}
/>
{routing.routes.map((route): React.ReactNode => (
route?.Modal
? route.Modal && modals[route.name]
? (
<route.Modal
key={route.name}
onClose={_toggleModal(route.name)}
/>
)
: <div key={route.name} />
: null
))}
{modals.network && (
<NetworkModal onClose={_toggleModal('network')}/>
)}
<div className='apps--SideBar'>
<Menu
secondary
vertical
>
<div className='apps--SideBar-Scroll'>
<ChainInfo onClick={_toggleModal('network')} />
{routing.routes.map((route, index): React.ReactNode => (
route
? (
<Item
isCollapsed={isCollapsed}
key={route.name}
onClick={
route.Modal
? _toggleModal(route.name)
: handleResize
}
route={route}
/>
)
: (
<Menu.Divider
hidden
key={index}
/>
)
))}
<Menu.Divider hidden />
<Menu.Item className='apps--SideBar-Item'>
<a
className='apps--SideBar-Item-NavLink'
href='https://github.com/polkadot-js/apps'
rel='noopener noreferrer'
target='_blank'
>
<Icon name='github' /><span className='text'>{t('GitHub')}</span>
</a>
</Menu.Item>
<Menu.Item className='apps--SideBar-Item'>
<a
className='apps--SideBar-Item-NavLink'
href='https://wiki.polkadot.network'
rel='noopener noreferrer'
target='_blank'
>
<Icon name='book' /><span className='text'>{t('Wiki')}</span>
</a>
</Menu.Item>
<Menu.Divider hidden />
{
isCollapsed
? undefined
: <NodeInfo />
}
</div>
<Responsive
className={`apps--SideBar-collapse ${isCollapsed ? 'collapsed' : 'expanded'}`}
minWidth={SIDEBAR_MENU_THRESHOLD}
>
<Button
icon={`angle double ${isCollapsed ? 'right' : 'left'}`}
isBasic
isCircular
onClick={collapse}
/>
</Responsive>
</Menu>
<Responsive minWidth={SIDEBAR_MENU_THRESHOLD}>
<div
className='apps--SideBar-toggle'
onClick={collapse}
/>
</Responsive>
</div>
</Responsive>
);
}
const sideBorderWidth = '0.65rem';
export default React.memo(styled(SideBar)`
display: flex;
position: relative;
transition: width 0.3s linear;
z-index: 300;
&.collapsed {
width: 4.2rem;
}
&.expanded {
width: 12rem;
}
.apps--SideBar {
align-items: center;
background: #4f4f4f;
box-sizing: border-box;
display: flex;
flex-flow: column;
height: auto;
position: relative;
transition: left 0.3s linear;
width: 100%;
.apps--SideBar-border {
border-top: ${sideBorderWidth} solid transparent;
position: absolute;
left: 0;
right: 0;
top: 0;
}
.ui.vertical.menu {
display: flex;
height: 100vh;
margin: 0;
top: 0;
width: 100%;
position: sticky;
}
.apps--SideBar-Scroll {
align-items: center;
display: flex;
flex-direction: column;
height: 100vh;
overflow-y: auto;
width: 100%;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
width: 0px;
}
}
.apps--SideBar-Item {
align-self: flex-end;
flex-grow: 0;
padding: 0 !important;
position: relative;
width: inherit;
.text {
padding-left: 0.5rem;
}
.ui--Badge {
margin: 0;
position: absolute;
right: 0.5rem;
top: 0.55rem;
z-index: 1;
}
}
.apps--SideBar-collapse {
background: #4f4f4f;
bottom: 0;
left: 0;
padding: 0.75rem 0 .75rem 0.65rem;
position: sticky;
right: 0;
text-align: left;
width: 100%;
.ui.circular.button {
background: white !important;
color: #3f3f3f !important;
box-shadow: 0 0 0 1px #eee inset !important;
margin: 0;
transition: transform 0.15s;
}
}
.apps--SideBar-toggle {
height: 100%;
position: absolute;
right: 0px;
top: 0px;
transition: all 0.2s;
width: 6px;
&:hover {
background: rgba(255,255,255,0.15);
cursor: pointer;
}
}
}
.toggleImg {
cursor: pointer;
height: 2.75rem;
left: 0.9rem;
opacity: 0;
position: absolute;
top: 0px;
transition: opacity 0.2s ease-in, top 0.2s ease-in;
width: 2.75rem;
&.delayed {
transition-delay: 0.4s;
}
&.open {
opacity: 1;
top: 0.9rem;
}
${media.DESKTOP`
opacity: 0 !important;
top: -2.9rem !important;
`}
}
`);