From d80e10875400777d4a05732b9ad9fdf4f9970316 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Thu, 24 Jan 2019 18:49:33 +0100 Subject: [PATCH] add responsive sidebar --- src/actions/WalletActions.js | 6 ++ src/actions/constants/wallet.js | 4 +- src/components/Header/index.js | 71 +++++++++++++++---- src/reducers/WalletReducer.js | 14 ++++ src/views/Wallet/components/Content/index.js | 6 +- .../components/MobileSidebar/index.js | 69 ++++++++++++++++++ .../Wallet/components/LeftNavigation/index.js | 10 ++- src/views/Wallet/index.js | 46 ++++++++---- 8 files changed, 197 insertions(+), 29 deletions(-) create mode 100644 src/views/Wallet/components/LeftNavigation/components/MobileSidebar/index.js diff --git a/src/actions/WalletActions.js b/src/actions/WalletActions.js index 9e5ea9f9..cfc23b12 100644 --- a/src/actions/WalletActions.js +++ b/src/actions/WalletActions.js @@ -40,6 +40,8 @@ export type WalletAction = { devices: Array } | { 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 diff --git a/src/actions/constants/wallet.js b/src/actions/constants/wallet.js index 04615371..40f0e914 100644 --- a/src/actions/constants/wallet.js +++ b/src/actions/constants/wallet.js @@ -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'; \ No newline at end of file +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'; \ No newline at end of file diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 823701a7..6c23f9f7 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -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` +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 Logo = styled.div` flex: 1; - display: flex; justify-content: flex-start; + display: flex; + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + flex: 1 0 33%; + justify-content: center; + } `; -const Right = styled.div``; +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 => ( +type Props = { + sidebarOpened?: boolean, + toggleSidebar?: toggleSidebarType, + +}; + +const Header = ({ sidebarOpened, toggleSidebar }: Props) => ( - + {sidebarOpened ? '✕ Close' : '☰ Menu'} + @@ -73,13 +118,15 @@ const Header = (): React$Element => ( - - - Trezor - Wiki - Blog - Support - + + + + Trezor + Wiki + Blog + Support + + ); diff --git a/src/reducers/WalletReducer.js b/src/reducers/WalletReducer.js index 8f07dc6e..35df8b18 100644 --- a/src/reducers/WalletReducer.js +++ b/src/reducers/WalletReducer.js @@ -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, diff --git a/src/views/Wallet/components/Content/index.js b/src/views/Wallet/components/Content/index.js index 150b84ff..62e0bbcb 100644 --- a/src/views/Wallet/components/Content/index.js +++ b/src/views/Wallet/components/Content/index.js @@ -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` diff --git a/src/views/Wallet/components/LeftNavigation/components/MobileSidebar/index.js b/src/views/Wallet/components/LeftNavigation/components/MobileSidebar/index.js new file mode 100644 index 00000000..7b9a0954 --- /dev/null +++ b/src/views/Wallet/components/LeftNavigation/components/MobileSidebar/index.js @@ -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 { + constructor(props: Props) { + super(props); + this.state = { + footerFixed: false, + }; + } + + render() { + return ( + + + {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; + })} + + + ); + } +} \ No newline at end of file diff --git a/src/views/Wallet/components/LeftNavigation/index.js b/src/views/Wallet/components/LeftNavigation/index.js index 5a2e7e83..e2139098 100644 --- a/src/views/Wallet/components/LeftNavigation/index.js +++ b/src/views/Wallet/components/LeftNavigation/index.js @@ -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 { const { selectedDevice, dropdownOpened } = props.wallet; const isDeviceAccessible = deviceUtils.isDeviceAccessible(selectedDevice); + const SidebarComponent = MobileSidebar; return ( - @@ -249,7 +253,7 @@ class LeftNavigation extends React.PureComponent { - + ); } } diff --git a/src/views/Wallet/index.js b/src/views/Wallet/index.js index f6063eb9..36888d8e 100644 --- a/src/views/Wallet/index.js +++ b/src/views/Wallet/index.js @@ -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, - 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) => ( -
+
+ {props.wallet.selectedDevice && } @@ -110,10 +128,14 @@ const Wallet = (props: WalletContainerProps) => ( ); -const mapStateToProps: MapStateToProps = (state: State): WalletContainerProps => ({ +const mapStateToProps: MapStateToProps = (state: State): StateProps => ({ wallet: state.wallet, }); +const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + toggleSidebar: bindActionCreators(toggleSidebar, dispatch), +}); + export default withRouter( - connect(mapStateToProps, null)(Wallet), + connect(mapStateToProps, mapDispatchToProps)(Wallet), );