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/Backdrop/index.js b/src/components/Backdrop/index.js new file mode 100644 index 00000000..328a7a40 --- /dev/null +++ b/src/components/Backdrop/index.js @@ -0,0 +1,37 @@ +import React from 'react'; +import styled, { css } from 'styled-components'; +import PropTypes from 'prop-types'; +import { FADE_IN } from 'config/animations'; + + +const StyledBackdrop = styled.div` + width: 100%; + height: 100%; + position: fixed; + z-index: 100; + left: 0; + top: 0; + background-color: rgba(0,0,0,0.5); + + ${props => props.animated && css` + animation: ${FADE_IN} 0.3s; + `}; +`; + +const Backdrop = ({ + className, + show, + animated, + onClick, +}) => ( + show ? : null +); + +Backdrop.propTypes = { + show: PropTypes.bool.isRequired, + className: PropTypes.string, + animated: PropTypes.bool, + onClick: PropTypes.func, +}; + +export default Backdrop; diff --git a/src/components/DeviceHeader/index.js b/src/components/DeviceHeader/index.js index 0742e4e3..bd3e6b4e 100644 --- a/src/components/DeviceHeader/index.js +++ b/src/components/DeviceHeader/index.js @@ -18,8 +18,10 @@ const Wrapper = styled.div` z-index: 10; display: flex; align-items: center; + padding: 0px 25px; background: ${props => (props.disabled ? colors.GRAY_LIGHT : 'transparent')}; background: ${props => (props.isSelected ? colors.WHITE : 'transparent')}; + cursor: pointer; border-radius: 4px 0 0 0; box-shadow: ${props => (props.disabled ? 'none' : '0 3px 8px rgba(0, 0, 0, 0.04)')}; @@ -28,6 +30,10 @@ const Wrapper = styled.div` box-shadow: none; `} + ${props => props.disabled && css` + cursor: default; + `} + ${props => props.isHoverable && !props.disabled && css` &:hover { background: ${colors.GRAY_LIGHT}; @@ -35,19 +41,6 @@ const Wrapper = styled.div` `} `; -const ClickWrapper = styled.div` - width: 100%; - display: flex; - padding-left: 25px; - height: 100%; - align-items: center; - cursor: pointer; - - ${props => props.disabled && css` - cursor: default; - `} -`; - const LabelWrapper = styled.div` flex: 1 1 auto; padding-left: 18px; @@ -73,7 +66,6 @@ const Status = styled.div` `; const IconWrapper = styled.div` - padding-right: 25px; display: flex; flex: 1 0 0; justify-content: flex-end; @@ -115,23 +107,19 @@ const DeviceHeader = ({ isHoverable={isHoverable} disabled={disabled} className={className} + onClick={onClickWrapper} > - - - - - - - {device.instanceLabel} - {getStatusName(status)} - - - {icon && !disabled && isAccessible && icon} - - + + + + + + {device.instanceLabel} + {getStatusName(status)} + + + {icon && !disabled && isAccessible && icon} + ); }; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 823701a7..ec5a767f 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}; @@ -31,12 +35,54 @@ const LayoutWrapper = styled.div` `; const Left = styled.div` + display: none; + flex: 0 0 33%; + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + display: initial; + } +`; + +const MenuToggler = styled.div` + display: none; + 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 MenuLinks = styled.div` + flex: 0; + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + flex: 0 1 33%; + } `; -const Right = styled.div``; +const Projects = styled.div` + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + display: none; + } +`; const A = styled.a` color: ${colors.WHITE}; @@ -58,10 +104,20 @@ const A = styled.a` } `; -const Header = (): React$Element => ( +type Props = { + sidebarEnabled?: boolean, + sidebarOpened?: boolean, + toggleSidebar?: toggleSidebarType, + +}; + +const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( + { sidebarEnabled && {sidebarOpened ? '✕ Close' : '☰ Menu'}} + + @@ -73,13 +129,15 @@ const Header = (): React$Element => ( - - - Trezor - Wiki - Blog - Support - + + + + Trezor + Wiki + Blog + Support + + ); diff --git a/src/components/Link/index.js b/src/components/Link/index.js index f6a62b84..49278e3a 100644 --- a/src/components/Link/index.js +++ b/src/components/Link/index.js @@ -12,10 +12,12 @@ const A = styled.a` font-size: ${FONT_SIZE.SMALL}; ${props => props.isGreen && css` - border-bottom: 1px solid ${colors.GREEN_PRIMARY}; + text-decoration: underline; + text-decoration-color: ${colors.GREEN_PRIMARY}; `} ${props => props.isGray && css` - border-bottom: 1px solid ${colors.TEXT_SECONDARY}; + text-decoration: underline; + text-decoration-color: ${colors.TEXT_SECONDARY}; `} &, diff --git a/src/components/Notification/components/NotificationButton/index.js b/src/components/Notification/components/NotificationButton/index.js index 9d001380..f2e9fcf6 100644 --- a/src/components/Notification/components/NotificationButton/index.js +++ b/src/components/Notification/components/NotificationButton/index.js @@ -8,7 +8,9 @@ import colors from 'config/colors'; import { WHITE_COLOR } from 'config/animations'; import { getPrimaryColor } from 'utils/notification'; import Loader from 'components/Loader'; -import { TRANSITION, FONT_SIZE, FONT_WEIGHT } from 'config/variables'; +import { + TRANSITION, FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE, +} from 'config/variables'; type Props = { type: string; @@ -48,6 +50,10 @@ const Wrapper = styled.button` border: 1px solid ${props => getPrimaryColor(props.type)}; transition: ${TRANSITION.HOVER}; + @media screen and (max-width: ${SCREEN_SIZE.SM}){ + padding: 12px 24px; + } + &:hover { color: ${colors.WHITE}; background: ${props => getPrimaryColor(props.type)}; diff --git a/src/components/modals/confirm/Address/index.js b/src/components/modals/confirm/Address/index.js index 93c49404..568784cf 100644 --- a/src/components/modals/confirm/Address/index.js +++ b/src/components/modals/confirm/Address/index.js @@ -13,7 +13,7 @@ import P from 'components/Paragraph'; import type { Props } from '../../Container'; const Wrapper = styled.div` - width: 390px; + max-width: 390px; `; const Header = styled.div` diff --git a/src/components/modals/confirm/SignTx/index.js b/src/components/modals/confirm/SignTx/index.js index ad9ccd86..a5bbdf2b 100644 --- a/src/components/modals/confirm/SignTx/index.js +++ b/src/components/modals/confirm/SignTx/index.js @@ -20,7 +20,7 @@ type Props = { } const Wrapper = styled.div` - width: 390px; + max-width: 390px; `; const Header = styled.div` diff --git a/src/components/modals/confirm/UnverifiedAddress/index.js b/src/components/modals/confirm/UnverifiedAddress/index.js index 871068c0..e6f26592 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.js @@ -30,7 +30,7 @@ const StyledLink = styled(Link)` `; const Wrapper = styled.div` - width: 370px; + max-width: 370px; padding: 30px 48px; `; diff --git a/src/components/modals/device/WalletType/index.js b/src/components/modals/device/WalletType/index.js index ca91bc99..349c7e1a 100644 --- a/src/components/modals/device/WalletType/index.js +++ b/src/components/modals/device/WalletType/index.js @@ -25,7 +25,6 @@ type Props = { } const Wrapper = styled.div` - width: 360px; `; const Header = styled.div` diff --git a/src/components/modals/passphrase/Type/index.js b/src/components/modals/passphrase/Type/index.js index fa44d5b1..422bb065 100644 --- a/src/components/modals/passphrase/Type/index.js +++ b/src/components/modals/passphrase/Type/index.js @@ -18,7 +18,7 @@ type Props = { } const Wrapper = styled.div` - width: 360px; + max-width: 360px; padding: 30px 48px; `; diff --git a/src/components/modals/pin/Pin/components/Button/index.js b/src/components/modals/pin/Pin/components/Button/index.js index 65b8c0a4..9f73ff6d 100644 --- a/src/components/modals/pin/Pin/components/Button/index.js +++ b/src/components/modals/pin/Pin/components/Button/index.js @@ -4,7 +4,7 @@ import * as React from 'react'; import styled from 'styled-components'; import PropTypes from 'prop-types'; import colors from 'config/colors'; -import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; +import { FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE } from 'config/variables'; type Props = { onClick: () => void; @@ -24,6 +24,11 @@ const Wrapper = styled.button` transition: all 0.3s; cursor: pointer; + @media screen and (max-width: ${SCREEN_SIZE.XS}) { + width: 50px; + height: 50px; + } + &:first-child { margin-left: 0px; } diff --git a/src/config/animations.js b/src/config/animations.js index c7fee813..29144b1c 100644 --- a/src/config/animations.js +++ b/src/config/animations.js @@ -80,4 +80,22 @@ export const SLIDE_DOWN = keyframes` 100% { transform: translateY(0%); } +`; + +export const SLIDE_RIGHT = keyframes` + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(0%); + } +`; + +export const SLIDE_LEFT = keyframes` + 0% { + transform: translateX(0%); + } + 100% { + transform: translateX(-100%); + } `; \ No newline at end of file diff --git a/src/config/variables.js b/src/config/variables.js index 9ec9aa0a..48a7f1a3 100644 --- a/src/config/variables.js +++ b/src/config/variables.js @@ -1,3 +1,15 @@ +// Bootstrap 3 breakpoints +/* XS - Extra Small Devices, Phones */ +/* SM - Small Devices, Tablets */ +/* MD - Medium Devices, Desktops */ +/* LG - Large Devices, Wide Screens */ +export const SCREEN_SIZE = { + XS: '480px', + SM: '768px', + MD: '992px', + LG: '1170px', +}; + // OLD UNITS // SMALLEST: '10px', // SMALLER: '12px', @@ -13,7 +25,6 @@ // H3: '14px', // H4: '12px', // COUNTER: '11px', - export const FONT_SIZE = { SMALL: '0.8571rem', BASE: '1rem', 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/support/styles/index.js b/src/support/styles/index.js index c3a84194..f8568221 100644 --- a/src/support/styles/index.js +++ b/src/support/styles/index.js @@ -8,7 +8,7 @@ import animationStyles from './Animations'; const baseStyles = createGlobalStyle` html, body { width: 100%; - height: 100%; + min-height: 100vh; position: relative; font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif; font-weight: ${FONT_WEIGHT.NORMAL}; @@ -43,7 +43,7 @@ const baseStyles = createGlobalStyle` } #trezor-wallet-root { - height: 100%; + min-height: 100vh; } ${animationStyles}; diff --git a/src/views/Landing/components/BrowserNotSupported/index.js b/src/views/Landing/components/BrowserNotSupported/index.js index 7e0b2d86..5ba7904a 100644 --- a/src/views/Landing/components/BrowserNotSupported/index.js +++ b/src/views/Landing/components/BrowserNotSupported/index.js @@ -8,7 +8,9 @@ import { H2 } from 'components/Heading'; import ChromeImage from 'images/browser-chrome.png'; import FirefoxImage from 'images/browser-firefox.png'; -const Wrapper = styled.div``; +const Wrapper = styled.div` + padding: 24px 0px; +`; const ChooseBrowserWrapper = styled.div` display: flex; diff --git a/src/views/Landing/components/ConnectDevice/index.js b/src/views/Landing/components/ConnectDevice/index.js index 450c9f3f..0e417cff 100644 --- a/src/views/Landing/components/ConnectDevice/index.js +++ b/src/views/Landing/components/ConnectDevice/index.js @@ -8,7 +8,7 @@ import Button from 'components/Button'; import { H2 } from 'components/Heading'; import { PULSATE } from 'config/animations'; import colors from 'config/colors'; -import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; +import { FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE } from 'config/variables'; import CaseImage from 'images/macbook.png'; import Link from 'components/Link'; @@ -18,31 +18,50 @@ type Props = { showDisconnect: boolean, }; +const StyledConnectDevice = styled.div` + padding: 0px 48px; +`; + const Title = styled.div` margin-top: 60px; `; const Wrapper = styled.div` display: flex; - justify-content: space-around; align-items: center; - width: 400px; margin: 0 auto; padding: 36px 0; + justify-content: center; + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + align-content: center; + flex-direction: column; + } `; const ConnectTrezorWrapper = styled.div` position: relative; top: 1px; + margin: 15px 15px 0px 15px; animation: ${PULSATE} 1.3s ease-out infinite; color: ${colors.GREEN_PRIMARY}; font-size: ${FONT_SIZE.BIG}; font-weight: ${FONT_WEIGHT.MEDIUM}; `; +const StyledP = styled(P)` + line-height: auto; + margin: 15px 15px 0px 15px; +`; + +const StyledButton = styled(Button)` + margin: 15px 15px 5px 15px; +`; + const Image = styled.img` - width: 777px; - min-height: 500px; + width: 100%; + max-width: 777px; + height: auto; margin: auto; background-repeat: no-repeat; background-position: center 0px; @@ -105,7 +124,7 @@ class ConnectDevice extends PureComponent { render() { return ( -
+ <H2 claim>The private bank in your hands.</H2> <P>Trezor Wallet is an easy-to-use interface for your Trezor.</P> @@ -124,10 +143,10 @@ class ConnectDevice extends PureComponent<Props> { </ConnectTrezorWrapper> {this.props.showWebUsb && !this.props.showDisconnect && ( <React.Fragment> - <P>and</P> - <Button isWebUsb> + <StyledP>and</StyledP> + <StyledButton isWebUsb> Check for devices - </Button> + </StyledButton> </React.Fragment> )} </Wrapper> @@ -156,7 +175,7 @@ class ConnectDevice extends PureComponent<Props> { </StyledLink> </P> </Footer> - </div> + </StyledConnectDevice> ); } } diff --git a/src/views/Landing/components/InitializationError/index.js b/src/views/Landing/components/InitializationError/index.js index b2e64961..188e6a3b 100644 --- a/src/views/Landing/components/InitializationError/index.js +++ b/src/views/Landing/components/InitializationError/index.js @@ -5,7 +5,6 @@ import styled from 'styled-components'; import Notification from 'components/Notification'; const Wrapper = styled.div` - min-width: 720px; width: 100%; `; diff --git a/src/views/Landing/components/LandingWrapper/index.js b/src/views/Landing/components/LandingWrapper/index.js index 7b6dccd5..1e38ce28 100644 --- a/src/views/Landing/components/LandingWrapper/index.js +++ b/src/views/Landing/components/LandingWrapper/index.js @@ -18,8 +18,7 @@ type Props = { } const Wrapper = styled.div` - min-height: 100%; - min-width: 720px; + min-height: 100vh; display: flex; flex-direction: column; diff --git a/src/views/Landing/views/Import/index.js b/src/views/Landing/views/Import/index.js index 43b35b52..e4b0fe11 100644 --- a/src/views/Landing/views/Import/index.js +++ b/src/views/Landing/views/Import/index.js @@ -13,7 +13,6 @@ import Button from 'components/Button'; import LandingWrapper from 'views/Landing/components/LandingWrapper'; const Wrapper = styled.div` - min-width: 720px; width: 100%; display: flex; flex-direction: column; diff --git a/src/views/Landing/views/InstallBridge/index.js b/src/views/Landing/views/InstallBridge/index.js index d2f7f3c0..3eb65953 100644 --- a/src/views/Landing/views/InstallBridge/index.js +++ b/src/views/Landing/views/InstallBridge/index.js @@ -6,7 +6,7 @@ import colors from 'config/colors'; import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; import { Select } from 'components/Select'; import Link from 'components/Link'; -import { H1 } from 'components/Heading'; +import { H1, H2 } from 'components/Heading'; import Button from 'components/Button'; import P from 'components/Paragraph'; import Icon from 'components/Icon'; @@ -42,6 +42,7 @@ const Wrapper = styled.div` justify-content: center; align-items: center; max-width: 500px; + padding: 0 24px; `; const Top = styled.div` @@ -62,6 +63,7 @@ const TitleHeader = styled(H1)` font-size: ${FONT_SIZE.HUGE}; justify-content: center; align-items: center; + flex-wrap: wrap; `; const Version = styled.span` @@ -81,12 +83,15 @@ const LearnMoreText = styled.span` const SelectWrapper = styled(Select)` margin-right: 10px; width: 180px; + margin-bottom: 5px; `; const Download = styled.div` margin: 24px auto; display: flex; align-items: center; + flex-wrap: wrap; + justify-content: center; `; const DownloadBridgeButton = styled(Button)` @@ -94,6 +99,7 @@ const DownloadBridgeButton = styled(Button)` padding-bottom: 5px; display: flex; align-items: center; + margin-bottom: 5px; `; const GoBack = styled.span` @@ -110,7 +116,7 @@ const Ol = styled.ul` margin: 0 auto; color: ${colors.TEXT_SECONDARY}; font-size: ${FONT_SIZE.BIG}; - padding: 10px 0 15px 25px; + padding: 0px 0 15px 25px; text-align: left; `; @@ -175,6 +181,7 @@ class InstallBridge extends PureComponent<Props, State> { </DownloadBridgeButton> </Link> </Download> + <H2>Changelog</H2> <Ol> {this.props.transport.bridge.changelog.map(entry => ( <Li key={entry}>{entry}</Li> diff --git a/src/views/Wallet/components/Content/index.js b/src/views/Wallet/components/Content/index.js index 150b84ff..3cfbd133 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` @@ -41,6 +45,7 @@ const Title = styled(H1)` font-weight: ${FONT_WEIGHT.NORMAL}; color: ${props => (props.type === 'progress' ? colors.TEXT_SECONDARY : '')}; margin-left: 10px; + text-align: center; `; const Message = styled(P)` diff --git a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js index 4797b422..0929ff93 100644 --- a/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js +++ b/src/views/Wallet/components/LeftNavigation/components/DeviceMenu/index.js @@ -19,6 +19,7 @@ import Divider from '../Divider'; const Wrapper = styled.div` position: absolute; + z-index: 1; width: 100%; padding-bottom: 8px; border-bottom: 1px solid #E3E3E3; diff --git a/src/views/Wallet/components/LeftNavigation/components/Sidebar/index.js b/src/views/Wallet/components/LeftNavigation/components/Sidebar/index.js new file mode 100644 index 00000000..a149cf8f --- /dev/null +++ b/src/views/Wallet/components/LeftNavigation/components/Sidebar/index.js @@ -0,0 +1,60 @@ +/* @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}; + + overflow-x: hidden; + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + position: absolute; + height: calc(100vh - 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 SidebarWrapper = styled.div` + height: 100%; + display: flex; + flex-direction: column; +`; + +export default class Sidebar extends React.PureComponent<Props, State> { + render() { + return ( + <AbsoluteWrapper isOpen={this.props.isOpen}> + <SidebarWrapper> + {this.props.children} + </SidebarWrapper> + </AbsoluteWrapper> + ); + } +} \ No newline at end of file diff --git a/src/views/Wallet/components/LeftNavigation/components/StickyContainer/index.js b/src/views/Wallet/components/LeftNavigation/components/StickyContainer/index.js deleted file mode 100644 index 2362ac81..00000000 --- a/src/views/Wallet/components/LeftNavigation/components/StickyContainer/index.js +++ /dev/null @@ -1,189 +0,0 @@ -/* @flow */ - -import * as React from 'react'; -import raf from 'raf'; -import { getViewportHeight, getScrollX, getScrollY } from 'utils/windowUtils'; -import styled from 'styled-components'; -import colors from 'config/colors'; - -type Props = { - children?: React.Node, -} - -type State = { - prevScrollY: number; - asideMinHeight: number, - wrapperTopOffset: number, - wrapperLeftOffset: number, - wrapperBottomPadding: number, - footerFixed: boolean, -} - -const AsideWrapper = styled.aside.attrs(props => ({ - style: { minHeight: props.minHeight }, -}))` - - position: relative; - top: 0px; - width: 320px; - min-width: 320px; - overflow: hidden; - background: ${colors.MAIN}; - border-right: 1px solid ${colors.DIVIDER}; - border-top-left-radius: 4px; - - @media screen and (max-width: 1170px) { - border-top-left-radius: 0px; - } -`; - -const StickyContainerWrapper = styled.div.attrs(props => ({ - style: { - top: props.top, - left: props.left, - paddingBottom: props.paddingBottom, - }, -}))` - position: fixed; - border-right: 1px solid ${colors.DIVIDER}; - width: 320px; - overflow: hidden; -`; - -export default class StickyContainer extends React.PureComponent<Props, State> { - constructor() { - super(); - this.state = { - prevScrollY: 0, - asideMinHeight: 0, - wrapperTopOffset: 0, - wrapperLeftOffset: 0, - wrapperBottomPadding: 0, - footerFixed: false, - }; - } - - componentDidMount() { - window.addEventListener('scroll', this.handleScroll); - window.addEventListener('resize', this.handleScroll); - this.update(); - } - - componentDidUpdate(prevProps: Props, newState: State) { - // recalculate view only if props was changed - // ignore when state is changed - if (this.state === newState) raf(this.update); - } - - componentWillUnmount() { - window.removeEventListener('scroll', this.handleScroll); - window.removeEventListener('resize', this.handleScroll); - } - - update = () => { - this.recalculatePosition(); - } - - handleScroll = () => raf(this.update); - - asideRefCallback = (element: ?HTMLElement) => { - this.aside = element; - } - - wrapperRefCallback = (element: ?HTMLElement) => { - this.wrapper = element; - } - - footerRefCallback = (element: ?HTMLElement) => { - this.footer = element; - } - - aside: ?HTMLElement; - - wrapper: ?HTMLElement; - - footer: ?HTMLElement; - - recalculatePosition() { - const { aside, wrapper, footer } = this; - if (!aside || !wrapper || !footer) return; - - const viewportHeight = getViewportHeight(); - const asideBounds = aside.getBoundingClientRect(); - const wrapperBounds = wrapper.getBoundingClientRect(); - const footerBounds = footer.getBoundingClientRect(); - const isHeaderFixed = asideBounds.top < 0; - const isWrapperBiggerThanViewport = wrapperBounds.height > viewportHeight; - const state = { ...this.state }; - - const scrollX = getScrollX(); - const scrollY = getScrollY(); - - if (isHeaderFixed) { - if (isWrapperBiggerThanViewport) { - const scrollDirection = scrollY >= state.prevScrollY ? 'down' : 'up'; - const topOutOfBounds: boolean = (wrapperBounds.top > 0 && scrollDirection === 'up'); - const bottomOutOfBounds: boolean = (footerBounds.bottom <= viewportHeight && scrollDirection === 'down'); - if (!topOutOfBounds && !bottomOutOfBounds) { - // neither "top" or "bottom" was reached - // scroll whole wrapper - const distanceScrolled = Math.abs(scrollY - state.prevScrollY); - state.wrapperTopOffset += scrollDirection === 'down' ? -distanceScrolled : distanceScrolled; - } - } - // make sure that wrapper will not be over scrolled - if (state.wrapperTopOffset > 0) state.wrapperTopOffset = 0; - const maxScrollTop = viewportHeight - wrapperBounds.height; - if (maxScrollTop < 0 && state.wrapperTopOffset < maxScrollTop) state.wrapperTopOffset = maxScrollTop; - } else { - // update wrapper "top" to be same as "aside" element - state.wrapperTopOffset = asideBounds.top; - } - - if (isWrapperBiggerThanViewport) { - state.footerFixed = false; - } else if (state.footerFixed) { - if (footerBounds.top < wrapperBounds.bottom - footerBounds.height) { - state.footerFixed = false; - } - } else if (footerBounds.bottom < viewportHeight) { - state.footerFixed = asideBounds.height > wrapperBounds.height; - } - - state.prevScrollY = scrollY; - state.asideMinHeight = wrapperBounds.height; - state.wrapperBottomPadding = state.footerFixed ? footerBounds.height : 0; - // update wrapper "left" position - state.wrapperLeftOffset = scrollX > 0 ? -scrollX : asideBounds.left; - - this.setState(state); - } - - render() { - return ( - <AsideWrapper - footerFixed={this.state.footerFixed} - minHeight={this.state.asideMinHeight} - ref={this.asideRefCallback} - onScroll={this.handleScroll} - onTouchStart={this.handleScroll} - onTouchMove={this.handleScroll} - onTouchEnd={this.handleScroll} - > - <StickyContainerWrapper - paddingBottom={this.state.wrapperBottomPadding} - top={this.state.wrapperTopOffset} - left={this.state.wrapperLeftOffset} - ref={this.wrapperRefCallback} - > - {React.Children.map(this.props.children, (child) => { // eslint-disable-line arrow-body-style - return child.key === 'sticky-footer' ? React.cloneElement(child, { - ref: this.footerRefCallback, - position: this.state.footerFixed ? 'fixed' : 'relative', - }) : child; - })} - </StickyContainerWrapper> - </AsideWrapper> - ); - } -} \ 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..8e926f67 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 Sidebar from './components/Sidebar'; 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')}; `; @@ -202,10 +204,7 @@ class LeftNavigation extends React.PureComponent<Props, State> { const { selectedDevice, dropdownOpened } = props.wallet; const isDeviceAccessible = deviceUtils.isDeviceAccessible(selectedDevice); return ( - <StickyContainer - location={props.router.location.pathname} - deviceSelection={this.props.wallet.dropdownOpened} - > + <Sidebar isOpen={props.wallet.showSidebar}> <Header isSelected isHoverable={false} @@ -249,7 +248,7 @@ class LeftNavigation extends React.PureComponent<Props, State> { </A> </Help> </Footer> - </StickyContainer> + </Sidebar> ); } } diff --git a/src/views/Wallet/components/TopNavigationAccount/index.js b/src/views/Wallet/components/TopNavigationAccount/index.js index 5f9ae7cc..94977803 100644 --- a/src/views/Wallet/components/TopNavigationAccount/index.js +++ b/src/views/Wallet/components/TopNavigationAccount/index.js @@ -2,7 +2,7 @@ import styled from 'styled-components'; import React from 'react'; -import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; +import { FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE } from 'config/variables'; import { NavLink } from 'react-router-dom'; import { connect } from 'react-redux'; import colors from 'config/colors'; @@ -24,6 +24,14 @@ const Wrapper = styled.div` padding: 0px 30px 0 35px; overflow-y: hidden; overflow-x: auto; + + @media screen and (max-width: ${SCREEN_SIZE.MD}) { + justify-content: space-between; + } + + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + padding: 0px 16px; + } `; const StyledNavLink = styled(NavLink)` @@ -34,6 +42,15 @@ const StyledNavLink = styled(NavLink)` padding: 20px 35px; white-space: nowrap; + @media screen and (max-width: ${SCREEN_SIZE.MD}) { + padding: 20px 10px; + } + + @media screen and (max-width: ${SCREEN_SIZE.XS}) { + font-size: ${FONT_SIZE.BASE}; + padding: 20px 10px; + } + &.active, &:hover { transition: all 0.3s ease-in-out; diff --git a/src/views/Wallet/index.js b/src/views/Wallet/index.js index f6063eb9..2e6c6e3b 100644 --- a/src/views/Wallet/index.js +++ b/src/views/Wallet/index.js @@ -2,39 +2,48 @@ import * as React from 'react'; import colors from 'config/colors'; -import styled from 'styled-components'; +import styled, { css } 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; + min-height: 100vh; display: flex; flex-direction: column; background: ${colors.BACKGROUND}; @@ -67,7 +76,15 @@ const MainContent = styled.article` flex-direction: column; overflow: auto; border-top-right-radius: 4px; - + + @media screen and (max-width: ${SCREEN_SIZE.SM}){ + ${props => props.preventBgScroll && css` + position: fixed; + width: 100%; + min-height: calc(100vh - 52px); + `} + } + @media screen and (max-width: 1170px) { border-top-right-radius: 0px; } @@ -87,13 +104,22 @@ 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 sidebarEnabled={!!props.wallet.selectedDevice} sidebarOpened={props.wallet.showSidebar} toggleSidebar={props.toggleSidebar} /> <AppNotifications /> <WalletWrapper> + <StyledBackdrop show={props.wallet.showSidebar} onClick={props.toggleSidebar} animated /> {props.wallet.selectedDevice && <LeftNavigation />} - <MainContent> + <MainContent preventBgScroll={props.wallet.showSidebar}> <Navigation> <Route path="/device/:device/network/:network/account/:account" component={TopNavigationAccount} /> <Route path="/device/:device/device-settings" component={TopNavigationDeviceSettings} /> @@ -110,10 +136,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), ); diff --git a/src/views/Wallet/views/Account/SignVerify/index.js b/src/views/Wallet/views/Account/SignVerify/index.js index 11e9b0fe..9b0c2516 100644 --- a/src/views/Wallet/views/Account/SignVerify/index.js +++ b/src/views/Wallet/views/Account/SignVerify/index.js @@ -7,6 +7,8 @@ import Title from 'views/Wallet/components/Title'; import Button from 'components/Button'; import Content from 'views/Wallet/components/Content'; import colors from 'config/colors'; +import { SCREEN_SIZE } from 'config/variables'; + import type { Props } from './Container'; @@ -14,6 +16,7 @@ const Wrapper = styled.div` display: flex; flex: 1; flex-direction: row; + flex-wrap: wrap; background: ${colors.WHITE}; `; @@ -52,14 +55,22 @@ const StyledButton = styled(Button)` const Column = styled.div` display: flex; - flex: 1; + flex: 1 1 50%; flex-direction: column; + + @media screen and (max-width: ${SCREEN_SIZE.XS}) { + flex: 1 1 100%; + } `; const Sign = styled(Column)``; const Verify = styled(Column)` padding-left: 20px; + + @media screen and (max-width: ${SCREEN_SIZE.XS}) { + padding-left: 0px; + } `; class SignVerify extends Component <Props> { diff --git a/src/views/Wallet/views/Account/Summary/components/AddTokenMessage/index.js b/src/views/Wallet/views/Account/Summary/components/AddTokenMessage/index.js index 1167de60..3c0e06c4 100644 --- a/src/views/Wallet/views/Account/Summary/components/AddTokenMessage/index.js +++ b/src/views/Wallet/views/Account/Summary/components/AddTokenMessage/index.js @@ -16,6 +16,10 @@ const StyledSVG = styled.svg` margin-bottom: 24px; `; +const StyledP = styled(P)` + text-align: center; +`; + const AddTokenMessage = () => ( <Wrapper> <StyledSVG width="84px" height="76px" viewBox="0 0 84 76"> @@ -51,7 +55,7 @@ const AddTokenMessage = () => ( </g> </StyledSVG> <H2>Add your tokens</H2> - <P isSmaller>Search for the token or add them manually by pasting token address into search input.</P> + <StyledP isSmaller>Search for the token or add them manually by pasting token address into search input.</StyledP> </Wrapper> ); export default AddTokenMessage; \ No newline at end of file diff --git a/src/views/Wallet/views/Account/Summary/components/Balance/index.js b/src/views/Wallet/views/Account/Summary/components/Balance/index.js index 960007b2..b0974e6d 100644 --- a/src/views/Wallet/views/Account/Summary/components/Balance/index.js +++ b/src/views/Wallet/views/Account/Summary/components/Balance/index.js @@ -21,7 +21,7 @@ type State = { }; const Wrapper = styled.div` - padding-bottom: 28px; + padding-bottom: ${props => (props.isHidden ? '0px' : '28px')}; position: relative; display: flex; @@ -116,7 +116,7 @@ class AccountBalance extends PureComponent<Props, State> { } return ( - <Wrapper> + <Wrapper isHidden={this.state.isHidden}> <HideBalanceIconWrapper onClick={() => this.handleHideBalanceIconClick()} > diff --git a/src/views/Wallet/views/Account/Summary/ethereum/index.js b/src/views/Wallet/views/Account/Summary/ethereum/index.js index e7cc7cc2..63f00a5d 100644 --- a/src/views/Wallet/views/Account/Summary/ethereum/index.js +++ b/src/views/Wallet/views/Account/Summary/ethereum/index.js @@ -50,10 +50,6 @@ const AccountTitle = styled.div` color: ${colors.WALLET_TITLE}; `; -const StyledCoinLogo = styled(CoinLogo)` - margin-right: 10px; -`; - const StyledIcon = styled(Icon)` position: relative; top: -7px; @@ -93,7 +89,7 @@ const AccountSummary = (props: Props) => { <React.Fragment> <AccountHeading> <AccountName> - <StyledCoinLogo network={account.network} /> + <CoinLogo network={account.network} /> <AccountTitle>Account #{parseInt(account.index, 10) + 1}</AccountTitle> </AccountName> <Link href={explorerLink} isGray>See full transaction history</Link> @@ -123,7 +119,7 @@ const AccountSummary = (props: Props) => { defaultOptions value={null} isMulti={false} - placeholder="Type in a token name or paste a token address directly" + placeholder="Type in a token name or a token address" loadingMessage={() => 'Loading...'} noOptionsMessage={() => 'Token not found'} onChange={(token) => { diff --git a/src/views/Wallet/views/Account/Summary/ripple/components/Balance/index.js b/src/views/Wallet/views/Account/Summary/ripple/components/Balance/index.js index f31a9c5e..e337cb63 100644 --- a/src/views/Wallet/views/Account/Summary/ripple/components/Balance/index.js +++ b/src/views/Wallet/views/Account/Summary/ripple/components/Balance/index.js @@ -22,7 +22,7 @@ type State = { }; const Wrapper = styled.div` - padding-bottom: 28px; + padding-bottom: ${props => (props.isHidden ? '0px' : '28px')}; position: relative; display: flex; @@ -117,7 +117,7 @@ class AccountBalance extends PureComponent<Props, State> { } return ( - <Wrapper> + <Wrapper isHidden={this.state.isHidden}> <HideBalanceIconWrapper onClick={() => this.handleHideBalanceIconClick()} > diff --git a/src/views/Wallet/views/Account/Summary/ripple/index.js b/src/views/Wallet/views/Account/Summary/ripple/index.js index 01242056..8c1e6a6a 100644 --- a/src/views/Wallet/views/Account/Summary/ripple/index.js +++ b/src/views/Wallet/views/Account/Summary/ripple/index.js @@ -47,10 +47,6 @@ const AccountTitle = styled.div` color: ${colors.WALLET_TITLE}; `; -const StyledCoinLogo = styled(CoinLogo)` - margin-right: 10px; -`; - const StyledIcon = styled(Icon)` position: relative; top: -7px; @@ -86,7 +82,7 @@ const AccountSummary = (props: Props) => { <React.Fragment> <AccountHeading> <AccountName> - <StyledCoinLogo network={account.network} /> + <CoinLogo network={account.network} /> <AccountTitle>Account #{parseInt(account.index, 10) + 1}</AccountTitle> </AccountName> { !account.empty && <Link href={explorerLink} isGray>See full transaction history</Link> } diff --git a/src/views/Wallet/views/Bootloader/index.js b/src/views/Wallet/views/Bootloader/index.js index d82a7a2a..2b10dfd9 100644 --- a/src/views/Wallet/views/Bootloader/index.js +++ b/src/views/Wallet/views/Bootloader/index.js @@ -22,10 +22,14 @@ const StyledP = styled(P)` text-align: center; `; +const StyledH1 = styled(H1)` + text-align: center; +`; + const Bootloader = () => ( <Wrapper> <Row> - <H1>Your device is in firmware update mode</H1> + <StyledH1>Your device is in firmware update mode</StyledH1> <StyledP>Please re-connect it</StyledP> </Row> </Wrapper> diff --git a/src/views/Wallet/views/DeviceSettings/index.js b/src/views/Wallet/views/DeviceSettings/index.js index 4dbc1546..019a6fbe 100644 --- a/src/views/Wallet/views/DeviceSettings/index.js +++ b/src/views/Wallet/views/DeviceSettings/index.js @@ -28,6 +28,10 @@ const StyledP = styled(P)` text-align: center; `; +const StyledH1 = styled(H1)` + text-align: center; +`; + const DeviceSettings = () => ( <Content> <Section> @@ -37,7 +41,7 @@ const DeviceSettings = () => ( color={colors.WARNING_PRIMARY} icon={ICONS.WARNING} /> - <H1>Device settings is under construction</H1> + <StyledH1>Device settings is under construction</StyledH1> <StyledP>Please use Bitcoin wallet interface to change your device settings</StyledP> <Link href="https://beta-wallet.trezor.io/"> <Button>Take me to the Bitcoin wallet</Button> diff --git a/src/views/Wallet/views/WalletSettings/index.js b/src/views/Wallet/views/WalletSettings/index.js index 5f2b3c66..bfdcfe62 100644 --- a/src/views/Wallet/views/WalletSettings/index.js +++ b/src/views/Wallet/views/WalletSettings/index.js @@ -23,6 +23,10 @@ const Row = styled.div` padding: 50px 0; `; +const StyledH1 = styled(H1)` + text-align: center; +`; + const WalletSettings = () => ( <Content> <Section> @@ -32,7 +36,7 @@ const WalletSettings = () => ( color={colors.WARNING_PRIMARY} icon={icons.WARNING} /> - <H1>Wallet settings is under construction</H1> + <StyledH1>Wallet settings is under construction</StyledH1> <Link to="/"> <Button>Take me back</Button> </Link>