(Fix) Basic mobile responsiveness (#532)

* (fix) header mobile version

* (fix) send / receive responsive

* (fix) footer overflowing

* (fix) responsive settings menu

* (fix) scrolling menu tabs

* (fix) responsive tables

* (fix) a few modal windows' responsive issues

* (remove) unused files

* (fix) sidebar responsive

* (fix) load safe responsive

* (fix) create safe responsiveness

* (fix) text wrap

* (fix) remove modal responsiveness

* (fix) name wrap

* (update) yarn.lock
This commit is contained in:
Gabriel Rodríguez Alsina 2020-02-06 12:40:29 -03:00 committed by GitHub
parent 16b3dc0f2a
commit 165a088b1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 972 additions and 1369 deletions

View File

@ -8,6 +8,6 @@
<title>Gnosis Safe Multisig</title>
</head>
<body>
<div id="root"></div>
<div id="root" style="overflow: hidden;"></div>
</body>
</html>

View File

@ -51,7 +51,6 @@ const useStyles = makeStyles({
gridTemplateColumns: '1fr',
paddingBottom: '30px',
rowGap: '10px',
[`@media (min-width: ${screenSm}px)`]: {
gridTemplateColumns: '1fr 1fr 1fr',
paddingBottom: '0',

View File

@ -10,8 +10,10 @@ import GnoButtonLink from '~/components/layout/ButtonLink'
const useStyles = makeStyles({
footer: {
boxSizing: 'border-box',
display: 'flex',
flexDirection: 'row',
flexShrink: '1',
flexWrap: 'wrap',
justifyContent: 'center',
margin: '0 auto',
@ -54,11 +56,10 @@ const Footer = () => {
return (
<footer className={classes.footer}>
<span className={classes.item}>
©
{' '}
©
{date.getFullYear()}
{' '}
Gnosis
Gnosis
</span>
<span className={classes.sep}>|</span>
<Link className={cn(classes.item, classes.link)} to="https://safe.gnosis.io/terms" target="_blank">
@ -85,7 +86,11 @@ const Footer = () => {
Preferences
</GnoButtonLink>
<span className={classes.sep}>|</span>
<Link className={cn(classes.item, classes.link)} to="https://github.com/gnosis/safe-react/releases" target="_blank">
<Link
className={cn(classes.item, classes.link)}
to="https://github.com/gnosis/safe-react/releases"
target="_blank"
>
{appVersion}
</Link>
</footer>

View File

@ -4,14 +4,19 @@ import { withStyles } from '@material-ui/core/styles'
import Dot from '@material-ui/icons/FiberManualRecord'
import Block from '~/components/layout/Block'
import Img from '~/components/layout/Img'
import { fancy, border, warning } from '~/theme/variables'
import {
fancy, border, warning, screenSm,
} from '~/theme/variables'
const key = require('../assets/key.svg')
const triangle = require('../assets/triangle.svg')
const styles = () => ({
root: {
display: 'flex',
display: 'none',
[`@media (min-width: ${screenSm}px)`]: {
display: 'flex',
},
},
dot: {
position: 'relative',

View File

@ -13,7 +13,7 @@ import Img from '~/components/layout/Img'
import Row from '~/components/layout/Row'
import Spacer from '~/components/Spacer'
import {
border, sm, md, headerHeight,
border, sm, md, headerHeight, screenSm,
} from '~/theme/variables'
import Provider from './Provider'
import NetworkLabel from './NetworkLabel'
@ -30,26 +30,34 @@ type Props = Open & {
const styles = () => ({
root: {
backgroundColor: 'white',
padding: 0,
boxShadow: '0 0 10px 0 rgba(33, 48, 77, 0.1)',
minWidth: '280px',
borderRadius: sm,
boxShadow: '0 0 10px 0 rgba(33, 48, 77, 0.1)',
marginTop: '11px',
minWidth: '280px',
padding: 0,
},
summary: {
borderBottom: `solid 2px ${border}`,
alignItems: 'center',
height: headerHeight,
boxShadow: '0 2px 4px 0 rgba(212, 212, 211, 0.59)',
backgroundColor: 'white',
zIndex: 1301,
borderBottom: `solid 2px ${border}`,
boxShadow: '0 2px 4px 0 rgba(212, 212, 211, 0.59)',
flexWrap: 'nowrap',
height: headerHeight,
position: 'fixed',
width: '100%',
zIndex: 1301,
},
logo: {
padding: `${sm} ${md}`,
flexBasis: '95px',
flexGrow: 0,
flexShrink: '0',
flexGrow: '0',
maxWidth: '55px',
padding: sm,
[`@media (min-width: ${screenSm}px)`]: {
maxWidth: 'none',
paddingLeft: md,
paddingRight: md,
},
},
popper: {
zIndex: 2000,
@ -65,46 +73,46 @@ const Layout = openHoc(
providerInfo,
providerDetails,
}: Props) => (
<Row className={classes.summary}>
<Col start="xs" middle="xs" className={classes.logo}>
<Link to="/">
<Img src={logo} height={32} alt="Gnosis Team Safe" />
</Link>
</Col>
<Divider />
<SafeListHeader />
<Divider />
<NetworkLabel />
<Spacer />
<Provider open={open} toggle={toggle} info={providerInfo}>
{(providerRef) => (
<Popper
open={open}
anchorEl={providerRef.current}
placement="bottom"
className={classes.popper}
popperOptions={{ positionFixed: true }}
>
{({ TransitionProps }) => (
<Grow {...TransitionProps}>
<>
<ClickAwayListener
onClickAway={clickAway}
mouseEvent="onClick"
touchEvent={false}
>
<List className={classes.root} component="div">
{providerDetails}
</List>
</ClickAwayListener>
</>
</Grow>
)}
</Popper>
)}
</Provider>
</Row>
),
<Row className={classes.summary}>
<Col start="xs" middle="xs" className={classes.logo}>
<Link to="/">
<Img src={logo} height={32} alt="Gnosis Team Safe" />
</Link>
</Col>
<Divider />
<SafeListHeader />
<Divider />
<NetworkLabel />
<Spacer />
<Provider open={open} toggle={toggle} info={providerInfo}>
{(providerRef) => (
<Popper
anchorEl={providerRef.current}
className={classes.popper}
open={open}
placement="bottom"
popperOptions={{ positionFixed: true }}
>
{({ TransitionProps }) => (
<Grow {...TransitionProps}>
<>
<ClickAwayListener
onClickAway={clickAway}
mouseEvent="onClick"
touchEvent={false}
>
<List className={classes.root} component="div">
{providerDetails}
</List>
</ClickAwayListener>
</>
</Grow>
)}
</Popper>
)}
</Provider>
</Row>
),
)
export default withStyles(styles)(Layout)

View File

@ -5,7 +5,7 @@ import { getNetwork } from '~/config'
import Paragraph from '~/components/layout/Paragraph'
import Col from '~/components/layout/Col'
import {
xs, sm, md, border,
xs, sm, md, border, screenSm,
} from '~/theme/variables'
const network = getNetwork()
@ -14,14 +14,22 @@ const formattedNetwork = network[0].toUpperCase() + network.substring(1).toLower
const useStyles = makeStyles({
container: {
flexGrow: 0,
padding: `0 ${md}`,
padding: `0 ${sm}`,
[`@media (min-width: ${screenSm}px)`]: {
paddingLeft: md,
paddingRight: md,
},
},
text: {
background: border,
padding: `${xs} ${sm}`,
borderRadius: '3px',
marginLeft: sm,
lineHeight: 'normal',
margin: '0',
padding: `${xs} ${sm}`,
[`@media (min-width: ${screenSm}px)`]: {
marginLeft: '8px',
},
},
})

View File

@ -7,7 +7,7 @@ import ExpandMore from '@material-ui/icons/ExpandMore'
import Col from '~/components/layout/Col'
import Divider from '~/components/layout/Divider'
import { type Open } from '~/components/hoc/OpenHoc'
import { sm, md } from '~/theme/variables'
import { sm, md, screenSm } from '~/theme/variables'
type Props = Open & {
classes: Object,
@ -18,22 +18,29 @@ type Props = Open & {
const styles = () => ({
root: {
height: '100%',
display: 'flex',
alignItems: 'center',
flexBasis: '284px',
marginRight: '20px',
display: 'flex',
height: '100%',
[`@media (min-width: ${screenSm}px)`]: {
flexBasis: '284px',
marginRight: '20px',
},
},
provider: {
padding: `${sm} ${md}`,
alignItems: 'center',
flex: '1 1 auto',
display: 'flex',
cursor: 'pointer',
display: 'flex',
flex: '1 1 auto',
padding: sm,
[`@media (min-width: ${screenSm}px)`]: {
paddingLeft: md,
paddingRight: md,
},
},
expand: {
width: '30px',
height: '30px',
width: '30px',
},
})

View File

@ -4,7 +4,7 @@ import { withStyles } from '@material-ui/core/styles'
import Dot from '@material-ui/icons/FiberManualRecord'
import Paragraph from '~/components/layout/Paragraph'
import Col from '~/components/layout/Col'
import { connected as connectedBg, sm } from '~/theme/variables'
import { screenSm, connected as connectedBg, sm } from '~/theme/variables'
import Identicon from '~/components/Identicon'
import { shortVersionOf } from '~/logic/wallets/ethAddresses'
import CircleDot from '~/components/Header/components/CircleDot'
@ -21,23 +21,33 @@ const styles = () => ({
network: {
fontFamily: 'Averta, sans-serif',
},
logo: {
height: '15px',
width: '15px',
top: '12px',
position: 'relative',
right: '10px',
backgroundColor: '#ffffff',
identicon: {
display: 'none',
[`@media (min-width: ${screenSm}px)`]: {
display: 'block',
},
},
dot: {
backgroundColor: '#fff',
borderRadius: '15px',
color: connectedBg,
display: 'none',
height: '15px',
position: 'relative',
right: '10px',
top: '12px',
width: '15px',
[`@media (min-width: ${screenSm}px)`]: {
display: 'block',
},
},
account: {
paddingRight: sm,
alignItems: 'start',
display: 'flex',
flexDirection: 'column',
justifyContent: 'left',
alignItems: 'start',
flexGrow: 1,
justifyContent: 'left',
paddingRight: sm,
},
address: {
letterSpacing: '-0.5px',
@ -56,8 +66,8 @@ const ProviderInfo = ({
<>
{connected && (
<>
<Identicon address={identiconAddress} diameter={30} />
<Dot className={classes.logo} />
<Identicon className={classes.identicon} address={identiconAddress} diameter={30} />
<Dot className={classes.dot} />
</>
)}
{!connected && <CircleDot keySize={14} circleSize={35} dotSize={16} dotTop={24} dotRight={11} mode="warning" />}

View File

@ -17,15 +17,16 @@ const styles = () => ({
fontFamily: 'Averta, sans-serif',
},
account: {
paddingRight: sm,
alignItems: 'start',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'start',
flexGrow: 1,
justifyContent: 'center',
paddingRight: sm,
},
connect: {
letterSpacing: '-0.5px',
whiteSpace: 'nowrap',
},
})

View File

@ -8,7 +8,7 @@ import ExpandLessIcon from '@material-ui/icons/ExpandLess'
import Paragraph from '~/components/layout/Paragraph'
import Col from '~/components/layout/Col'
import {
xs, sm, md, border,
xs, sm, md, border, screenSm,
} from '~/theme/variables'
import { safesCountSelector } from '~/routes/safe/store/selectors'
import { SidebarContext } from '~/components/Sidebar'
@ -18,7 +18,11 @@ export const TOGGLE_SIDEBAR_BTN_TESTID = 'TOGGLE_SIDEBAR_BTN'
const useStyles = makeStyles({
container: {
flexGrow: 0,
padding: `0 ${md}`,
padding: `0 ${sm}`,
[`@media (min-width: ${screenSm}px)`]: {
paddingLeft: md,
paddingRight: md,
},
},
counter: {
background: border,
@ -60,7 +64,7 @@ const SafeListHeader = ({ safesCount }: Props) => {
)
}
export default connect<Object, Object, ?Function, ?Object>(
export default connect<Object, Object,?Function,?Object>(
// $FlowFixMe
(state) => ({ safesCount: safesCountSelector(state) }),
null,

View File

@ -81,11 +81,11 @@ class HeaderComponent extends React.PureComponent<Props, State> {
return (
<UserDetails
provider={provider}
network={network}
userAddress={userAddress}
connected={available}
network={network}
onDisconnect={this.onDisconnect}
provider={provider}
userAddress={userAddress}
/>
)
}

View File

@ -57,6 +57,7 @@ const useStyles = makeStyles({
},
safeName: {
color: primary,
overflowWrap: 'break-word',
},
safeAddress: {
color: disabled,

View File

@ -97,32 +97,34 @@ const Sidebar = ({
<SidebarContext.Provider value={{ isOpen, toggleSidebar }}>
<ClickAwayListener onClickAway={toggleSidebar}>
<Drawer
className={classes.sidebar}
open={isOpen}
onKeyDown={handleEsc}
classes={{ paper: classes.sidebarPaper }}
ModalProps={{ onBackdropClick: toggleSidebar }}
className={classes.sidebar}
classes={{ paper: classes.sidebarPaper }}
onKeyDown={handleEsc}
open={isOpen}
>
<Row align="center">
<SearchIcon className={classes.searchIcon} />
<SearchBar
classes={searchClasses}
placeholder="Search by name or address"
searchIcon={<div />}
onChange={handleFilterChange}
onCancelSearch={handleFilterCancel}
value={filter}
/>
<Divider />
<Spacer />
<Row className={classes.topComponents} align="center">
<Row className={classes.searchWrapper} align="center">
<SearchIcon className={classes.searchIcon} />
<SearchBar
classes={searchClasses}
onCancelSearch={handleFilterCancel}
onChange={handleFilterChange}
placeholder="Search by name or address"
searchIcon={<div />}
value={filter}
/>
</Row>
<Divider className={classes.divider} />
<Spacer className={classes.spacer} />
<Button
component={Link}
to={WELCOME_ADDRESS}
className={classes.addSafeBtn}
variant="contained"
size="small"
color="primary"
component={Link}
onClick={toggleSidebar}
size="small"
to={WELCOME_ADDRESS}
variant="contained"
>
+ Add Safe
</Button>

View File

@ -1,7 +1,7 @@
// @flow
import { makeStyles } from '@material-ui/core/styles'
import {
xs, mediumFontSize, secondaryText, md, headerHeight,
xs, mediumFontSize, secondaryText, md, headerHeight, screenSm,
} from '~/theme/variables'
const sidebarWidth = '400px'
@ -12,17 +12,50 @@ const sidebarBorderRadius = '8px'
const useSidebarStyles = makeStyles({
sidebar: {
width: sidebarWidth,
marginLeft: sidebarMarginLeft,
borderRadius: sidebarBorderRadius,
marginLeft: sidebarMarginLeft,
top: sidebarMarginTop,
width: sidebarWidth,
},
sidebarPaper: {
width: sidebarWidth,
marginLeft: sidebarMarginLeft,
top: `calc(${headerHeight} + ${sidebarMarginTop})`,
maxHeight: `calc(100vh - ${headerHeight} - ${sidebarMarginTop} - ${sidebarMarginBottom})`,
borderRadius: sidebarBorderRadius,
marginLeft: sidebarMarginLeft,
maxHeight: `calc(100vh - ${headerHeight} - ${sidebarMarginTop} - ${sidebarMarginBottom})`,
top: `calc(${headerHeight} + ${sidebarMarginTop})`,
width: sidebarWidth,
maxWidth: `calc(100% - ${sidebarMarginLeft} - ${sidebarMarginLeft})`,
[`@media (min-width: ${screenSm}px)`]: {
maxWidth: 'none',
},
},
topComponents: {
alignItems: 'center',
flexFlow: 'column',
paddingBottom: '30px',
[`@media (min-width: ${screenSm}px)`]: {
flexFlow: 'row',
paddingBottom: '0',
},
},
searchWrapper: {
width: '100%',
[`@media (min-width: ${screenSm}px)`]: {
width: 'auto',
},
},
divider: {
display: 'none',
[`@media (min-width: ${screenSm}px)`]: {
display: 'block',
},
},
spacer: {
display: 'none',
[`@media (min-width: ${screenSm}px)`]: {
display: 'block',
},
},
headerPlaceholder: {
minHeight: headerHeight,
@ -48,14 +81,16 @@ const useSidebarStyles = makeStyles({
},
},
searchContainer: {
width: '190px',
flexGrow: '1',
marginLeft: xs,
marginRight: xs,
minWidth: '190px',
},
searchRoot: {
letterSpacing: '-0.5px',
border: 'none',
boxShadow: 'none',
flexGrow: '1',
'& > button': {
display: 'none',
},

View File

@ -1,8 +1,12 @@
// @flow
import * as React from 'react'
type Props = {
className?: string,
}
const style = {
flexGrow: 1,
}
export default () => <div style={style} />
export default ({ className }: Props) => <div className={className} style={style} />

View File

@ -2,11 +2,15 @@
import * as React from 'react'
import { border } from '~/theme/variables'
const style = {
height: '100%',
borderRight: `solid 2px ${border}`,
type Props = {
className?: string,
}
const Divider = () => <div style={style} />
const style = {
borderRight: `solid 2px ${border}`,
height: '100%',
}
const Divider = ({ className }: Props) => <div className={className} style={style} />
export default Divider

View File

@ -12,16 +12,19 @@ const calculateStyleFrom = (color?: string, margin?: Size) => ({
})
type Props = {
margin?: Size,
className?: string,
color?: string,
margin?: Size,
style?: Object,
}
const Hairline = ({ margin, color, style }: Props) => {
const Hairline = ({
margin, color, style, className,
}: Props) => {
const calculatedStyles = calculateStyleFrom(color, margin)
const mergedStyles = { ...calculatedStyles, ...(style || {}) }
return <div style={mergedStyles} />
return <div style={mergedStyles} className={className} />
}
export default Hairline

View File

@ -1,6 +1,7 @@
// @flow
import React, { useState, useEffect } from 'react'
import { withStyles } from '@material-ui/core/styles'
import TableContainer from '@material-ui/core/TableContainer'
import Block from '~/components/layout/Block'
import Field from '~/components/forms/Field'
import { required } from '~/components/forms/validator'
@ -14,7 +15,7 @@ import Hairline from '~/components/layout/Hairline'
import EtherscanBtn from '~/components/EtherscanBtn'
import CopyBtn from '~/components/CopyBtn'
import {
sm, md, lg, border, disabled, extraSmallFontSize,
sm, md, lg, border, disabled, extraSmallFontSize, screenSm,
} from '~/theme/variables'
import { getOwnerNameBy, getOwnerAddressBy } from '~/routes/open/components/fields'
import { FIELD_LOAD_ADDRESS, THRESHOLD } from '~/routes/load/components/fields'
@ -30,8 +31,13 @@ const styles = () => ({
display: 'flex',
justifyContent: 'flex-start',
},
ownerNames: {
maxWidth: '400px',
ownerName: {
marginBottom: '15px',
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
marginBottom: '0',
minWidth: '0',
},
},
ownerAddresses: {
alignItems: 'center',
@ -118,39 +124,41 @@ const OwnerListComponent = (props: Props) => {
</Paragraph>
</Block>
<Hairline />
<Row className={classes.header}>
<Col xs={4}>NAME</Col>
<Col xs={8}>ADDRESS</Col>
</Row>
<Hairline />
<Block margin="md" padding="md">
{owners.map((address, index) => (
<Row key={address} className={classes.owner}>
<Col xs={4}>
<Field
className={classes.name}
name={getOwnerNameBy(index)}
component={TextField}
type="text"
validate={required}
initialValue={`Owner #${index + 1}`}
placeholder="Owner Name*"
text="Owner Name"
/>
</Col>
<Col xs={8}>
<Row className={classes.ownerAddresses}>
<Identicon address={address} diameter={32} />
<Paragraph size="md" color="disabled" noMargin className={classes.address}>
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
</Row>
</Col>
</Row>
))}
</Block>
<TableContainer>
<Row className={classes.header}>
<Col xs={4}>NAME</Col>
<Col xs={8}>ADDRESS</Col>
</Row>
<Hairline />
<Block margin="md" padding="md">
{owners.map((address, index) => (
<Row key={address} className={classes.owner}>
<Col className={classes.ownerName} xs={4}>
<Field
className={classes.name}
component={TextField}
initialValue={`Owner #${index + 1}`}
name={getOwnerNameBy(index)}
placeholder="Owner Name*"
text="Owner Name"
type="text"
validate={required}
/>
</Col>
<Col xs={8}>
<Row className={classes.ownerAddresses}>
<Identicon address={address} diameter={32} />
<Paragraph size="md" color="disabled" noMargin className={classes.address}>
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
</Row>
</Col>
</Row>
))}
</Block>
</TableContainer>
</>
)
}

View File

@ -2,6 +2,7 @@
import * as React from 'react'
import classNames from 'classnames'
import { withStyles } from '@material-ui/core/styles'
import TableContainer from '@material-ui/core/TableContainer'
import Block from '~/components/layout/Block'
import Identicon from '~/components/Identicon'
import OpenPaper from '~/components/Stepper/OpenPaper'
@ -12,7 +13,7 @@ import Paragraph from '~/components/layout/Paragraph'
import CopyBtn from '~/components/CopyBtn'
import Hairline from '~/components/layout/Hairline'
import {
xs, sm, lg, border,
xs, sm, lg, border, screenSm,
} from '~/theme/variables'
import { shortVersionOf } from '~/logic/wallets/ethAddresses'
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
@ -22,7 +23,23 @@ import type { LayoutProps } from '../Layout'
const styles = () => ({
root: {
flexDirection: 'column',
minHeight: '300px',
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
},
detailsColumn: {
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '0',
},
},
ownersColumn: {
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '0',
},
},
details: {
padding: lg,
@ -40,9 +57,10 @@ const styles = () => ({
whiteSpace: 'nowrap',
},
owner: {
alignItems: 'center',
minWidth: 'fit-content',
padding: sm,
paddingLeft: lg,
alignItems: 'center',
},
user: {
justifyContent: 'left',
@ -101,7 +119,7 @@ class ReviewComponent extends React.PureComponent<Props, State> {
return (
<>
<Row className={classes.root}>
<Col xs={4} layout="column">
<Col className={classes.detailsColumn} xs={4} layout="column">
<Block className={classes.details}>
<Block margin="lg">
<Paragraph size="lg" color="primary" noMargin>
@ -147,37 +165,39 @@ class ReviewComponent extends React.PureComponent<Props, State> {
</Block>
</Block>
</Col>
<Col xs={8} layout="column">
<Block className={classes.owners}>
<Paragraph size="lg" color="primary" noMargin>
{`${getNumOwnersFrom(values)} Safe owners`}
</Paragraph>
</Block>
<Hairline />
{owners.map((address, index) => (
<React.Fragment key={address}>
<Row className={classes.owner}>
<Col xs={1} align="center">
<Identicon address={address} diameter={32} />
</Col>
<Col xs={11}>
<Block className={classNames(classes.name, classes.userName)}>
<Paragraph size="lg" noMargin>
{values[getOwnerNameBy(index)]}
</Paragraph>
<Block justify="center" className={classes.user}>
<Paragraph size="md" color="disabled" noMargin>
{address}
<Col className={classes.ownersColumn} xs={8} layout="column">
<TableContainer>
<Block className={classes.owners}>
<Paragraph size="lg" color="primary" noMargin>
{`${getNumOwnersFrom(values)} Safe owners`}
</Paragraph>
</Block>
<Hairline />
{owners.map((address, index) => (
<>
<Row className={classes.owner}>
<Col xs={1} align="center">
<Identicon address={address} diameter={32} />
</Col>
<Col xs={11}>
<Block className={classNames(classes.name, classes.userName)}>
<Paragraph size="lg" noMargin>
{values[getOwnerNameBy(index)]}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
<Block justify="center" className={classes.user}>
<Paragraph size="md" color="disabled" noMargin>
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
</Block>
</Block>
</Block>
</Col>
</Row>
{index !== owners.length - 1 && <Hairline />}
</React.Fragment>
))}
</Col>
</Row>
{index !== owners.length - 1 && <Hairline />}
</>
))}
</TableContainer>
</Col>
</Row>
</>

View File

@ -2,6 +2,7 @@
import * as React from 'react'
import classNames from 'classnames'
import { withStyles } from '@material-ui/core/styles'
import TableContainer from '@material-ui/core/TableContainer'
import { estimateGasForDeployingSafe } from '~/logic/contracts/safeContracts'
import { getNamesFrom, getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
import Block from '~/components/layout/Block'
@ -14,7 +15,7 @@ import Row from '~/components/layout/Row'
import Paragraph from '~/components/layout/Paragraph'
import { formatAmount } from '~/logic/tokens/utils/formatAmount'
import {
sm, md, lg, border, background,
sm, md, lg, border, background, screenSm,
} from '~/theme/variables'
import Hairline from '~/components/layout/Hairline'
import { getWeb3 } from '~/logic/wallets/getWeb3'
@ -25,6 +26,21 @@ const { useEffect, useState } = React
const styles = () => ({
root: {
minHeight: '300px',
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
},
detailsColumn: {
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '0',
},
},
ownersColumn: {
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '0',
},
},
details: {
padding: lg,
@ -33,10 +49,10 @@ const styles = () => ({
},
info: {
backgroundColor: background,
padding: lg,
justifyContent: 'center',
textAlign: 'center',
flexDirection: 'column',
justifyContent: 'center',
padding: lg,
textAlign: 'center',
},
owners: {
padding: lg,
@ -49,8 +65,10 @@ const styles = () => ({
whiteSpace: 'nowrap',
},
owner: {
padding: md,
alignItems: 'center',
minWidth: 'fit-content',
padding: sm,
paddingLeft: lg,
},
user: {
justifyContent: 'left',
@ -102,7 +120,7 @@ const ReviewComponent = ({ values, classes, userAccount }: Props) => {
return (
<>
<Row className={classes.root}>
<Col xs={4} layout="column">
<Col className={classes.detailsColumn} xs={4} layout="column">
<Block className={classes.details}>
<Block margin="lg">
<Paragraph size="lg" color="primary" noMargin>
@ -127,37 +145,39 @@ const ReviewComponent = ({ values, classes, userAccount }: Props) => {
</Block>
</Block>
</Col>
<Col xs={8} layout="column">
<Block className={classes.owners}>
<Paragraph size="lg" color="primary" noMargin>
{`${numOwners} Safe owners`}
</Paragraph>
</Block>
<Hairline />
{names.map((name, index) => (
<React.Fragment key={`name${index}`}>
<Row className={classes.owner}>
<Col xs={1} align="center">
<Identicon address={addresses[index]} diameter={32} />
</Col>
<Col xs={11}>
<Block className={classNames(classes.name, classes.userName)}>
<Paragraph size="lg" noMargin>
{name}
</Paragraph>
<Block justify="center" className={classes.user}>
<Paragraph size="md" color="disabled" noMargin>
{addresses[index]}
<Col className={classes.ownersColumn} xs={8} layout="column">
<TableContainer>
<Block className={classes.owners}>
<Paragraph size="lg" color="primary" noMargin>
{`${numOwners} Safe owners`}
</Paragraph>
</Block>
<Hairline />
{names.map((name, index) => (
<React.Fragment key={`name${index}`}>
<Row className={classes.owner}>
<Col xs={1} align="center">
<Identicon address={addresses[index]} diameter={32} />
</Col>
<Col xs={11}>
<Block className={classNames(classes.name, classes.userName)}>
<Paragraph size="lg" noMargin>
{name}
</Paragraph>
<CopyBtn content={addresses[index]} />
<EtherscanBtn type="address" value={addresses[index]} />
<Block justify="center" className={classes.user}>
<Paragraph size="md" color="disabled" noMargin>
{addresses[index]}
</Paragraph>
<CopyBtn content={addresses[index]} />
<EtherscanBtn type="address" value={addresses[index]} />
</Block>
</Block>
</Block>
</Col>
</Row>
<Hairline />
</React.Fragment>
))}
</Col>
</Row>
<Hairline />
</React.Fragment>
))}
</TableContainer>
</Col>
</Row>
<Row className={classes.info} align="center">

View File

@ -132,7 +132,7 @@ const SafeOwners = (props: Props) => {
return (
<Row key={`owner${index}`} className={classes.owner}>
<Col xs={4}>
<Col className={classes.ownerName} xs={4}>
<Field
className={classes.name}
name={getOwnerNameBy(index)}
@ -143,7 +143,7 @@ const SafeOwners = (props: Props) => {
text="Owner Name"
/>
</Col>
<Col xs={6}>
<Col className={classes.ownerAddress} xs={6}>
<AddressInput
name={addressName}
component={TextField}
@ -193,8 +193,8 @@ const SafeOwners = (props: Props) => {
<Paragraph size="md" color="primary">
Any transaction requires the confirmation of:
</Paragraph>
<Row margin="xl" align="center">
<Col xs={2}>
<Row className={classes.ownersAmount} margin="xl" align="center">
<Col className={classes.ownersAmountItem} xs={2}>
<Field
name={FIELD_CONFIRMATIONS}
component={SelectField}
@ -208,7 +208,7 @@ const SafeOwners = (props: Props) => {
))}
</Field>
</Col>
<Col xs={10}>
<Col className={classes.ownersAmountItem} xs={10}>
<Paragraph size="lg" color="primary" noMargin className={classes.owners}>
out of
{' '}

View File

@ -1,6 +1,6 @@
// @flow
import {
md, lg, sm, disabled, extraSmallFontSize,
md, lg, sm, disabled, extraSmallFontSize, screenSm,
} from '~/theme/variables'
export const styles = () => ({
@ -11,11 +11,32 @@ export const styles = () => ({
padding: `${md} ${lg}`,
},
owner: {
padding: `0 ${lg}`,
flexDirection: 'column',
marginTop: '12px',
padding: `0 ${lg}`,
'&:first-child': {
marginTop: 0,
},
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
},
ownerName: {
marginBottom: '5px',
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
marginBottom: '0',
minWidth: '0',
},
},
ownerAddress: {
marginBottom: '15px',
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
marginBottom: '0',
minWidth: '0',
},
},
header: {
padding: `${sm} ${lg}`,
@ -45,4 +66,16 @@ export const styles = () => ({
owners: {
paddingLeft: md,
},
ownersAmount: {
flexDirection: 'column',
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
},
ownersAmountItem: {
minWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '0',
},
},
})

View File

@ -9,6 +9,7 @@ import { withStyles } from '@material-ui/core/styles'
import classNames from 'classnames/bind'
import CallMade from '@material-ui/icons/CallMade'
import { useDispatch, useSelector } from 'react-redux'
import TableContainer from '@material-ui/core/TableContainer'
import Block from '~/components/layout/Block'
import Row from '~/components/layout/Row'
import { type Column, cellWidth } from '~/components/Table/TableHead'
@ -123,97 +124,99 @@ const AddressBookTable = ({ classes }: Props) => {
</Col>
</Row>
<Block className={classes.formContainer}>
<Table
label="Owners"
columns={columns}
data={addressBook}
size={addressBook.size}
defaultFixed
disableLoadingOnEmptyTable
defaultRowsPerPage={25}
>
{(sortedData: List<OwnerRow>) => sortedData.map((row: AddressBookEntry, index: number) => {
const userOwner = isUserOwnerOnAnySafe(safesList, row.address)
const hideBorderBottom = index >= 3
<TableContainer>
<Table
label="Owners"
columns={columns}
data={addressBook}
size={addressBook.size}
defaultFixed
disableLoadingOnEmptyTable
defaultRowsPerPage={25}
>
{(sortedData: List<OwnerRow>) => sortedData.map((row: AddressBookEntry, index: number) => {
const userOwner = isUserOwnerOnAnySafe(safesList, row.address)
const hideBorderBottom = index >= 3
&& index === sortedData.size - 1
&& classes.noBorderBottom
return (
<TableRow
tabIndex={-1}
key={index}
className={cn(
classes.hide,
hideBorderBottom,
)}
data-testid={ADDRESS_BOOK_ROW_ID}
>
{autoColumns.map((column: Column) => (
<TableCell
key={column.id}
style={cellWidth(column.width)}
align={column.align}
component="td"
>
{column.id === AB_ADDRESS_ID ? (
<OwnerAddressTableCell
address={row[column.id]}
showLinks
/>
) : (
row[column.id]
)}
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Img
alt="Edit entry"
className={classes.editEntryButton}
src={RenameOwnerIcon}
onClick={() => {
setSelectedEntry({ entry: { ...row, isOwnerAddress: userOwner } })
setEditCreateEntryModalOpen(true)
}}
testId={EDIT_ENTRY_BUTTON}
/>
<Img
alt="Remove entry"
className={userOwner ? classes.removeEntryButtonDisabled : classes.removeEntryButton}
src={userOwner ? RemoveOwnerIconDisabled : RemoveOwnerIcon}
onClick={() => {
if (!userOwner) {
setSelectedEntry({ entry: row })
setDeleteEntryModalOpen(true)
}
}}
testId={REMOVE_ENTRY_BUTTON}
/>
<Button
variant="contained"
size="small"
color="primary"
className={classes.send}
testId={SEND_ENTRY_BUTTON}
onClick={() => {
setSelectedEntry({ entry: row })
setSendFundsModalOpen(true)
}}
return (
<TableRow
tabIndex={-1}
key={index}
className={cn(
classes.hide,
hideBorderBottom,
)}
data-testid={ADDRESS_BOOK_ROW_ID}
>
{autoColumns.map((column: Column) => (
<TableCell
key={column.id}
style={cellWidth(column.width)}
align={column.align}
component="td"
>
<CallMade
alt="Send Transaction"
className={classNames(
classes.leftIcon,
classes.iconSmall,
)}
{column.id === AB_ADDRESS_ID ? (
<OwnerAddressTableCell
address={row[column.id]}
showLinks
/>
) : (
row[column.id]
)}
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Img
alt="Edit entry"
className={classes.editEntryButton}
src={RenameOwnerIcon}
onClick={() => {
setSelectedEntry({ entry: { ...row, isOwnerAddress: userOwner } })
setEditCreateEntryModalOpen(true)
}}
testId={EDIT_ENTRY_BUTTON}
/>
<Img
alt="Remove entry"
className={userOwner ? classes.removeEntryButtonDisabled : classes.removeEntryButton}
src={userOwner ? RemoveOwnerIconDisabled : RemoveOwnerIcon}
onClick={() => {
if (!userOwner) {
setSelectedEntry({ entry: row })
setDeleteEntryModalOpen(true)
}
}}
testId={REMOVE_ENTRY_BUTTON}
/>
<Button
variant="contained"
size="small"
color="primary"
className={classes.send}
testId={SEND_ENTRY_BUTTON}
onClick={() => {
setSelectedEntry({ entry: row })
setSendFundsModalOpen(true)
}}
>
<CallMade
alt="Send Transaction"
className={classNames(
classes.leftIcon,
classes.iconSmall,
)}
/>
Send
</Button>
</Row>
</TableCell>
</TableRow>
)
})}
</Table>
</Button>
</Row>
</TableCell>
</TableRow>
)
})}
</Table>
</TableContainer>
</Block>
<CreateEditEntryModal
onClose={() => setEditCreateEntryModalOpen(false)}

View File

@ -14,7 +14,7 @@ import Col from '~/components/layout/Col'
import EtherscanBtn from '~/components/EtherscanBtn'
import CopyBtn from '~/components/CopyBtn'
import {
sm, lg, md, secondaryText,
sm, lg, md, secondaryText, screenSm,
} from '~/theme/variables'
import { copyToClipboard } from '~/utils/clipboard'
@ -53,11 +53,23 @@ const styles = () => ({
},
},
addressContainer: {
flexDirection: 'column',
justifyContent: 'center',
margin: `${lg} 0`,
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
},
address: {
marginLeft: sm,
marginRight: sm,
maxWidth: '70%',
overflowWrap: 'break-word',
[`@media (min-width: ${screenSm}px)`]: {
maxWidth: 'none',
},
},
})

View File

@ -4,6 +4,7 @@ import { List } from 'immutable'
import classNames from 'classnames/bind'
import TableRow from '@material-ui/core/TableRow'
import TableCell from '@material-ui/core/TableCell'
import TableContainer from '@material-ui/core/TableContainer'
import { withStyles } from '@material-ui/core/styles'
import CallMade from '@material-ui/icons/CallMade'
import CallReceived from '@material-ui/icons/CallReceived'
@ -148,93 +149,95 @@ class Balances extends React.Component<Props, State> {
</Modal>
</Col>
</Row>
<Table
label="Balances"
defaultOrderBy={BALANCE_TABLE_ASSET_ID}
defaultRowsPerPage={10}
columns={columns}
data={filteredData}
size={filteredData.size}
defaultFixed
>
{(sortedData: Array<BalanceRow>) => sortedData.map((row: any, index: number) => (
<TableRow tabIndex={-1} key={index} className={classes.hide} data-testid={BALANCE_ROW_TEST_ID}>
{autoColumns.map((column: Column) => {
const { id, width, align } = column
let cellItem
switch (id) {
case BALANCE_TABLE_ASSET_ID: {
cellItem = <AssetTableCell asset={row[id]} />
break
<TableContainer>
<Table
columns={columns}
data={filteredData}
defaultFixed
defaultOrderBy={BALANCE_TABLE_ASSET_ID}
defaultRowsPerPage={10}
label="Balances"
size={filteredData.size}
>
{(sortedData: Array<BalanceRow>) => sortedData.map((row: any, index: number) => (
<TableRow tabIndex={-1} key={index} className={classes.hide} data-testid={BALANCE_ROW_TEST_ID}>
{autoColumns.map((column: Column) => {
const { id, width, align } = column
let cellItem
switch (id) {
case BALANCE_TABLE_ASSET_ID: {
cellItem = <AssetTableCell asset={row[id]} />
break
}
case BALANCE_TABLE_BALANCE_ID: {
cellItem = (
<div>
{row[id]}
</div>
)
break
}
case BALANCE_TABLE_VALUE_ID: {
cellItem = <div className={classes.currencyValueRow}>{row[id]}</div>
break
}
default: {
cellItem = null
break
}
}
case BALANCE_TABLE_BALANCE_ID: {
cellItem = (
<div>
{row[id]}
</div>
)
break
}
case BALANCE_TABLE_VALUE_ID: {
cellItem = <div className={classes.currencyValueRow}>{row[id]}</div>
break
}
default: {
cellItem = null
break
}
}
return (
<TableCell
key={id}
style={cellWidth(width)}
align={align}
component="td"
>
{cellItem}
</TableCell>
)
})}
<TableCell component="td">
<Row align="end" className={classes.actions}>
{granted && (
return (
<TableCell
key={id}
style={cellWidth(width)}
align={align}
component="td"
>
{cellItem}
</TableCell>
)
})}
<TableCell component="td">
<Row align="end" className={classes.actions}>
{granted && (
<Button
variant="contained"
size="small"
color="primary"
className={classes.send}
onClick={() => this.showSendFunds(row.asset.address)}
testId="balance-send-btn"
>
<CallMade alt="Send Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Send
</Button>
)}
<Button
variant="contained"
size="small"
color="primary"
className={classes.send}
onClick={() => this.showSendFunds(row.asset.address)}
testId="balance-send-btn"
className={classes.receive}
onClick={this.onShow('Receive')}
>
<CallMade alt="Send Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Send
</Button>
)}
<Button
variant="contained"
size="small"
color="primary"
className={classes.receive}
onClick={this.onShow('Receive')}
>
<CallReceived alt="Receive Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
<CallReceived alt="Receive Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Receive
</Button>
</Row>
</TableCell>
</TableRow>
))}
</Table>
</Button>
</Row>
</TableCell>
</TableRow>
))}
</Table>
</TableContainer>
<SendModal
onClose={this.hideSendFunds}
activeScreenType="sendFunds"
createTransaction={createTransaction}
ethBalance={ethBalance}
isOpen={sendFunds.isOpen}
onClose={this.hideSendFunds}
safeAddress={safeAddress}
safeName={safeName}
ethBalance={ethBalance}
tokens={activeTokens}
selectedToken={sendFunds.selectedToken}
createTransaction={createTransaction}
activeScreenType="sendFunds"
tokens={activeTokens}
/>
<Modal
title="Receive Tokens"

View File

@ -20,7 +20,10 @@ export const styles = (theme: Object) => ({
fontSize: 16,
},
receiveModal: {
height: '544px',
height: 'auto',
maxWidth: 'calc(100% - 30px)',
minHeight: '544px',
overflow: 'hidden',
},
hide: {
'&:hover': {

View File

@ -133,93 +133,86 @@ const Layout = (props: Props) => {
return (
<>
<Block className={classes.container} margin="xl">
<Identicon address={address} diameter={50} />
<Block className={classes.name}>
<Row>
<Heading tag="h2" color="primary" testId={SAFE_VIEW_NAME_HEADING_TEST_ID}>
{name}
</Heading>
{!granted && <Block className={classes.readonly}>Read Only</Block>}
</Row>
<Block justify="center" className={classes.user}>
<Paragraph size="md" className={classes.address} color="disabled" noMargin>
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
<Row className={classes.userInfo}>
<Identicon address={address} diameter={50} />
<Block className={classes.name}>
<Row>
<Heading className={classes.nameText} tag="h2" color="primary" testId={SAFE_VIEW_NAME_HEADING_TEST_ID}>
{name}
</Heading>
{!granted && <Block className={classes.readonly}>Read Only</Block>}
</Row>
<Block justify="center" className={classes.user}>
<Paragraph size="md" className={classes.address} color="disabled" noMargin>
{address}
</Paragraph>
<CopyBtn content={address} />
<EtherscanBtn type="address" value={address} />
</Block>
</Block>
</Block>
</Row>
<Block className={classes.balance}>
<Row align="end" className={classes.actions}>
<Button
variant="contained"
size="small"
color="primary"
className={classes.send}
onClick={() => showSendFunds('Ether')}
disabled={!granted}
>
<CallMade alt="Send Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Send
</Button>
<Button
variant="contained"
size="small"
color="primary"
className={classes.receive}
onClick={onShow('Receive')}
>
<CallReceived alt="Receive Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Receive
</Button>
</Row>
<Button
className={classes.send}
color="primary"
disabled={!granted}
onClick={() => showSendFunds('Ether')}
size="small"
variant="contained"
>
<CallMade alt="Send Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Send
</Button>
<Button
className={classes.receive}
color="primary"
onClick={onShow('Receive')}
size="small"
variant="contained"
>
<CallReceived alt="Receive Transaction" className={classNames(classes.leftIcon, classes.iconSmall)} />
Receive
</Button>
</Block>
</Block>
<Row>
<Tabs
value={location.pathname}
onChange={handleCallToRouter}
indicatorColor="secondary"
textColor="secondary"
>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
label={labelBalances}
value={`${match.url}/balances`}
data-testid={BALANCES_TAB_BTN_TEST_ID}
/>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
label={labelTransactions}
value={`${match.url}/transactions`}
data-testid={TRANSACTIONS_TAB_BTN_TEST_ID}
/>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
label={labelAddressBook}
value={`${match.url}/address-book`}
data-testid={ADDRESS_BOOK_TAB_BTN_TEST_ID}
/>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
label={labelSettings}
value={`${match.url}/settings`}
data-testid={SETTINGS_TAB_BTN_TEST_ID}
/>
</Tabs>
</Row>
<Tabs variant="scrollable" value={location.pathname} onChange={handleCallToRouter} indicatorColor="secondary" textColor="secondary">
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
data-testid={BALANCES_TAB_BTN_TEST_ID}
label={labelBalances}
value={`${match.url}/balances`}
/>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
data-testid={TRANSACTIONS_TAB_BTN_TEST_ID}
label={labelTransactions}
value={`${match.url}/transactions`}
/>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
data-testid={ADDRESS_BOOK_TAB_BTN_TEST_ID}
label={labelAddressBook}
value={`${match.url}/address-book`}
/>
<Tab
classes={{
selected: classes.tabWrapperSelected,
wrapper: classes.tabWrapper,
}}
data-testid={SETTINGS_TAB_BTN_TEST_ID}
label={labelSettings}
value={`${match.url}/settings`}
/>
</Tabs>
<Hairline color={border} style={{ marginTop: '-2px' }} />
<Switch>
<Route
@ -283,13 +276,7 @@ const Layout = (props: Props) => {
/>
)}
/>
<Route
exact
path={`${match.path}/address-book`}
render={() => (
<AddressBookTable />
)}
/>
<Route exact path={`${match.path}/address-book`} render={() => <AddressBookTable />} />
<Redirect to={`${match.path}/balances`} />
</Switch>
<SendModal
@ -304,11 +291,11 @@ const Layout = (props: Props) => {
activeScreenType="chooseTxType"
/>
<Modal
title="Receive Tokens"
description="Receive Tokens Form"
handleClose={onHide('Receive')}
open={showReceive}
paperClassName={classes.receiveModal}
title="Receive Tokens"
>
<Receive safeName={name} safeAddress={address} onClose={onHide('Receive')} />
</Modal>

View File

@ -5,6 +5,7 @@ import { List } from 'immutable'
import { withStyles } from '@material-ui/core/styles'
import TableRow from '@material-ui/core/TableRow'
import TableCell from '@material-ui/core/TableCell'
import TableContainer from '@material-ui/core/TableContainer'
import Block from '~/components/layout/Block'
import Col from '~/components/layout/Col'
import Table from '~/components/Table'
@ -145,66 +146,68 @@ class ManageOwners extends React.Component<Props, State> {
Add, remove and replace owners or rename existing owners. Owner names are only stored locally and never
shared with Gnosis or any third parties.
</Paragraph>
<Table
label="Owners"
defaultOrderBy={OWNERS_TABLE_NAME_ID}
columns={columns}
data={ownerData}
size={ownerData.size}
disablePagination
defaultFixed
noBorder
>
{(sortedData: List<OwnerRow>) => sortedData.map((row: any, index: number) => (
<TableRow
tabIndex={-1}
key={index}
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
data-testid={OWNERS_ROW_TEST_ID}
>
{autoColumns.map((column: Column) => (
<TableCell key={column.id} style={cellWidth(column.width)} align={column.align} component="td">
{column.id === OWNERS_TABLE_ADDRESS_ID ? (
<OwnerAddressTableCell address={row[column.id]} />
) : (
row[column.id]
)}
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Img
alt="Edit owner"
className={classes.editOwnerIcon}
src={RenameOwnerIcon}
onClick={this.onShow('EditOwner', row)}
testId={RENAME_OWNER_BTN_TEST_ID}
/>
{granted && (
<>
<Img
alt="Replace owner"
className={classes.replaceOwnerIcon}
src={ReplaceOwnerIcon}
onClick={this.onShow('ReplaceOwner', row)}
testId={REPLACE_OWNER_BTN_TEST_ID}
/>
{ownerData.size > 1 && (
<TableContainer>
<Table
label="Owners"
defaultOrderBy={OWNERS_TABLE_NAME_ID}
columns={columns}
data={ownerData}
size={ownerData.size}
disablePagination
defaultFixed
noBorder
>
{(sortedData: List<OwnerRow>) => sortedData.map((row: any, index: number) => (
<TableRow
tabIndex={-1}
key={index}
className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)}
data-testid={OWNERS_ROW_TEST_ID}
>
{autoColumns.map((column: Column) => (
<TableCell key={column.id} style={cellWidth(column.width)} align={column.align} component="td">
{column.id === OWNERS_TABLE_ADDRESS_ID ? (
<OwnerAddressTableCell address={row[column.id]} />
) : (
row[column.id]
)}
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Img
alt="Edit owner"
className={classes.editOwnerIcon}
src={RenameOwnerIcon}
onClick={this.onShow('EditOwner', row)}
testId={RENAME_OWNER_BTN_TEST_ID}
/>
{granted && (
<>
<Img
alt="Remove owner"
className={classes.removeOwnerIcon}
src={RemoveOwnerIcon}
onClick={this.onShow('RemoveOwner', row)}
testId={REMOVE_OWNER_BTN_TEST_ID}
alt="Replace owner"
className={classes.replaceOwnerIcon}
src={ReplaceOwnerIcon}
onClick={this.onShow('ReplaceOwner', row)}
testId={REPLACE_OWNER_BTN_TEST_ID}
/>
)}
</>
) }
</Row>
</TableCell>
</TableRow>
))}
</Table>
{ownerData.size > 1 && (
<Img
alt="Remove owner"
className={classes.removeOwnerIcon}
src={RemoveOwnerIcon}
onClick={this.onShow('RemoveOwner', row)}
testId={REMOVE_OWNER_BTN_TEST_ID}
/>
)}
</>
) }
</Row>
</TableCell>
</TableRow>
))}
</Table>
</TableContainer>
</Block>
{granted && (
<>

View File

@ -38,7 +38,7 @@ type Props = Actions & {
const RemoveSafeComponent = ({
onClose, isOpen, classes, safeAddress, etherScanLink, safeName, removeSafe,
}: Props) => (
<Modal title="Remove Safe" description="Remove the selected Safe" handleClose={onClose} open={isOpen}>
<Modal paperClassName={classes.modal} title="Remove Safe" description="Remove the selected Safe" handleClose={onClose} open={isOpen}>
<Row align="center" grow className={classes.heading}>
<Paragraph className={classes.manage} noMargin weight="bolder">
Remove Safe

View File

@ -5,10 +5,10 @@ import {
export const styles = () => ({
heading: {
padding: `${sm} ${lg}`,
boxSizing: 'border-box',
justifyContent: 'space-between',
maxHeight: '75px',
boxSizing: 'border-box',
padding: `${sm} ${lg}`,
},
container: {
minHeight: '369px',
@ -54,4 +54,9 @@ export const styles = () => ({
cursor: 'pointer',
},
},
modal: {
height: 'auto',
maxWidth: 'calc(100% - 30px)',
overflow: 'hidden',
},
})

View File

@ -12,16 +12,16 @@ export const styles = () => ({
maxWidth: '460px',
},
saveBtn: {
marginRight: sm,
fontWeight: boldFont,
marginRight: sm,
},
controlsRow: {
padding: lg,
position: 'absolute',
borderTop: `2px solid ${border}`,
bottom: 0,
boxSizing: 'border-box',
padding: lg,
position: 'absolute',
width: '100%',
borderTop: `2px solid ${border}`,
},
versionNumber: {
height: '21px',

View File

@ -117,7 +117,7 @@ class Settings extends React.Component<Props, State> {
/>
</Row>
<Block className={classes.root}>
<Col xs={3} layout="column">
<Col className={classes.menuWrapper} layout="column">
<Block className={classes.menu}>
<Row
className={cn(classes.menuOption, menuOptionIndex === 1 && classes.active)}
@ -126,7 +126,7 @@ class Settings extends React.Component<Props, State> {
<SafeDetailsIcon />
Safe details
</Row>
<Hairline />
<Hairline className={classes.hairline} />
<Row
className={cn(classes.menuOption, menuOptionIndex === 2 && classes.active)}
onClick={this.handleChange(2)}
@ -138,7 +138,7 @@ class Settings extends React.Component<Props, State> {
{owners.size}
</Paragraph>
</Row>
<Hairline />
<Hairline className={classes.hairline} />
<Row
className={cn(classes.menuOption, menuOptionIndex === 3 && classes.active)}
onClick={this.handleChange(3)}
@ -146,10 +146,10 @@ class Settings extends React.Component<Props, State> {
<RequiredConfirmationsIcon />
Policies
</Row>
<Hairline />
<Hairline className={classes.hairline} />
</Block>
</Col>
<Col xs={9} layout="column">
<Col className={classes.contents} layout="column">
<Block className={classes.container}>
{menuOptionIndex === 1 && (
<SafeDetails safeAddress={safeAddress} safeName={safeName} updateSafe={updateSafe} />

View File

@ -1,36 +1,85 @@
// @flow
import {
xs, sm, md, border, secondary, bolderFont, background, largeFontSize, fontColor,
xs, sm, md, border, secondary, bolderFont, background, largeFontSize, fontColor, screenSm,
} from '~/theme/variables'
export const styles = () => ({
root: {
backgroundColor: 'white',
boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
minHeight: '505px',
marginBottom: '54px',
display: 'flex',
borderRadius: sm,
boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
display: 'flex',
flexDirection: 'column',
marginBottom: '54px',
minHeight: '505px',
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
},
settings: {
letterSpacing: '-0.5px',
},
menuWrapper: {
display: 'flex',
flexDirection: 'row',
flexGrow: '0',
maxWidth: '100%',
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
maxWidth: 'unset',
},
},
menu: {
borderRight: `solid 2px ${border}`,
borderBottom: `solid 2px ${border}`,
display: 'flex',
flexDirection: 'row',
flexGrow: '1',
height: '100%',
width: '100%',
[`@media (min-width: ${screenSm}px)`]: {
borderBottom: 'none',
borderRight: `solid 2px ${border}`,
flexDirection: 'column',
width: '250px',
},
},
menuOption: {
alignItems: 'center',
borderRight: `solid 1px ${border}`,
boxSizing: 'border-box',
cursor: 'pointer',
fontSize: largeFontSize,
flexGrow: '1',
flexShrink: '1',
fontSize: '13px',
justifyContent: 'center',
lineHeight: '1.2',
padding: `${md} 0 ${md} ${md}`,
minWidth: '0',
padding: `${md} ${sm}`,
width: '100%',
[`@media (min-width: ${screenSm}px)`]: {
borderRight: 'none',
flexGrow: '0',
fontSize: largeFontSize,
justifyContent: 'flex-start',
padding: `${md} 0 ${md} ${md}`,
},
'&:last-of-type': {
borderRight: 'none',
},
'&:first-child': {
borderTopLeftRadius: sm,
},
'& svg': {
display: 'block',
marginRight: sm,
marginRight: xs,
maxWidth: '16px',
[`@media (min-width: ${screenSm}px)`]: {
marginRight: sm,
},
},
'& .fill': {
fill: fontColor,
@ -44,7 +93,18 @@ export const styles = () => ({
fill: secondary,
},
},
contents: {
width: '100%',
},
hairline: {
display: 'none',
[`@media (min-width: ${screenSm}px)`]: {
display: 'block',
},
},
container: {
flexGrow: '1',
height: '100%',
position: 'relative',
},

View File

@ -8,6 +8,7 @@ import ExpandLess from '@material-ui/icons/ExpandLess'
import ExpandMore from '@material-ui/icons/ExpandMore'
import TableRow from '@material-ui/core/TableRow'
import TableCell from '@material-ui/core/TableCell'
import TableContainer from '@material-ui/core/TableContainer'
import { withStyles } from '@material-ui/core/styles'
import Block from '~/components/layout/Block'
import Row from '~/components/layout/Row'
@ -81,77 +82,79 @@ const TxsTable = ({
return (
<Block className={classes.container}>
<Table
label="Transactions"
defaultOrderBy={TX_TABLE_ID}
defaultOrder="desc"
defaultRowsPerPage={25}
columns={columns}
data={filteredData}
size={filteredData.size}
defaultFixed
>
{(sortedData: Array<TransactionRow>) => sortedData.map((row: any, index: number) => (
<React.Fragment key={index}>
<TableRow
tabIndex={-1}
className={cn(classes.row, expandedTx === row.tx.safeTxHash && classes.expandedRow)}
onClick={() => handleTxExpand(row.tx.safeTxHash)}
data-testid={TRANSACTION_ROW_TEST_ID}
>
{autoColumns.map((column: Column) => (
<TableCell
key={column.id}
className={cn(classes.cell, row.status === 'cancelled' && classes.cancelledRow)}
style={cellWidth(column.width)}
align={column.align}
component="td"
>
{row[column.id]}
<TableContainer>
<Table
label="Transactions"
defaultOrderBy={TX_TABLE_ID}
defaultOrder="desc"
defaultRowsPerPage={25}
columns={columns}
data={filteredData}
size={filteredData.size}
defaultFixed
>
{(sortedData: Array<TransactionRow>) => sortedData.map((row: any, index: number) => (
<React.Fragment key={index}>
<TableRow
tabIndex={-1}
className={cn(classes.row, expandedTx === row.tx.safeTxHash && classes.expandedRow)}
onClick={() => handleTxExpand(row.tx.safeTxHash)}
data-testid={TRANSACTION_ROW_TEST_ID}
>
{autoColumns.map((column: Column) => (
<TableCell
key={column.id}
className={cn(classes.cell, row.status === 'cancelled' && classes.cancelledRow)}
style={cellWidth(column.width)}
align={column.align}
component="td"
>
{row[column.id]}
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Status status={row.status} />
</Row>
</TableCell>
))}
<TableCell component="td">
<Row align="end" className={classes.actions}>
<Status status={row.status} />
</Row>
</TableCell>
<TableCell style={expandCellStyle}>
{!row.tx.creationTx && (
<IconButton disableRipple>
{expandedTx === row.safeTxHash ? <ExpandLess /> : <ExpandMore />}
</IconButton>
)}
</TableCell>
</TableRow>
{!row.tx.creationTx && (
<TableRow>
<TableCell
style={{ paddingBottom: 0, paddingTop: 0 }}
colSpan={6}
className={classes.extendedTxContainer}
>
<Collapse
in={expandedTx === row.tx.safeTxHash}
timeout="auto"
component={ExpandedTxComponent}
unmountOnExit
tx={row[TX_TABLE_RAW_TX_ID]}
cancelTx={row[TX_TABLE_RAW_CANCEL_TX_ID]}
threshold={threshold}
owners={owners}
granted={granted}
userAddress={userAddress}
createTransaction={createTransaction}
processTransaction={processTransaction}
safeAddress={safeAddress}
nonce={nonce}
/>
<TableCell style={expandCellStyle}>
{!row.tx.creationTx && (
<IconButton disableRipple>
{expandedTx === row.safeTxHash ? <ExpandLess /> : <ExpandMore />}
</IconButton>
)}
</TableCell>
</TableRow>
)}
</React.Fragment>
))}
</Table>
{!row.tx.creationTx && (
<TableRow>
<TableCell
style={{ paddingBottom: 0, paddingTop: 0 }}
colSpan={6}
className={classes.extendedTxContainer}
>
<Collapse
in={expandedTx === row.tx.safeTxHash}
timeout="auto"
component={ExpandedTxComponent}
unmountOnExit
tx={row[TX_TABLE_RAW_TX_ID]}
cancelTx={row[TX_TABLE_RAW_CANCEL_TX_ID]}
threshold={threshold}
owners={owners}
granted={granted}
userAddress={userAddress}
createTransaction={createTransaction}
processTransaction={processTransaction}
safeAddress={safeAddress}
nonce={nonce}
/>
</TableCell>
</TableRow>
)}
</React.Fragment>
))}
</Table>
</TableContainer>
</Block>
)
}

View File

@ -1,12 +1,21 @@
// @flow
import {
sm, xs, smallFontSize, secondaryText, secondary,
sm, xs, smallFontSize, secondaryText, secondary, screenSm,
} from '~/theme/variables'
export const styles = () => ({
container: {
display: 'flex',
alignItems: 'center',
display: 'flex',
flexWrap: 'wrap',
},
userInfo: {
flexWrap: 'nowrap',
marginBottom: sm,
[`@media (min-width: ${screenSm}px)`]: {
marginBottom: '0',
},
},
name: {
marginLeft: sm,
@ -16,12 +25,22 @@ export const styles = () => ({
},
address: {
marginRight: sm,
overflow: 'hidden',
maxWidth: '50%',
[`@media (min-width: ${screenSm}px)`]: {
overflow: 'visible',
maxWidth: 'none',
},
},
user: {
justifyContent: 'left',
},
receiveModal: {
height: '544px',
height: 'auto',
maxWidth: 'calc(100% - 30px)',
minHeight: '544px',
overflow: 'hidden',
},
open: {
paddingLeft: sm,
@ -31,39 +50,53 @@ export const styles = () => ({
},
},
readonly: {
backgroundColor: secondaryText,
borderRadius: xs,
color: '#ffffff',
fontSize: smallFontSize,
letterSpacing: '0.5px',
color: '#ffffff',
backgroundColor: secondaryText,
textTransform: 'uppercase',
padding: `0 ${sm}`,
marginLeft: sm,
borderRadius: xs,
lineHeight: '28px',
marginLeft: sm,
padding: `0 ${sm}`,
textTransform: 'uppercase',
},
iconSmall: {
fontSize: 16,
},
balance: {
marginLeft: 'auto',
display: 'flex',
overflow: 'hidden',
width: '100%',
[`@media (min-width: ${screenSm}px)`]: {
marginLeft: 'auto',
width: 'auto',
},
},
receive: {
width: '95px',
minWidth: '95px',
marginLeft: sm,
borderRadius: '4px',
marginLeft: sm,
width: '50%',
'& > span': {
fontSize: '14px',
},
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '95px',
width: 'auto',
},
},
send: {
width: '75px',
minWidth: '75px',
borderRadius: '4px',
width: '50%',
'& > span': {
fontSize: '14px',
},
[`@media (min-width: ${screenSm}px)`]: {
minWidth: '75px',
width: 'auto',
},
},
leftIcon: {
marginRight: sm,
@ -84,4 +117,9 @@ export const styles = () => ({
fill: secondary,
},
},
nameText: {
overflowWrap: 'break-word',
wordBreak: 'break-word',
whiteSpace: 'normal',
},
})

737
yarn.lock

File diff suppressed because it is too large Load Diff