diff --git a/.eslintrc b/.eslintrc index 60da420..475c1cd 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,8 @@ { "parser": "babel-eslint", + "extends": ["prettier"], + "parserOptions": { "ecmaVersion": 6, "sourceType": "module" @@ -51,6 +53,7 @@ }, "rules": { + "prettier/prettier": "error", "react/jsx-uses-react": 2, "react/jsx-uses-vars": 2, "react/react-in-jsx-scope": 2, @@ -176,14 +179,14 @@ "no-extra-parens": [0], "one-var": [0], "operator-assignment": [0, "always"], - "operator-linebreak": [2, "before"], + "operator-linebreak": [0], "padded-blocks": [0], "quote-props": [0], "radix": [0], "semi": [2], "semi-spacing": [2, {"before": false, "after": true}], "sort-vars": [0], - "space-before-function-paren": [2, {"anonymous": "always", "named": "always"}], + "space-before-function-paren": [0], "space-before-blocks": [0, "always"], "space-in-brackets": [ 0, "never", { diff --git a/package-lock.json b/package-lock.json index cb29799..b37bdbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3644,6 +3644,15 @@ } } }, + "eslint-config-prettier": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.9.0.tgz", + "integrity": "sha512-ag8YEyBXsm3nmOv1Hz991VtNNDMRa+MNy8cY47Pl4bw6iuzqKbJajXdqUpiw13STdLLrznxgm1hj9NhxeOYq0A==", + "dev": true, + "requires": { + "get-stdin": "5.0.1" + } + }, "eslint-plugin-prettier": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz", @@ -5942,6 +5951,12 @@ "resolved": "https://registry.npmjs.org/get-params/-/get-params-0.1.2.tgz", "integrity": "sha1-uuDfq6WIoMYNeDTA2Nwv9g7u8v4=" }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", diff --git a/package.json b/package.json index 0d0010a..3fc6708 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "develop": "gatsby develop", "build": "gatsby build", "serve": "gatsby serve", - "format": "prettier --trailing-comma es5 --single-quote --write \"src/**/*.js\"", + "format": "prettier --write \"src/**/*.js\"", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { @@ -22,11 +22,13 @@ "polished": "^1.9.2", "react-helmet": "^5.2.0", "react-ripples": "^1.1.2", - "react-simple-dropdown": "^3.2.0" + "react-simple-dropdown": "^3.2.0", + "react-textarea-autosize": "^5.2.1" }, "devDependencies": { "babel-eslint": "^8.2.1", "eslint": "^4.18.0", + "eslint-config-prettier": "^2.9.0", "eslint-plugin-prettier": "^2.6.0", "eslint-plugin-react": "^7.6.1", "prettier": "^1.10.2", diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..b231cbe --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,7 @@ +module.exports = { + parser: 'babylon', + printWidth: 80, + semi: true, + singleQuote: true, + bracketSpacing: true +}; diff --git a/src/components/common/Footer/index.js b/src/components/common/Footer/index.js index 79cc67a..cf9da87 100644 --- a/src/components/common/Footer/index.js +++ b/src/components/common/Footer/index.js @@ -11,12 +11,9 @@ const Footer = props => { const { theme, sheet, classes, className, ...etc } = props; return ( diff --git a/src/components/common/Footer/styles.js b/src/components/common/Footer/styles.js index 713edfb..eff4d95 100644 --- a/src/components/common/Footer/styles.js +++ b/src/components/common/Footer/styles.js @@ -1,9 +1,9 @@ export default () => ({ root: { - padding: 20, + padding: 20 }, logo: { width: 100, - height: 100, - }, + height: 100 + } }); diff --git a/src/components/common/Header/index.js b/src/components/common/Header/index.js index 1f33b61..2c8198a 100644 --- a/src/components/common/Header/index.js +++ b/src/components/common/Header/index.js @@ -14,13 +14,13 @@ const Header = props => { const { theme, sheet, classes, className, reversed, ...etc } = props; return (
-
-
- +
+
+ Real World React
@@ -37,7 +37,7 @@ Header.propTypes = { /** * Set theme with reversed colors. */ - reversed: PropTypes.bool, + reversed: PropTypes.bool }; export default withStyles(styles)(Header); diff --git a/src/components/common/Header/styles.js b/src/components/common/Header/styles.js index 86feb75..168ed38 100644 --- a/src/components/common/Header/styles.js +++ b/src/components/common/Header/styles.js @@ -1,26 +1,26 @@ export default () => ({ root: { - padding: 20, + padding: 20 }, logo: { display: 'inline-block', - height: 60, + height: 60 }, menu: { display: 'flex', - textAlign: 'right', + textAlign: 'right' }, desktop: { - display: 'none', + display: 'none' }, // small + '@media screen and (min-width: 48em)': { mobile: { - display: 'none', + display: 'none' }, desktop: { - display: 'flex', - }, - }, + display: 'flex' + } + } }); diff --git a/src/components/forms/Button/index.js b/src/components/forms/Button/index.js index 457273d..bdfbe3c 100644 --- a/src/components/forms/Button/index.js +++ b/src/components/forms/Button/index.js @@ -8,12 +8,11 @@ import cx from 'classnames'; import styles from './styles'; class Button extends React.Component { - - constructor () { + constructor() { super(...arguments); } - render () { + render() { const { theme, sheet, @@ -37,10 +36,10 @@ class Button extends React.Component { ); } - onClick = (ev) => { + onClick = ev => { ev.preventDefault(); if (this.props.href) navigateTo(this.props.href); - } + }; } Button.propTypes = { @@ -57,7 +56,14 @@ Button.propTypes = { /** * Color palette for styles. */ - palette: PropTypes.oneOf(['primary', 'secondary', 'inverted', 'text', 'alert', 'black']), + palette: PropTypes.oneOf([ + 'primary', + 'secondary', + 'inverted', + 'text', + 'alert', + 'black' + ]), /** * Button would be outlined. @@ -72,11 +78,11 @@ Button.propTypes = { /** * Button content. */ - children: PropTypes.any.isRequired, + children: PropTypes.any.isRequired }; Button.defaultProps = { - palette: 'primary', + palette: 'primary' }; export default withStyles(styles)(Button); diff --git a/src/components/forms/Button/styles.js b/src/components/forms/Button/styles.js index f4ab670..b981c0d 100644 --- a/src/components/forms/Button/styles.js +++ b/src/components/forms/Button/styles.js @@ -1,12 +1,12 @@ -export default (theme) => ({ +export default theme => ({ root: { - display: props => props.fullWidth ? 'block' : 'inline-block', + display: props => (props.fullWidth ? 'block' : 'inline-block'), overflow: 'hidden', - verticalAlign: 'middle', + verticalAlign: 'middle' }, ripples: { width: '100%', - height: '100%', + height: '100%' }, button: { display: 'inline-block', @@ -25,20 +25,21 @@ export default (theme) => ({ transition: 'all 200ms ease-out', color: props => theme.palette[props.palette].contrastText, - border: props => '2px solid ' - + theme.palette[props.palette][props.outline ? 'contrastText' : 'base'], - backgroundColor: props => props.outline - ? 'transparent' - : theme.palette[props.palette].base, + border: props => + '2px solid ' + + theme.palette[props.palette][props.outline ? 'contrastText' : 'base'], + backgroundColor: props => + props.outline ? 'transparent' : theme.palette[props.palette].base, '&:hover': { color: props => props.outline && theme.palette[props.palette].light, borderColor: props => theme.palette[props.palette].light, - backgroundColor: props => !props.outline && theme.palette[props.palette].light, + backgroundColor: props => + !props.outline && theme.palette[props.palette].light }, '&::-moz-focus-inner': { - border: 'none', - }, - }, + border: 'none' + } + } }); diff --git a/src/components/forms/TextField/index.js b/src/components/forms/TextField/index.js new file mode 100644 index 0000000..ec8779c --- /dev/null +++ b/src/components/forms/TextField/index.js @@ -0,0 +1,96 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import withStyles from 'react-jss'; +import TextareaAutoize from 'react-textarea-autosize'; +import cx from 'classnames'; + +import styles from './styles'; + +const TextField = props => { + const { + theme, + sheet, + classes, + className, + palette, + reversed, + isTextarea, + type, + value, + onChange, + placeholder, + errorText, + fieldProps, + ...etc + } = props; + const cls = cx(classes.root, className); + const node = isTextarea ? TextareaAutoize : 'input'; + return ( +
+ {React.createElement(node, { + type: node === 'input' ? type : null, + value, + onChange, + placeholder, + ...fieldProps, + className: cx(classes.field, fieldProps.className), + })} + {!!errorText &&
{errorText}
} +
+ ); +}; + +TextField.propTypes = { + /** + * Color palette. + */ + palette: PropTypes.oneOf(['primary', 'secondary']), + + /** + * If colors should be reversed. + */ + reversed: PropTypes.bool, + + /** + * If the field is a textarea. + */ + isTextarea: PropTypes.bool, + + /** + * If node 'input', its type. + */ + type: PropTypes.oneOf(['text', 'number', 'email']), + + /** + * Field value when controlled. + */ + value: PropTypes.string, + + /** + * Field onchange event handler. + */ + onChange: PropTypes.func, + + /** + * Field placeholder text. + */ + placeholder: PropTypes.string, + + /** + * When the field has an error, show this text below it. + */ + errorText: PropTypes.string, + + /** + * Extra fields to pass to the encapsulated field element. + */ + fieldProps: PropTypes.object, +}; + +TextField.defaultProps = { + type: 'text', + palette: 'primary', + fieldProps: {}, +}; + +export default withStyles(styles)(TextField); diff --git a/src/components/forms/TextField/styles.js b/src/components/forms/TextField/styles.js new file mode 100644 index 0000000..c7c0e83 --- /dev/null +++ b/src/components/forms/TextField/styles.js @@ -0,0 +1,73 @@ +export default (theme) => { + const getColor = props => props.reversed ? '#fff' : theme.palette.inverted.base; + return { + root: { + display: 'block', + }, + field: { + display: 'block', + margin: 0, + padding: [4, 12], + width: '100%', + outline: 'none', + background: 'transparent', + lineHeight: '1', + fontSize: 16, + fontWeight: 'bold', + resize: 'none', + transition: 'border-color 200ms ease-out', + + minHeight: props => props.isTextarea ? 16 * 3 : 'auto', + border: props => '2px solid ' + getColor(props), + color: props => getColor(props), + + '&:focus': { + borderColor: props => theme.palette[props.palette].base, + }, + + // Remove browser/os appareance + '-webkit-appearance': 'none', + '-moz-appearance': 'none', + appearance: 'none', + + // Placeholder color + '&::-webkit-input-placeholder': { + opacity: 1, + color: props => getColor(props), + }, + '&::-moz-placeholder': { + opacity: 1, + color: props => getColor(props), + }, + '&:-ms-input-placeholder': { + opacity: 1, + color: props => getColor(props), + }, + '&:-moz-placeholder': { + opacity: 1, + color: props => getColor(props), + }, + + // Autofill background color + '&:-webkit-autofill': { + '-webkit-box-shadow': '0 0 0 rgba(0, 0, 0, 0.001)', + }, + + // Input number arrows + '&::-webkit-inner-spin-button, &::-webkit-outer-spin-button': { + '-webkit-appearance': 'none', + margin: 0, + }, + '&[type="number"]': { + '-moz-appearance': 'textfield', + }, + }, + errorText: { + padding: [4, 0, 0, 14], + lineHeight: 1.2, + fontFamily: theme.typography.secondary, + fontStyle: 'italic', + color: theme.palette.alert.base, + }, + }; +}; diff --git a/src/components/forms/index.js b/src/components/forms/index.js index 75b7ff0..b0cbeeb 100644 --- a/src/components/forms/index.js +++ b/src/components/forms/index.js @@ -1 +1,2 @@ export Button from './Button'; +export TextField from './TextField'; diff --git a/src/components/navigation/Dropdown/index.js b/src/components/navigation/Dropdown/index.js index 1eba081..78cbb17 100644 --- a/src/components/navigation/Dropdown/index.js +++ b/src/components/navigation/Dropdown/index.js @@ -3,7 +3,10 @@ import PropTypes from 'prop-types'; import Link from 'gatsby-link'; import withStyles from 'react-jss'; import cx from 'classnames'; -import SimpleDropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; +import SimpleDropdown, { + DropdownTrigger, + DropdownContent +} from 'react-simple-dropdown'; import styles from './styles'; @@ -30,9 +33,9 @@ Dropdown.propTypes = { items: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string, - route: PropTypes.string, + route: PropTypes.string }) - ).isRequired, + ).isRequired }; export default withStyles(styles)(Dropdown); diff --git a/src/components/navigation/Dropdown/styles.js b/src/components/navigation/Dropdown/styles.js index 30b18d3..be093c6 100644 --- a/src/components/navigation/Dropdown/styles.js +++ b/src/components/navigation/Dropdown/styles.js @@ -1,18 +1,18 @@ export default theme => ({ '@global': { '.dropdown': { - display: 'inline-block', + display: 'inline-block' }, '.dropdown__content': { display: 'none', - position: 'absolute', + position: 'absolute' }, '.dropdown--active .dropdown__content': { - display: 'block', - }, + display: 'block' + } }, root: { - userSelect: 'none', + userSelect: 'none' }, content: { marginTop: 10, @@ -20,7 +20,7 @@ export default theme => ({ minWidth: 180, boxShadow: '0 0 6px #eaeaea', backgroundColor: '#fff', - animation: 'dropdown-show 200ms ease-out', + animation: 'dropdown-show 200ms ease-out' }, item: { display: 'block', @@ -32,23 +32,23 @@ export default theme => ({ transition: 'all 200ms ease-out', '& + &': { - marginTop: 10, + marginTop: 10 }, '&:hover': { borderColor: theme.palette.primary.base, color: '#fff', - backgroundColor: theme.palette.primary.base, - }, + backgroundColor: theme.palette.primary.base + } }, '@keyframes dropdown-show': { '0%': { marginTop: -10, - opacity: 0, + opacity: 0 }, '100%': { marginTop: 10, - opacity: 1, - }, - }, + opacity: 1 + } + } }); diff --git a/src/components/navigation/MenuDesktop/index.js b/src/components/navigation/MenuDesktop/index.js index d87f39f..65e9e0c 100644 --- a/src/components/navigation/MenuDesktop/index.js +++ b/src/components/navigation/MenuDesktop/index.js @@ -13,22 +13,26 @@ const MenuDesktop = props => { const { theme, sheet, classes, className, reversed, ...etc } = props; return (
- Home + + Home + {navigation.map((nav, index) => ( {nav.name} - )} + trigger={ + + {nav.name} + + } items={nav.items} /> ))} + {/* Navigations */} {navigation.map((nav, index) => (
- _{nav.name} + _ + {nav.name}
{/* List of links */} @@ -63,7 +65,8 @@ class MenuMobile extends React.Component { this.setState({ active }); // Add/remove body class to remove scrolls according to the state. - const baseClass = document.querySelector('body').getAttribute('class') || ''; + const baseClass = + document.querySelector('body').getAttribute('class') || ''; let updateClass = active ? `${baseClass} ${MENU_MOBILE_BODY_ACTIVE}` : baseClass.replace(MENU_MOBILE_BODY_ACTIVE, '').trim(); @@ -73,14 +76,14 @@ class MenuMobile extends React.Component { const body = document.body; const html = document.documentElement; body.scrollTop = html.scrollTop = 0; - } + }; } MenuMobile.propTypes = { /** * Set theme with reversed colors. */ - reversed: PropTypes.bool, + reversed: PropTypes.bool }; export default withStyles(styles)(MenuMobile); diff --git a/src/components/navigation/MenuMobile/styles.js b/src/components/navigation/MenuMobile/styles.js index 73b2499..6d23be5 100644 --- a/src/components/navigation/MenuMobile/styles.js +++ b/src/components/navigation/MenuMobile/styles.js @@ -9,21 +9,21 @@ export const MENU_MOBILE_BODY_ACTIVE = 'menu-mobile-body-active'; export default theme => ({ '@global': { ['.' + MENU_MOBILE_BODY_ACTIVE]: { - overflow: 'hidden', - }, + overflow: 'hidden' + } }, root: { display: 'flex', flex: '1', alignItems: 'center', - justifyContent: 'flex-end', + justifyContent: 'flex-end' }, trigger: { position: 'relative', padding: 2, width: 36, height: 36, - cursor: 'pointer', + cursor: 'pointer' }, triggerLine: { position: 'relative', @@ -32,7 +32,8 @@ export default theme => ({ display: 'block', top: 0, transition: 'all 500ms ease-out', - backgroundColor: props => props.reversed ? '#fff' : theme.palette.inverted.base, + backgroundColor: props => + props.reversed ? '#fff' : theme.palette.inverted.base, '$active &': { '&:first-child': { @@ -46,7 +47,7 @@ export default theme => ({ '&:nth-child(2)': { opacity: 0 } - }, + } }, // By default, the menu is hidden with height 0. On shown, it is fixated // on the page with the header top height. @@ -59,8 +60,8 @@ export default theme => ({ right: 0, overflow: 'hidden', '$active &': { - bottom: 0, - }, + bottom: 0 + } }, menu: { overflowY: 'auto', @@ -69,25 +70,26 @@ export default theme => ({ height: 0, opacity: 0, textAlign: 'left', - backgroundColor: props => props.reversed ? theme.palette.inverted.base : '#fff', + backgroundColor: props => + props.reversed ? theme.palette.inverted.base : '#fff', backgroundImage: props => props.reversed && `url(${imagePatternDots})`, transition: 'all 500ms ease-out', '$active &': { height: '100%', - opacity: 1, - }, + opacity: 1 + } }, navTitle: { margin: [20, 0], fontSize: 20, fontWeight: 'bold', - color: props => props.reversed ? '#fff' : theme.palette.inverted.base, + color: props => (props.reversed ? '#fff' : theme.palette.inverted.base) }, navItem: { - marginBottom: 10, + marginBottom: 10 }, // When menu is being shown. - active: {}, + active: {} }); diff --git a/src/layouts/ThemedStyles.js b/src/layouts/ThemedStyles.js index b3975e2..139b40c 100644 --- a/src/layouts/ThemedStyles.js +++ b/src/layouts/ThemedStyles.js @@ -1,35 +1,52 @@ import React from 'react'; import withStyles from 'react-jss'; -const styles = (theme) => ({ +const styles = theme => ({ '@global': { - 'body': { + body: { color: theme.palette.text.base, - fontFamily: theme.typography.primary, + fontFamily: theme.typography.primary }, - 'a': { + a: { color: theme.palette.primary.base, transition: 'color 200ms ease-out', '&:hover': { - color: theme.palette.primary.light, + color: theme.palette.primary.light } }, + + // Global selection '::selection': { backgroundColor: theme.palette.primary.base, - color: theme.palette.primary.contrastText, + color: theme.palette.primary.contrastText }, '::-moz-selection': { backgroundColor: theme.palette.primary.base, - color: theme.palette.primary.contrastText, + color: theme.palette.primary.contrastText }, + + // Text '.text-primary': { - color: theme.palette.primary.base, + color: theme.palette.primary.base + }, + '.text-secondary': { + color: theme.palette.secondary.base + }, + '.text-inverted': { + color: theme.palette.inverted.base + }, + '.background-primary': { + backgroundColor: theme.palette.primary.base + }, + '.background-secondary': { + backgroundColor: theme.palette.secondary.base }, - }, + '.background-inverted': { + backgroundColor: theme.palette.inverted.base + } + } }); -const ThemedStyles = ({ children }) => ( -
{children}
-); +const ThemedStyles = ({ children }) =>
{children}
; export default withStyles(styles)(ThemedStyles); diff --git a/src/layouts/createTheme.js b/src/layouts/createTheme.js index 9608e7f..30f74cb 100644 --- a/src/layouts/createTheme.js +++ b/src/layouts/createTheme.js @@ -4,33 +4,33 @@ export default () => ({ palette: { primary: { base: '#36a7ee', - dark: darken(0.10, '#36a7ee'), - light: lighten(0.10, '#36a7ee'), - contrastText: '#fff', + dark: darken(0.1, '#36a7ee'), + light: lighten(0.1, '#36a7ee'), + contrastText: '#fff' }, secondary: { base: '#fffd41', - contrastText: '#242424', + contrastText: '#242424' }, text: { base: '#2b3642', - contrastText: '#fff', + contrastText: '#fff' }, alert: { base: '#e85c3b', - contrastText: '#fff', + contrastText: '#fff' }, inverted: { base: '#242424', - contrastText: '#fff', + contrastText: '#fff' }, black: { base: '#fff', - contrastText: '#242424', - }, + contrastText: '#242424' + } }, typography: { primary: '"GT Walsheim", sans-serif', - secondary: '"Georgia", "Neuton", sans-serif', - }, + secondary: '"Georgia", "Neuton", sans-serif' + } }); diff --git a/src/layouts/index.js b/src/layouts/index.js index b114c10..1d6aa2b 100644 --- a/src/layouts/index.js +++ b/src/layouts/index.js @@ -15,20 +15,24 @@ import ThemedStyles from './ThemedStyles'; const TemplateWrapper = ({ children }) => (
- - - + + + - - {children()} - + {children()}
); TemplateWrapper.propTypes = { - children: PropTypes.func, + children: PropTypes.func }; export default TemplateWrapper; diff --git a/src/pages/index.js b/src/pages/index.js index c72034f..c4238c1 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -48,10 +48,6 @@ const styles = (theme) => ({ textAlign: 'center', color: '#fff', }, - ink: { - backgroundColor: theme.palette.secondary.base, - color: theme.palette.inverted.base, - }, // MAIN main: { @@ -101,7 +97,7 @@ const HomePage = ({ classes }) => (

We are a professional services and consulting firm specializing - in  modern front-end tools  and + in  modern front-end tools  and serverless architecture.

Fig. 1 - Our fields of speciality
diff --git a/src/pages/talks/index.js b/src/pages/talks/index.js index 0097c0e..02b59d3 100644 --- a/src/pages/talks/index.js +++ b/src/pages/talks/index.js @@ -9,7 +9,6 @@ const TalksPage = () => ( Talks | Real World React
Talks
-
);