Feat: New styleguide, table component and screen

This commit is contained in:
Rafael Taranto 2019-11-11 15:34:14 +00:00
parent c100c11a2f
commit 8b4a1ff23f
20 changed files with 309 additions and 287 deletions

View file

@ -4737,7 +4737,8 @@
"asap": { "asap": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
"dev": true
}, },
"asn1": { "asn1": {
"version": "0.2.4", "version": "0.2.4",
@ -7705,6 +7706,7 @@
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
"integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==",
"dev": true,
"requires": { "requires": {
"fbjs": "^0.8.0", "fbjs": "^0.8.0",
"gud": "^1.0.0" "gud": "^1.0.0"
@ -8800,6 +8802,7 @@
"version": "0.1.12", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"dev": true,
"requires": { "requires": {
"iconv-lite": "~0.4.13" "iconv-lite": "~0.4.13"
} }
@ -10278,6 +10281,7 @@
"version": "0.8.17", "version": "0.8.17",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
"integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
"dev": true,
"requires": { "requires": {
"core-js": "^1.0.0", "core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1", "isomorphic-fetch": "^2.1.1",
@ -10291,12 +10295,14 @@
"core-js": { "core-js": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
"dev": true
}, },
"promise": { "promise": {
"version": "7.3.1", "version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"dev": true,
"requires": { "requires": {
"asap": "~2.0.3" "asap": "~2.0.3"
} }
@ -10618,17 +10624,16 @@
"dev": true "dev": true
}, },
"formik": { "formik": {
"version": "1.5.8", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/formik/-/formik-1.5.8.tgz", "resolved": "https://registry.npmjs.org/formik/-/formik-2.0.3.tgz",
"integrity": "sha512-fNvPe+ddbh+7xiByT25vuso2p2hseG/Yvuj211fV1DbCjljUEG9OpgRpcb7g7O3kxHX/q31cbZDzMxJXPWSNwA==", "integrity": "sha512-kYBvcxlsYSncY8OiJHD49C0UmoWXbgmIc9V1g3N1WwBJ7SMLk34QpcJDgroYd42K1cH+mSJlXhB7PlgTXTzlWg==",
"requires": { "requires": {
"create-react-context": "^0.2.2",
"deepmerge": "^2.1.1", "deepmerge": "^2.1.1",
"hoist-non-react-statics": "^3.3.0", "hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.14", "lodash": "^4.17.14",
"lodash-es": "^4.17.14", "lodash-es": "^4.17.14",
"prop-types": "^15.6.1",
"react-fast-compare": "^2.0.1", "react-fast-compare": "^2.0.1",
"scheduler": "^0.14.0",
"tiny-warning": "^1.0.2", "tiny-warning": "^1.0.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
@ -10637,6 +10642,15 @@
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
},
"scheduler": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.14.0.tgz",
"integrity": "sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
} }
} }
}, },
@ -11476,6 +11490,7 @@
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
@ -11975,7 +11990,8 @@
"is-stream": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
}, },
"is-svg": { "is-svg": {
"version": "3.0.0", "version": "3.0.0",
@ -12041,6 +12057,7 @@
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"dev": true,
"requires": { "requires": {
"node-fetch": "^1.0.1", "node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0" "whatwg-fetch": ">=0.10.0"
@ -14856,6 +14873,7 @@
"version": "1.7.3", "version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"dev": true,
"requires": { "requires": {
"encoding": "^0.1.11", "encoding": "^0.1.11",
"is-stream": "^1.0.1" "is-stream": "^1.0.1"
@ -19994,7 +20012,8 @@
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
}, },
"sane": { "sane": {
"version": "4.1.0", "version": "4.1.0",
@ -20318,7 +20337,8 @@
"setimmediate": { "setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
"dev": true
}, },
"setprototypeof": { "setprototypeof": {
"version": "1.1.1", "version": "1.1.1",
@ -22019,7 +22039,8 @@
"ua-parser-js": { "ua-parser-js": {
"version": "0.7.20", "version": "0.7.20",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz",
"integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==" "integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==",
"dev": true
}, },
"uglify-js": { "uglify-js": {
"version": "3.4.9", "version": "3.4.9",
@ -22849,7 +22870,8 @@
"whatwg-fetch": { "whatwg-fetch": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==",
"dev": true
}, },
"whatwg-mimetype": { "whatwg-mimetype": {
"version": "2.3.0", "version": "2.3.0",

View file

@ -11,7 +11,7 @@
"classnames": "2.2.6", "classnames": "2.2.6",
"downshift": "3.3.4", "downshift": "3.3.4",
"file-saver": "2.0.2", "file-saver": "2.0.2",
"formik": "1.5.8", "formik": "2.0.3",
"jss-plugin-extend": "^10.0.0", "jss-plugin-extend": "^10.0.0",
"lodash": "4.17.15", "lodash": "4.17.15",
"moment": "2.24.0", "moment": "2.24.0",

View file

@ -62,7 +62,9 @@ const Header = memo(({ tree }) => {
className={classnames(classes.link, classes.whiteLink)} className={classnames(classes.link, classes.whiteLink)}
activeClassName={classes.activeLink} activeClassName={classes.activeLink}
> >
{it.label} <span className={classes.forceSize} forcesize={it.label}>
{it.label}
</span>
</NavLink> </NavLink>
</li> </li>
))} ))}

View file

@ -11,10 +11,10 @@ import {
import typographyStyles from './typography/styles' import typographyStyles from './typography/styles'
const { tl2 } = typographyStyles const { tl2, p } = typographyStyles
let headerHeight = spacer * 7 let headerHeight = spacer * 7
let subheaderHeight = spacer * 6 let subheaderHeight = spacer * 5
if (version === 8) { if (version === 8) {
headerHeight = spacer * 8 headerHeight = spacer * 8
@ -51,11 +51,15 @@ export default {
padding: `0 ${spacer * 2.5}px` padding: `0 ${spacer * 2.5}px`
}, },
link: { link: {
extend: tl2, extend: p,
textDecoration: 'none', textDecoration: 'none',
border: 'none', border: 'none',
color: white, color: white,
backgroundColor: 'transparent', backgroundColor: 'transparent',
'&:hover': {
extend: tl2,
color: white
},
'&:hover::after': { '&:hover::after': {
width: '50%', width: '50%',
marginLeft: '-25%' marginLeft: '-25%'
@ -75,7 +79,21 @@ export default {
transition: 'all 0.2s cubic-bezier(0.95, 0.1, 0.45, 0.94)' transition: 'all 0.2s cubic-bezier(0.95, 0.1, 0.45, 0.94)'
} }
}, },
forceSize: {
display: 'inline-block',
textAlign: 'center',
'&:after': {
display: 'block',
content: 'attr(forcesize)',
fontWeight: 700,
height: 0,
overflow: 'hidden',
visibility: 'hidden'
}
},
activeLink: { activeLink: {
extend: tl2,
color: white,
'&::after': { '&::after': {
width: '50%', width: '50%',
marginLeft: '-25%' marginLeft: '-25%'

View file

@ -6,19 +6,25 @@ import styles from './Sidebar.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const Logs = ({ data, displayName, isSelected, onClick, children }) => { const Logs = ({ data, displayName, isSelected, onClick, children, itemRender }) => {
const classes = useStyles() const classes = useStyles()
return ( return (
<div className={classes.sidebar}> <div className={classes.sidebar}>
{data && {data &&
data.map((it, idx) => ( data.map((it, idx) => (
<p <div
key={idx} key={idx}
className={classnames(isSelected(it) ? classes.activeLink : '', classes.link)} className={classnames({
[classes.activeLink]: isSelected(it),
[classes.customRenderActiveLink]: itemRender && isSelected(it),
[classes.customRenderLink]: itemRender,
[classes.link]: true
})}
onClick={() => onClick(it)} onClick={() => onClick(it)}
> >
{displayName(it)} {itemRender ? itemRender(it) : displayName(it)}
</p> </div>
))} ))}
{children} {children}
</div> </div>

View file

@ -1,6 +1,10 @@
import { respondTo } from '../styling/helpers' import { respondTo } from '../styling/helpers'
import { primaryColor, spacer, placeholderColor, zircon, xxl } from '../styling/variables' import { primaryColor, spacer, placeholderColor, zircon, xxl } from '../styling/variables'
import typographyStyles from './typography/styles'
const { tl2 } = typographyStyles
const sidebarColor = zircon const sidebarColor = zircon
export default { export default {
@ -12,7 +16,7 @@ export default {
boxShadow: `-500px 0px 0px 0px ${sidebarColor}`, boxShadow: `-500px 0px 0px 0px ${sidebarColor}`,
borderRadius: '0 20px 0 0', borderRadius: '0 20px 0 0',
alignItems: 'flex-end', alignItems: 'flex-end',
padding: spacer * 2.5, padding: spacer * 3,
flexDirection: 'column', flexDirection: 'column',
[respondTo(xxl)]: { [respondTo(xxl)]: {
width: 'auto', width: 'auto',
@ -23,9 +27,10 @@ export default {
}, },
link: { link: {
extend: tl2,
position: 'relative', position: 'relative',
color: placeholderColor, color: placeholderColor,
marginRight: 24, margin: '12px 24px 12px 0',
cursor: 'pointer', cursor: 'pointer',
'&:hover::after': { '&:hover::after': {
height: '140%' height: '140%'
@ -50,5 +55,18 @@ export default {
'&::after': { '&::after': {
height: '140%' height: '140%'
} }
},
customRenderLink: {
'&:hover::after': {
height: '100%'
},
'&:after': {
bottom: 0
}
},
customRenderActiveLink: {
'&::after': {
height: '100%'
}
} }
} }

View file

@ -9,7 +9,7 @@ import {
import typographyStyles from '../typography/styles' import typographyStyles from '../typography/styles'
const { label } = typographyStyles const { p } = typographyStyles
const colors = (color1, color2, color3) => { const colors = (color1, color2, color3) => {
return { return {
@ -25,7 +25,7 @@ const colors = (color1, color2, color3) => {
export default { export default {
actionButton: { actionButton: {
extend: label, extend: p,
cursor: 'pointer', cursor: 'pointer',
border: 'none', border: 'none',
height: 24, height: 24,

View file

@ -1,6 +1,6 @@
import React, { useState } from 'react' import React, { useState, memo } from 'react'
import { identity } from 'lodash/fp' import { identity, isEmpty } from 'lodash/fp'
import { FastField } from 'formik' import { Form, Formik, FastField, useFormikContext } from 'formik'
import { import {
Td, Td,
@ -13,18 +13,24 @@ const getField = (name, component, props = {}) => (
<FastField name={name} component={component} {...props} /> <FastField name={name} component={component} {...props} />
) )
const ERow = ({ elements, cancel, save, value }) => { const ERow = memo(({ elements }) => {
const { values, submitForm, resetForm, errors } = useFormikContext()
const [editing, setEditing] = useState(false) const [editing, setEditing] = useState(false)
const innerSave = () => {
submitForm()
}
const innerCancel = () => { const innerCancel = () => {
setEditing(false) setEditing(false)
cancel() resetForm()
} }
return ( return (
<Tr> <Tr error={!isEmpty(errors)} errorMessage={errors && errors.toString()}>
{elements.map(({ name, input, size, view = identity, inputProps }, idx) => ( {elements.map(({ name, input, size, textAlign, view = identity, inputProps }, idx) => (
<Td key={idx} size={size}> <Td key={idx} size={size} textAlign={textAlign}>
{editing ? getField(name, input, inputProps) : view(value[name])} {editing ? getField(name, input, inputProps) : view(values[name])}
</Td> </Td>
))} ))}
<Td size={175}> <Td size={175}>
@ -33,7 +39,7 @@ const ERow = ({ elements, cancel, save, value }) => {
<Link style={{ marginRight: '20px' }} color='secondary' onClick={innerCancel}> <Link style={{ marginRight: '20px' }} color='secondary' onClick={innerCancel}>
Cancel Cancel
</Link> </Link>
<Link color='primary' onClick={save}> <Link color='primary' onClick={innerSave}>
Save Save
</Link> </Link>
</> </>
@ -45,6 +51,21 @@ const ERow = ({ elements, cancel, save, value }) => {
</Td> </Td>
</Tr> </Tr>
) )
} })
export default ERow const ERowWithFormik = memo(({ value, validationSchema, save, elements }) => {
return (
<Formik
enableReinitialize
initialValues={value}
validationSchema={validationSchema}
onSubmit={save}
>
<Form>
<ERow elements={elements} />
</Form>
</Formik>
)
})
export default ERowWithFormik

View file

@ -1,4 +1,4 @@
import React from 'react' import React, { memo } from 'react'
import { startCase } from 'lodash/fp' import { startCase } from 'lodash/fp'
import { import {
@ -10,23 +10,20 @@ import {
import ERow from './Row' import ERow from './Row'
const ETable = ({ elements = [], data = [], cancel, save, components = {} }) => { const ETable = memo(({ elements = [], data = [], save, validationSchema }) => {
const { row } = components
const Row = row || ERow
return ( return (
<Table> <Table>
<THead> <THead>
{elements.map(({ name, size, header }, idx) => ( {elements.map(({ name, size, header, textAlign }, idx) => (
<Td header key={idx} size={size}>{header || startCase(name)}</Td> <Td header key={idx} size={size} textAlign={textAlign}>{header || startCase(name)}</Td>
))} ))}
<Td header size={175} /> <Td header size={175} />
</THead> </THead>
<TBody> <TBody>
{data.map((it, idx) => <Row key={idx} value={it} elements={elements} cancel={cancel} save={save} />)} {data.map((it, idx) => <ERow key={idx} value={it} elements={elements} save={save} validationSchema={validationSchema} />)}
</TBody> </TBody>
</Table> </Table>
) )
} })
export default ETable export default ETable

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import ExpansionPanel from '@material-ui/core/ExpansionPanel' import Card from '@material-ui/core/Card'
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary' import CardContent from '@material-ui/core/CardContent'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
@ -10,20 +10,21 @@ import { Link } from '../../components/buttons'
import { import {
tableHeaderColor, tableHeaderColor,
tableHeaderHeight, tableHeaderHeight,
tableErrorColor,
spacer, spacer,
white white
} from '../../styling/variables' } from '../../styling/variables'
import typographyStyles from '../typography/styles' import typographyStyles from '../typography/styles'
const { label2, p } = typographyStyles const { tl2, p } = typographyStyles
const useStyles = makeStyles({ const useStyles = makeStyles({
body: { body: {
borderSpacing: '0 4px' borderSpacing: '0 4px'
}, },
header: { header: {
extend: label2, extend: tl2,
backgroundColor: tableHeaderColor, backgroundColor: tableHeaderColor,
height: tableHeaderHeight, height: tableHeaderHeight,
textAlign: 'left', textAlign: 'left',
@ -39,39 +40,31 @@ const useStyles = makeStyles({
display: 'table-cell', display: 'table-cell',
padding: `0 ${spacer * 3}px` padding: `0 ${spacer * 3}px`
}, },
summary: { trError: {
cursor: 'auto' backgroundColor: tableErrorColor
},
mainContent: {
display: 'flex',
alignItems: 'center',
minHeight: 54
}, },
// mui-overrides // mui-overrides
panelRoot: { cardContentRoot: {
// display: 'flex',
margin: 0,
padding: 0,
'&:last-child': {
padding: 0
}
},
card: {
extend: p, extend: p,
display: 'table-row',
'&:before': { '&:before': {
height: 0 height: 0
}, },
margin: '4px 0', margin: '4px 0',
width: 'min-content',
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)' boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)'
},
panelExpanded: {
'&:first-child': {
marginTop: '4px !important'
}
},
summaryRoot: {
userSelect: 'auto',
'&:hover:not(.Mui-disabled)': {
cursor: 'auto'
},
cursor: 'auto',
padding: 0
},
summaryContent: {
margin: 0,
height: 54,
alignItems: 'center'
},
summaryFocused: {
backgroundColor: 'inherit !important'
} }
}) })
@ -99,7 +92,7 @@ const TBody = ({ children, className }) => {
) )
} }
const Td = ({ children, header, className, size = 100 }) => { const Td = ({ children, header, className, size = 100, textAlign }) => {
const classes = useStyles() const classes = useStyles()
const classNames = { const classNames = {
[classes.td]: true, [classes.td]: true,
@ -107,23 +100,28 @@ const Td = ({ children, header, className, size = 100 }) => {
} }
return ( return (
<div className={classnames(className, classNames)} style={{ width: size }}> <div className={classnames(className, classNames)} style={{ width: size, textAlign }}>
{children} {children}
</div> </div>
) )
} }
const Tr = ({ children }) => { const Tr = ({ error, errorMessage, children }) => {
const classes = useStyles() const classes = useStyles()
const epClasses = { root: classes.panelRoot, expanded: classes.panelExpanded } const cardClasses = { root: classes.cardContentRoot }
const summaryClasses = { root: classes.summaryRoot, content: classes.summaryContent, focused: classes.summaryFocused } const classNames = {
[classes.trError]: error
}
return ( return (
<ExpansionPanel expanded={false} classes={epClasses} square> <>
<ExpansionPanelSummary tabIndex={null} classes={summaryClasses} className={classes.summary}> <Card className={classnames(classNames, classes.card)}>
{children} <CardContent classes={cardClasses}>
</ExpansionPanelSummary> <div className={classes.mainContent}>{children}</div>
</ExpansionPanel> {error && <div className={classes.errorContent}>{errorMessage}</div>}
</CardContent>
</Card>
</>
) )
} }

View file

@ -35,13 +35,15 @@ const Autocomplete = memo(({ suggestions, classes, placeholder, label, itemToStr
}) => ( }) => (
<div className={classes.container}> <div className={classes.container}>
{renderInput({ {renderInput({
id: name,
fullWidth: true, fullWidth: true,
classes, classes,
onBlur, error: (touched[`${name}-input`] || touched[name]) && errors[name],
error: touched[name] && errors[name], success: (touched[`${name}-input`] || touched[name] || value) && !errors[name],
InputProps: getInputProps({ InputProps: getInputProps({
value: inputValue2 || '', value: inputValue2 || '',
placeholder, placeholder,
onBlur,
onClick: () => toggleMenu(), onClick: () => toggleMenu(),
onChange: it => { onChange: it => {
if (it.target.value === '') { if (it.target.value === '') {

View file

@ -60,10 +60,11 @@ const AutocompleteMultiple = memo(
}) => ( }) => (
<div className={classes.container}> <div className={classes.container}>
{renderInput({ {renderInput({
id: name,
fullWidth: true, fullWidth: true,
classes, classes,
onBlur, error: (touched[`${name}-input`] || touched[name]) && errors[name],
error: touched[name] && errors[name], success: (touched[`${name}-input`] || touched[name] || value) && !errors[name],
InputProps: getInputProps({ InputProps: getInputProps({
startAdornment: (value || []).map(item => ( startAdornment: (value || []).map(item => (
<Chip <Chip
@ -72,6 +73,7 @@ const AutocompleteMultiple = memo(
label={item.code} label={item.code}
/> />
)), )),
onBlur,
onChange: it => setInputValue(it.target.value), onChange: it => setInputValue(it.target.value),
onClick: () => toggleMenu(), onClick: () => toggleMenu(),
onKeyDown: handleKeyDown, onKeyDown: handleKeyDown,

View file

@ -6,7 +6,7 @@ import MenuItem from '@material-ui/core/MenuItem'
import { fontColor, inputFontSize, inputFontWeight } from '../../../styling/variables' import { fontColor, inputFontSize, inputFontWeight } from '../../../styling/variables'
function renderInput (inputProps) { function renderInput (inputProps) {
const { InputProps, classes, ref, ...other } = inputProps const { onBlur, success, InputProps, classes, ref, ...other } = inputProps
return ( return (
<TextField <TextField
@ -14,7 +14,8 @@ function renderInput (inputProps) {
inputRef: ref, inputRef: ref,
classes: { classes: {
root: classes.inputRoot, root: classes.inputRoot,
input: classes.inputInput input: classes.inputInput,
underline: success ? classes.success : ''
}, },
...InputProps ...InputProps
}} }}
@ -88,6 +89,11 @@ const styles = theme => ({
inputInput: { inputInput: {
flex: 1 flex: 1
}, },
success: {
'&:after': {
transform: 'scaleX(1)'
}
},
divider: { divider: {
height: theme.spacing(2) height: theme.spacing(2)
} }

View file

@ -25,22 +25,25 @@ function H4 ({ children, className, ...props }) {
return <h4 className={classnames(classes.h3, className)} {...props}>{children}</h4> return <h4 className={classnames(classes.h3, className)} {...props}>{children}</h4>
} }
const P = pBuilder() const P = pBuilder('p')
const Info1 = pBuilder('info1') const Info1 = pBuilder('info1')
const Info2 = pBuilder('info2') const Info2 = pBuilder('info2')
const Info3 = pBuilder('info3') const Info3 = pBuilder('info3')
const Info4 = pBuilder('info4')
const Mono = pBuilder('mono') const Mono = pBuilder('mono')
const TL1 = pBuilder('tl1') const TL1 = pBuilder('tl1')
const TL2 = pBuilder('tl2') const TL2 = pBuilder('tl2')
const Label1 = pBuilder('label1')
const Label2 = pBuilder('label2')
const Label3 = pBuilder('label3')
function pBuilder (elementClass) { function pBuilder (elementClass) {
return ({ inline, className, children, ...props }) => { return ({ inline, noMargin, className, children, ...props }) => {
const classes = useStyles() const classes = useStyles()
const classNames = { const classNames = {
[classes[elementClass]]: elementClass, [classes[elementClass]]: elementClass,
className: true, className: true,
[classes.inline]: inline [classes.inline]: inline,
[classes.noMarginP]: noMargin
} }
return ( return (
<p className={classnames(classNames, className)} {...props}> <p className={classnames(classNames, className)} {...props}>
@ -50,4 +53,4 @@ function pBuilder (elementClass) {
} }
} }
export { H1, H2, H3, H4, TL1, TL2, P, Info1, Info2, Info3, Info4, Mono } export { H1, H2, H3, H4, TL1, TL2, P, Info1, Info2, Info3, Mono, Label1, Label2, Label3 }

View file

@ -70,6 +70,12 @@ export default {
fontFamily: fontSecondary, fontFamily: fontSecondary,
fontWeight: 700 fontWeight: 700
}, },
info3: {
extend: base,
fontSize: fontSize3,
fontFamily: fontSecondary,
fontWeight: 500
},
mono: { mono: {
extend: base, extend: base,
fontSize: fontSize4, fontSize: fontSize4,
@ -95,7 +101,7 @@ export default {
fontWeight: 500, fontWeight: 500,
lineHeight: '110%' lineHeight: '110%'
}, },
label: { label1: {
fontSize: fontSize5, fontSize: fontSize5,
fontFamily: fontSecondary, fontFamily: fontSecondary,
fontWeight: 500 fontWeight: 500
@ -105,6 +111,12 @@ export default {
fontFamily: fontSecondary, fontFamily: fontSecondary,
fontWeight: 700 fontWeight: 700
}, },
label3: {
fontSize: fontSize4,
fontFamily: fontSecondary,
fontWeight: 500,
color: fontColor
},
select: { select: {
fontSize: fontSize3, fontSize: fontSize3,
fontFamily: fontSecondary, fontFamily: fontSecondary,
@ -112,5 +124,8 @@ export default {
}, },
inline: { inline: {
display: 'inline' display: 'inline'
},
noMarginP: {
margin: 0
} }
} }

View file

@ -5,7 +5,7 @@ import BigNumber from 'bignumber.js'
import useAxios from '@use-hooks/axios' import useAxios from '@use-hooks/axios'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { H3, Info1, Info3, Info4, Mono } from '../components/typography' import { H3, Info1, Info2, Info3, Mono, Label1, Label3, TL2 } from '../components/typography'
import Title from '../components/Title' import Title from '../components/Title'
import Sidebar from '../components/Sidebar' import Sidebar from '../components/Sidebar'
import { primaryColor } from '../styling/variables' import { primaryColor } from '../styling/variables'
@ -58,6 +58,16 @@ const Funding = () => {
} }
}) })
const itemRender = (it) => {
return (
<div className={classes.itemWrapper}>
<div className={classes.firstItem}>{it.display}</div>
<div className={classes.item}>{it.fiatConfirmedBalance} {it.fiatCode}</div>
<div className={classes.item}>{it.confirmedBalance} {it.cryptoCode}</div>
</div>
)
}
return ( return (
<> <>
<Title>Funding</Title> <Title>Funding</Title>
@ -67,14 +77,15 @@ const Funding = () => {
isSelected={isSelected} isSelected={isSelected}
onClick={setSelected} onClick={setSelected}
displayName={it => it.display} displayName={it => it.display}
itemRender={itemRender}
> >
{data && data.length && ( {data && data.length && (
<div className={classes.total}> <div className={classes.total}>
<Info3 className={classes.totalTitle}>Total Crypto Balance</Info3> <Label1 className={classes.totalTitle}>Total Crypto Balance</Label1>
<Info1 className={classes.noMargin}> <Info1 className={classes.noMargin}>
{getConfirmedTotal(data)} {data[0].fiatCode} {getConfirmedTotal(data)} {data[0].fiatCode}
</Info1> </Info1>
<Info4>(+{getPendingTotal(data)} pending)</Info4> <Label1 className={classes.totalPending}>(+{getPendingTotal(data)} pending)</Label1>
</div> </div>
)} )}
</Sidebar> </Sidebar>
@ -83,21 +94,21 @@ const Funding = () => {
<div className={classes.firstSide}> <div className={classes.firstSide}>
<H3>Balance ({selected.display})</H3> <H3>Balance ({selected.display})</H3>
<div className={classes.coinTotal}> <div className={classes.coinTotal}>
<span className='info1'> <Info1 inline noMargin>
{`${selected.confirmedBalance} ${selected.cryptoCode}`} {`${selected.confirmedBalance} ${selected.cryptoCode}`}
</span> </Info1>
<span className={classnames('tl2', classes.leftSpacer)}> <Info2 inline noMargin className={classes.leftSpacer}>
{` (+ ${selected.pending} pending)`} {`(+ ${selected.pending} pending)`}
</span> </Info2>
</div> </div>
<div className={classes.coinTotal}> <div className={classes.coinTotal}>
<span className={classnames('info3', classes.noMarginTop)}> <Info3 inline noMargin>
{`= ${formatNumber(selected.fiatConfirmedBalance)} ${selected.fiatCode}`} {`= ${formatNumber(selected.fiatConfirmedBalance)} ${selected.fiatCode}`}
</span> </Info3>
<span className={classnames('info4', classes.leftSpacer)}> <Label3 inline noMargin className={classes.leftSpacer}>
{`(+${formatNumber(selected.fiatPending)} pending)`} {`(+${formatNumber(selected.fiatPending)} pending)`}
</span> </Label3>
</div> </div>
<H3 className={classes.topSpacer}>Address</H3> <H3 className={classes.topSpacer}>Address</H3>
@ -109,7 +120,7 @@ const Funding = () => {
</div> </div>
<div className={classes.secondSide}> <div className={classes.secondSide}>
<Info3>Scan to send {selected.display}</Info3> <Label1>Scan to send {selected.display}</Label1>
<QRCode size={240} fgColor={primaryColor} value={selected.fundingAddressUrl} /> <QRCode size={240} fgColor={primaryColor} value={selected.fundingAddressUrl} />
</div> </div>
</div> </div>

View file

@ -1,4 +1,8 @@
import { spacer, subheaderColor, placeholderColor } from '../styling/variables' import { spacer, subheaderColor, placeholderColor, fontColor } from '../styling/variables'
import typographyStyles from '../components/typography/styles'
const { label1 } = typographyStyles
export default { export default {
wrapper: { wrapper: {
@ -38,12 +42,27 @@ export default {
width: 375, width: 375,
margin: `${spacer * 1.5}px ${spacer * 3}px` margin: `${spacer * 1.5}px ${spacer * 3}px`
}, },
itemWrapper: {
textAlign: 'end'
},
item: {
extend: label1,
margin: 2
},
firstItem: {
margin: 2
},
total: { total: {
marginTop: 'auto', marginTop: 'auto',
textAlign: 'right', textAlign: 'right',
marginRight: 20 marginRight: 24
},
totalPending: {
color: fontColor,
marginTop: 2
}, },
totalTitle: { totalTitle: {
color: placeholderColor color: placeholderColor,
marginBottom: 2
} }
} }

View file

@ -1,5 +1,4 @@
import React, { memo, useState } from 'react' import React, { memo, useState } from 'react'
import { withFormik } from 'formik'
import * as Yup from 'yup' import * as Yup from 'yup'
import useAxios from '@use-hooks/axios' import useAxios from '@use-hooks/axios'
@ -24,7 +23,6 @@ const initialValues = {
const Locales = memo(() => { const Locales = memo(() => {
const [locale, setLocale] = useState(initialValues) const [locale, setLocale] = useState(initialValues)
const [editing, setEditing] = useState(false)
const [data, setData] = useState(null) const [data, setData] = useState(null)
useAxios({ useAxios({
@ -48,21 +46,16 @@ const Locales = memo(() => {
} }
}) })
const MainFormFormik = withFormik({ const save = (it) => {
mapPropsToValues: () => locale, setLocale(it)
validationSchema: LocaleSchema, reFetch()
handleSubmit: it => { }
setEditing(false)
setLocale(it)
reFetch()
}
})(MainForm)
return ( return (
<> <>
<Title>Locales</Title> <Title>Locales</Title>
<Subtitle>Default settings</Subtitle> <Subtitle>Default settings</Subtitle>
<MainFormFormik editing={editing} setEditing={setEditing} data={data} /> <MainForm validationSchema={LocaleSchema} value={locale} save={save} auxData={data} />
<Subtitle extraMarginTop>Overrides</Subtitle> <Subtitle extraMarginTop>Overrides</Subtitle>
</> </>
) )

View file

@ -1,181 +1,70 @@
import React, { useState, memo } from 'react' import React, { memo } from 'react'
import { Form, FastField } from 'formik' import { __, compose, join, map, get } from 'lodash/fp'
import { __, compose, join, map, get, isEmpty } from 'lodash/fp'
import { Link } from '../../components/buttons'
import { Autocomplete, AutocompleteMultiple } from '../../components/inputs' import { Autocomplete, AutocompleteMultiple } from '../../components/inputs'
import { Checkbox } from '../../components/inputs/formik' import { Checkbox } from '../../components/inputs/formik'
import {
EditCell,
Table,
TableHead,
TableRow,
TableHeader,
TableBody,
TableCell as Td
} from '../../components/table'
// import styles from './MainForm.module.scss' // import styles from './MainForm.module.scss'
import { Table as EditableTable } from '../../components/editableTable' import { Table as EditableTable } from '../../components/editableTable'
const styles = {}
const sizes = { const sizes = {
country: 200, country: 200,
fiatCurrency: 150, fiatCurrency: 150,
languages: 240, languages: 240,
cryptoCurrencies: 280, cryptoCurrencies: 270,
showRates: 125, showRates: 125,
action: 175 action: 175
} }
const MainForm = memo( const MainForm = memo(
({ values, resetForm, submitForm, errors, editing, setEditing, data }) => { ({ value, save, auxData, validationSchema }) => {
const [submitted, setSubmitted] = useState(false) const getData = get(__, auxData)
const getData = get(__, data)
const displayCodeArray = compose(join(', '), map(get('code'))) const displayCodeArray = compose(join(', '), map(get('code')))
const save = () => {
setSubmitted(true)
submitForm()
}
const cancel = () => {
resetForm()
setSubmitted(false)
setEditing(false)
}
const ViewFields = (
<>
<Td>{values.country && values.country.display}</Td>
<Td>{values.fiatCurrency && values.fiatCurrency.code}</Td>
<Td>
{values.languages && values.languages.map(it => it.code).join(', ')}
</Td>
<Td>
{values.cryptoCurrencies &&
values.cryptoCurrencies.map(it => it.code).join(', ')}
</Td>
<Td>{values.showRates ? 'true' : 'false'}</Td>
<Td rightAlign>
<Link color='primary' onClick={() => setEditing(true)}>
Edit
</Link>
</Td>
</>
)
const EditFields = (
<>
<Td>
<FastField
name='country'
component={Autocomplete}
suggestions={data && data.countries}
/>
</Td>
<Td>
<FastField
name='fiatCurrency'
component={Autocomplete}
suggestions={data && data.currencies}
/>
</Td>
<Td>
<FastField
name='languages'
component={AutocompleteMultiple}
suggestions={data && data.languages}
/>
</Td>
<Td>
<FastField
name='cryptoCurrencies'
component={AutocompleteMultiple}
suggestions={data && data.cryptoCurrencies}
/>
</Td>
<Td>
<FastField name='showRates' component={Checkbox} />
</Td>
<EditCell save={save} cancel={cancel} />
</>
)
return ( return (
<Form> <EditableTable
<Table> save={save}
<colgroup> validationSchema={validationSchema}
<col className={styles.bigColumn} /> data={[value]}
<col className={styles.mediumColumn} /> elements={[
<col className={styles.mediumColumn} /> {
<col className={styles.bigColumn} /> name: 'country',
<col className={styles.showRates} /> size: sizes.country,
<col className={styles.actionColumn} /> view: get('display'),
</colgroup> input: Autocomplete,
<TableHead> inputProps: { suggestions: getData('countries') }
<TableRow header> },
<TableHeader>Country</TableHeader> {
<TableHeader>Fiat Currency</TableHeader> name: 'fiatCurrency',
<TableHeader>Languages</TableHeader> size: sizes.fiatCurrency,
<TableHeader>Crypto Currencies</TableHeader> view: get('code'),
<TableHeader>Show Rates</TableHeader> input: Autocomplete,
<TableHeader /> inputProps: { suggestions: getData('currencies') }
</TableRow> },
</TableHead> {
<TableBody> name: 'languages',
<TableRow error={submitted && !isEmpty(errors)}> size: sizes.languages,
{editing ? EditFields : ViewFields} view: displayCodeArray,
</TableRow> input: AutocompleteMultiple,
</TableBody> inputProps: { suggestions: getData('languages') }
</Table> },
{
<EditableTable name: 'cryptoCurrencies',
cancel={cancel} size: sizes.cryptoCurrencies,
save={save} view: displayCodeArray,
data={[values]} input: AutocompleteMultiple,
elements={[ inputProps: { suggestions: getData('cryptoCurrencies') }
{ },
name: 'country', {
size: sizes.country, name: 'showRates',
view: get('display'), size: sizes.showRates,
input: Autocomplete, textAlign: 'center',
inputProps: { suggestions: getData('countries') } view: it => it ? 'true' : 'false',
}, input: Checkbox
{ }
name: 'fiatCurrency', ]}
size: sizes.fiatCurrency, />
view: get('code'),
input: Autocomplete,
inputProps: { suggestions: getData('currencies') }
},
{
name: 'languages',
size: sizes.languages,
input: AutocompleteMultiple,
view: displayCodeArray,
inputProps: { suggestions: getData('languages') }
},
{
name: 'cryptoCurrencies',
size: sizes.languages,
input: AutocompleteMultiple,
view: displayCodeArray,
inputProps: { suggestions: getData('cryptoCurrencies') }
},
{
name: 'showRates',
size: sizes.showRates,
input: Checkbox,
view: it => it ? 'true' : 'false'
}
]}
/>
</Form>
) )
} }
) )

View file

@ -1,4 +1,4 @@
const version = 8 const version = 9
// Primary // Primary
const zodiac = '#1b2559' const zodiac = '#1b2559'
@ -8,8 +8,8 @@ const spring = '#48f694'
const comet = '#5f668a' const comet = '#5f668a'
const comet2 = '#72799d' const comet2 = '#72799d'
const spring2 = '#44e188' const spring2 = '#44e188'
const spring4 = '#3fd07e'
const spring3 = '#ecfbef' const spring3 = '#ecfbef'
const spring4 = '#3fd07e'
const zircon = '#ebefff' const zircon = '#ebefff'
const zircon2 = '#dbdfed' const zircon2 = '#dbdfed'
@ -75,7 +75,7 @@ if (version === 8) {
fontSize5 = 14 fontSize5 = 14
} }
const smallestFontSize = fontSize5 - 1 const smallestFontSize = fontSize5
const inputFontSize = fontSize4 const inputFontSize = fontSize4
const inputFontSizeLg = fontSize1 const inputFontSizeLg = fontSize1
const inputFontWeight = 500 const inputFontWeight = 500