mirror of
https://github.com/trezor/trezor-wallet
synced 2025-01-03 21:00:55 +00:00
add responsive sidebar
This commit is contained in:
parent
bc38fd3ade
commit
d80e108754
@ -40,6 +40,8 @@ export type WalletAction = {
|
||||
devices: Array<TrezorDevice>
|
||||
} | {
|
||||
type: typeof WALLET.SHOW_BETA_DISCLAIMER | typeof WALLET.HIDE_BETA_DISCLAIMER | typeof WALLET.SET_FIRST_LOCATION_CHANGE,
|
||||
} | {
|
||||
type: typeof WALLET.TOGGLE_SIDEBAR,
|
||||
}
|
||||
|
||||
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
@ -62,6 +64,10 @@ export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
|
||||
opened,
|
||||
});
|
||||
|
||||
export const toggleSidebar = (): WalletAction => ({
|
||||
type: WALLET.TOGGLE_SIDEBAR,
|
||||
});
|
||||
|
||||
// This method will be called after each DEVICE.CONNECT action
|
||||
// if connected device has different "passphrase_protection" settings than saved instances
|
||||
// all saved instances will be removed immediately inside DevicesReducer
|
||||
|
@ -10,4 +10,6 @@ export const UPDATE_SELECTED_DEVICE: 'wallet__update_selected_device' = 'wallet_
|
||||
export const SHOW_BETA_DISCLAIMER: 'wallet__show_beta_disclaimer' = 'wallet__show_beta_disclaimer';
|
||||
export const HIDE_BETA_DISCLAIMER: 'wallet__hide_beta_disclaimer' = 'wallet__hide_beta_disclaimer';
|
||||
|
||||
export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_data' = 'wallet__clear_unavailable_device_data';
|
||||
export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_data' = 'wallet__clear_unavailable_device_data';
|
||||
|
||||
export const TOGGLE_SIDEBAR: 'wallet__toggle_sidebar' = 'wallet__toggle_sidebar';
|
@ -3,11 +3,15 @@ import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import colors from 'config/colors';
|
||||
import { SCREEN_SIZE } from 'config/variables';
|
||||
import type { toggleSidebar as toggleSidebarType } from 'actions/WalletActions';
|
||||
|
||||
const Wrapper = styled.header`
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
background: ${colors.HEADER};
|
||||
overflow: hidden;
|
||||
z-index: 200;
|
||||
|
||||
svg {
|
||||
fill: ${colors.WHITE};
|
||||
@ -30,13 +34,47 @@ const LayoutWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Left = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
const MenuToggler = styled.div`
|
||||
display: none;
|
||||
flex: 0 0 33%;
|
||||
white-space: nowrap;
|
||||
color: ${colors.WHITE};
|
||||
align-self: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 10px 0px;
|
||||
transition: all .1s ease-in;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
display: initial;
|
||||
}
|
||||
`;
|
||||
|
||||
const Right = styled.div``;
|
||||
const Logo = styled.div`
|
||||
flex: 1;
|
||||
justify-content: flex-start;
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
flex: 1 0 33%;
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const MenuLinks = styled.div`
|
||||
flex: 0;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
flex: 0 1 33%;
|
||||
}
|
||||
`;
|
||||
|
||||
const Projects = styled.div`
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const A = styled.a`
|
||||
color: ${colors.WHITE};
|
||||
@ -58,10 +96,17 @@ const A = styled.a`
|
||||
}
|
||||
`;
|
||||
|
||||
const Header = (): React$Element<string> => (
|
||||
type Props = {
|
||||
sidebarOpened?: boolean,
|
||||
toggleSidebar?: toggleSidebarType,
|
||||
|
||||
};
|
||||
|
||||
const Header = ({ sidebarOpened, toggleSidebar }: Props) => (
|
||||
<Wrapper>
|
||||
<LayoutWrapper>
|
||||
<Left>
|
||||
<MenuToggler onClick={toggleSidebar}>{sidebarOpened ? '✕ Close' : '☰ Menu'}</MenuToggler>
|
||||
<Logo>
|
||||
<NavLink to="/">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 163.7 41.9" width="100%" height="100%" preserveAspectRatio="xMinYMin meet">
|
||||
<polygon points="101.1,12.8 118.2,12.8 118.2,17.3 108.9,29.9 118.2,29.9 118.2,35.2 101.1,35.2 101.1,30.7 110.4,18.1 101.1,18.1" />
|
||||
@ -73,13 +118,15 @@ const Header = (): React$Element<string> => (
|
||||
<polygon points="40.5,12.8 58.6,12.8 58.6,18.1 52.4,18.1 52.4,35.2 46.6,35.2 46.6,18.1 40.5,18.1 " />
|
||||
</svg>
|
||||
</NavLink>
|
||||
</Left>
|
||||
<Right>
|
||||
<A href="https://trezor.io/" target="_blank" rel="noreferrer noopener">Trezor</A>
|
||||
<A href="https://wiki.trezor.io/" target="_blank" rel="noreferrer noopener">Wiki</A>
|
||||
<A href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</A>
|
||||
<A href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</A>
|
||||
</Right>
|
||||
</Logo>
|
||||
<MenuLinks>
|
||||
<Projects>
|
||||
<A href="https://trezor.io/" target="_blank" rel="noreferrer noopener">Trezor</A>
|
||||
<A href="https://wiki.trezor.io/" target="_blank" rel="noreferrer noopener">Wiki</A>
|
||||
<A href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</A>
|
||||
<A href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</A>
|
||||
</Projects>
|
||||
</MenuLinks>
|
||||
</LayoutWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ import { DEVICE, TRANSPORT } from 'trezor-connect';
|
||||
import * as MODAL from 'actions/constants/modal';
|
||||
import * as WALLET from 'actions/constants/wallet';
|
||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||
import * as ACCOUNT from 'actions/constants/account';
|
||||
|
||||
import type { Action, RouterLocationState, TrezorDevice } from 'flowtype';
|
||||
|
||||
@ -14,6 +15,7 @@ type State = {
|
||||
online: boolean;
|
||||
dropdownOpened: boolean;
|
||||
showBetaDisclaimer: boolean;
|
||||
showSidebar: boolean;
|
||||
initialParams: ?RouterLocationState;
|
||||
initialPathname: ?string;
|
||||
firstLocationChange: boolean;
|
||||
@ -27,6 +29,7 @@ const initialState: State = {
|
||||
dropdownOpened: false,
|
||||
firstLocationChange: true,
|
||||
showBetaDisclaimer: false,
|
||||
showSidebar: true,
|
||||
initialParams: null,
|
||||
initialPathname: null,
|
||||
disconnectRequest: null,
|
||||
@ -71,6 +74,11 @@ export default function wallet(state: State = initialState, action: Action): Sta
|
||||
...state,
|
||||
dropdownOpened: false,
|
||||
};
|
||||
case ACCOUNT.UPDATE_SELECTED_ACCOUNT:
|
||||
return {
|
||||
...state,
|
||||
showSidebar: false,
|
||||
};
|
||||
|
||||
case CONNECT.DISCONNECT_REQUEST:
|
||||
return {
|
||||
@ -94,6 +102,12 @@ export default function wallet(state: State = initialState, action: Action): Sta
|
||||
selectedDevice: action.device,
|
||||
};
|
||||
|
||||
case WALLET.TOGGLE_SIDEBAR:
|
||||
return {
|
||||
...state,
|
||||
showSidebar: !state.showSidebar,
|
||||
};
|
||||
|
||||
case WALLET.SHOW_BETA_DISCLAIMER:
|
||||
return {
|
||||
...state,
|
||||
|
@ -4,7 +4,7 @@ import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import Loader from 'components/Loader';
|
||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||
import { FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE } from 'config/variables';
|
||||
import { H1 } from 'components/Heading';
|
||||
import P from 'components/Paragraph';
|
||||
import colors from 'config/colors';
|
||||
@ -26,6 +26,10 @@ const Wrapper = styled.div`
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
padding: 40px 35px 40px 35px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}){
|
||||
padding: 20px 35px;
|
||||
}
|
||||
`;
|
||||
|
||||
const Loading = styled.div`
|
||||
|
@ -0,0 +1,69 @@
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import colors from 'config/colors';
|
||||
import { SCREEN_SIZE } from 'config/variables';
|
||||
import { SLIDE_RIGHT, SLIDE_LEFT } from 'config/animations';
|
||||
|
||||
|
||||
type Props = {
|
||||
children?: React.Node,
|
||||
isOpen: boolean,
|
||||
}
|
||||
|
||||
type State = {
|
||||
footerFixed: boolean,
|
||||
}
|
||||
|
||||
const AbsoluteWrapper = styled.aside`
|
||||
width: 320px;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
|
||||
background: ${colors.MAIN};
|
||||
border-top-left-radius: 4px;
|
||||
border-right: 1px solid ${colors.DIVIDER};
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
position: absolute;
|
||||
height: calc(100% - 52px);
|
||||
z-index: 200;
|
||||
top: 52px;
|
||||
animation: ${props => (props.isOpen ? SLIDE_RIGHT : SLIDE_LEFT)} 0.25s cubic-bezier(0.17, 0.04, 0.03, 0.94) forwards;
|
||||
}
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.LG}) {
|
||||
border-top-left-radius: 0px;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const MobileSidebarWrapper = styled.div`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export default class MobileSidebar extends React.PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
footerFixed: false,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<AbsoluteWrapper isOpen={this.props.isOpen}>
|
||||
<MobileSidebarWrapper>
|
||||
{React.Children.map(this.props.children, (child) => { // eslint-disable-line arrow-body-style
|
||||
return child.key === 'sticky-footer' ? React.cloneElement(child, {
|
||||
position: this.state.footerFixed ? 'fixed' : 'relative',
|
||||
}) : child;
|
||||
})}
|
||||
</MobileSidebarWrapper>
|
||||
</AbsoluteWrapper>
|
||||
);
|
||||
}
|
||||
}
|
@ -15,12 +15,13 @@ import * as deviceUtils from 'utils/device';
|
||||
import AccountMenu from './components/AccountMenu';
|
||||
import CoinMenu from './components/CoinMenu';
|
||||
import DeviceMenu from './components/DeviceMenu';
|
||||
import StickyContainer from './components/StickyContainer';
|
||||
import MobileSidebar from './components/MobileSidebar';
|
||||
|
||||
import type { Props } from './components/common';
|
||||
|
||||
const Header = styled(DeviceHeader)`
|
||||
border-right: 1px solid ${colors.BACKGROUND};
|
||||
flex: 0 0 auto;
|
||||
`;
|
||||
|
||||
const Counter = styled.div`
|
||||
@ -56,6 +57,7 @@ const Footer = styled.div.attrs(props => ({
|
||||
`;
|
||||
|
||||
const Body = styled.div`
|
||||
flex: 1 0 auto;
|
||||
width: 320px;
|
||||
min-height: ${props => (props.minHeight ? `${props.minHeight}px` : '0px')};
|
||||
`;
|
||||
@ -201,8 +203,10 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
|
||||
const { selectedDevice, dropdownOpened } = props.wallet;
|
||||
const isDeviceAccessible = deviceUtils.isDeviceAccessible(selectedDevice);
|
||||
const SidebarComponent = MobileSidebar;
|
||||
return (
|
||||
<StickyContainer
|
||||
<SidebarComponent
|
||||
isOpen={props.wallet.showSidebar}
|
||||
location={props.router.location.pathname}
|
||||
deviceSelection={this.props.wallet.dropdownOpened}
|
||||
>
|
||||
@ -249,7 +253,7 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
</A>
|
||||
</Help>
|
||||
</Footer>
|
||||
</StickyContainer>
|
||||
</SidebarComponent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,35 +6,44 @@ import styled from 'styled-components';
|
||||
import { connect } from 'react-redux';
|
||||
import { Route, withRouter } from 'react-router-dom';
|
||||
|
||||
import type { MapStateToProps } from 'react-redux';
|
||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import type { State } from 'flowtype';
|
||||
|
||||
import type { WalletAction } from 'actions/WalletActions';
|
||||
import { toggleSidebar } from 'actions/WalletActions';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import Header from 'components/Header';
|
||||
import Footer from 'components/Footer';
|
||||
import ModalContainer from 'components/modals/Container';
|
||||
import AppNotifications from 'components/notifications/App';
|
||||
import ContextNotifications from 'components/notifications/Context';
|
||||
|
||||
import { SCREEN_SIZE } from 'config/variables';
|
||||
|
||||
import Log from 'components/Log';
|
||||
import Backdrop from 'components/Backdrop';
|
||||
|
||||
import LeftNavigation from './components/LeftNavigation/Container';
|
||||
import TopNavigationAccount from './components/TopNavigationAccount';
|
||||
import TopNavigationDeviceSettings from './components/TopNavigationDeviceSettings';
|
||||
|
||||
|
||||
type WalletContainerProps = {
|
||||
type StateProps = {
|
||||
wallet: $ElementType<State, 'wallet'>,
|
||||
children?: React.Node
|
||||
children?: React.Node,
|
||||
}
|
||||
|
||||
// type ContentProps = {
|
||||
// children?: React.Node
|
||||
// }
|
||||
type DispatchProps = {
|
||||
toggleSidebar: WalletAction,
|
||||
};
|
||||
|
||||
type OwnProps = {};
|
||||
|
||||
export type Props = StateProps & DispatchProps;
|
||||
|
||||
const AppWrapper = styled.div`
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
min-width: 720px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${colors.BACKGROUND};
|
||||
@ -87,11 +96,20 @@ const Body = styled.div`
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const Wallet = (props: WalletContainerProps) => (
|
||||
const StyledBackdrop = styled(Backdrop)`
|
||||
display: none;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
display: initial;
|
||||
}
|
||||
`;
|
||||
|
||||
const Wallet = (props: Props) => (
|
||||
<AppWrapper>
|
||||
<Header />
|
||||
<Header sidebarOpened={props.wallet.showSidebar} toggleSidebar={props.toggleSidebar} />
|
||||
<AppNotifications />
|
||||
<WalletWrapper>
|
||||
<StyledBackdrop show={props.wallet.showSidebar} onClick={props.toggleSidebar} animated />
|
||||
{props.wallet.selectedDevice && <LeftNavigation />}
|
||||
<MainContent>
|
||||
<Navigation>
|
||||
@ -110,10 +128,14 @@ const Wallet = (props: WalletContainerProps) => (
|
||||
</AppWrapper>
|
||||
);
|
||||
|
||||
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State): WalletContainerProps => ({
|
||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
|
||||
wallet: state.wallet,
|
||||
});
|
||||
|
||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||
toggleSidebar: bindActionCreators(toggleSidebar, dispatch),
|
||||
});
|
||||
|
||||
export default withRouter(
|
||||
connect(mapStateToProps, null)(Wallet),
|
||||
connect(mapStateToProps, mapDispatchToProps)(Wallet),
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user