diff --git a/frontend/webpack/base.js b/frontend/webpack/base.js index a1d0df01..4fe01b16 100644 --- a/frontend/webpack/base.js +++ b/frontend/webpack/base.js @@ -22,7 +22,7 @@ const plugins = { }), 'define-plugin': new webpack.DefinePlugin({ 'process.env': { - NODE_ENV: JSON.stringify(process.env['NODE_ENV'] || 'development'), + NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'), APP_NAME: JSON.stringify(pkg.name), APP_VERSION: JSON.stringify(pkg.version) } diff --git a/ui/docs/root.js b/ui/docs/root.js index 700a7282..cf432b94 100644 --- a/ui/docs/root.js +++ b/ui/docs/root.js @@ -1,6 +1,6 @@ const React = require('react'); const ReactHotLoader = require('react-hot-loader'); -const Button = require('../src/components/grid/readme.md'); +const Button = require('../src/components/column/readme.md'); const InnerHTML = require('dangerously-set-inner-html'); const { diff --git a/ui/package.json b/ui/package.json index 837a4195..09469aed 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "classnames": "^2.2.5", + "lodash.flatten": "^4.4.0", "react": "^15.3.2" }, "devDependencies": { @@ -42,6 +43,7 @@ "nyc": "^8.3.1", "postcss-cssnext": "^2.8.0", "postcss-loader": "^1.0.0", + "postcss-mixins": "^5.4.0", "postcss-modules-values": "^1.2.2", "pre-commit": "^1.1.3", "raw-loader": "^0.5.1", diff --git a/ui/src/components/column/index.js b/ui/src/components/column/index.js new file mode 100644 index 00000000..d3bd8623 --- /dev/null +++ b/ui/src/components/column/index.js @@ -0,0 +1,62 @@ +/* + * based on + * https://github.com/roylee0704/react-flexbox-grid/blob/master/src/components/Col.js + */ + +const flatten = require('lodash.flatten'); +const classNames = require('classnames'); +const React = require('react'); +const styles = require('./style.css'); + +const breakpoints = [ + 'xs', + 'sm', + 'md', + 'lg' +]; + +const getClasses = (props) => { + return flatten(breakpoints.map((size) => { + const number = props[size]; + const offset = props[`${size}Offset`]; + + return [ + number ? styles[`${size}-${number}`] : '', + offset ? styles[`${size}-offset-${offset}`] : '' + ]; + })).filter(Boolean); +}; + +const Column = module.exports = (props) => { + const { + className, + reverse, + children, + style + } = props; + + const cn = classNames( + className, + styles.column, + reverse ? styles.reverse : '', + ...getClasses(props) + ); + + return ( +
+ {children} +
+ ); +}; + +Column.propTypes = { + reverse: React.PropTypes.bool, + className: React.PropTypes.string, + style: React.PropTypes.object, + children: React.PropTypes.node, + ...breakpoints.reduce((all, bp) => ({ + ...all, + [`${bp}Offset`]: React.PropTypes.number, + [bp]: React.PropTypes.number + }), {}) +}; diff --git a/ui/src/components/column/readme.md b/ui/src/components/column/readme.md new file mode 100644 index 00000000..73363d69 --- /dev/null +++ b/ui/src/components/column/readme.md @@ -0,0 +1,64 @@ +# `` + +## demo + +```embed +const React = require('react'); +const ReactDOM = require('react-dom/server'); +const Grid = require('../grid'); +const Row = require('../row'); +const Column = require('./index'); + +const styles = { + grid: { + backgroundColor: '#FFEBEE' + }, + row: { + backgroundColor: '#EF5350' + }, + column: { + backgroundColor: '#B71C1C', + textAlign: 'center' + }, + p: { + color: 'white' + } +}; + +nmodule.exports = ReactDOM.renderToString( + + + +

1

+
+ +

2

+
+ +

3

+
+
+
+); +``` + +## usage + +```js +const React = require('react'); +const Grid = require('ui/grid'); +const Row = require('ui/row'); +const Column = require('ui/index'); + +module.exports = () => { + return ( + + + 1 + 2 + 3 + + + ); +}; +``` \ No newline at end of file diff --git a/ui/src/components/column/style.css b/ui/src/components/column/style.css new file mode 100644 index 00000000..5afc2a98 --- /dev/null +++ b/ui/src/components/column/style.css @@ -0,0 +1,152 @@ +/* + * based on + * https://github.com/kristoferjoseph/flexboxgrid/blob/master/dist/flexboxgrid.css + */ + +@value half-gutter-width from "../../constants/sizes.css"; +@value sm-viewport, md-viewport, lg-viewport from "../../constants/breakpoints.css"; + +:root { + --half-gutter-width: half-gutter-width; +} + +@define-mixin viewport $size { + &.$(size) { + flex-grow: 1; + flex-basis: 0; + max-width: 100%; + } + + &.$(size)-1 { + flex-basis: 8.33333333%; + max-width: 8.33333333%; + } + + &.$(size)-2 { + flex-basis: 16.66666667%; + max-width: 16.66666667%; + } + + &.$(size)-3 { + flex-basis: 25%; + max-width: 25%; + } + + &.$(size)-4 { + flex-basis: 33.33333333%; + max-width: 33.33333333%; + } + + &.$(size)-5 { + flex-basis: 41.66666667%; + max-width: 41.66666667%; + } + + &.$(size)-6 { + flex-basis: 50%; + max-width: 50%; + } + + &.$(size)-7 { + flex-basis: 58.33333333%; + max-width: 58.33333333%; + } + + &.$(size)-8 { + flex-basis: 66.66666667%; + max-width: 66.66666667%; + } + + &.$(size)-9 { + flex-basis: 75%; + max-width: 75%; + } + + &.$(size)-10 { + flex-basis: 83.33333333%; + max-width: 83.33333333%; + } + + &.$(size)-11 { + flex-basis: 91.66666667%; + max-width: 91.66666667%; + } + + &.$(size)-12 { + flex-basis: 100%; + max-width: 100%; + } + + &.$(size)-offset-0 { + margin-left: 0; + } + + &.$(size)-offset-1 { + margin-left: 8.33333333%; + } + + &.$(size)-offset-2 { + margin-left: 16.66666667%; + } + + &.$(size)-offset-3 { + margin-left: 25%; + } + + &.$(size)-offset-4 { + margin-left: 33.33333333%; + } + + &.$(size)-offset-5 { + margin-left: 41.66666667%; + } + + &.$(size)-offset-6 { + margin-left: 50%; + } + + &.$(size)-offset-7 { + margin-left: 58.33333333%; + } + + &.$(size)-offset-8 { + margin-left: 66.66666667%; + } + + &.$(size)-offset-9 { + margin-left: 75%; + } + + &.$(size)-offset-10 { + margin-left: 83.33333333%; + } + + &.$(size)-offset-11 { + margin-left: 91.66666667%; + } +} + +.column { + box-sizing: border-box; + flex: 0 0 auto; + padding-right: var(--half-gutter-width, 0.5rem); + padding-left: var(--half-gutter-width, 0.5rem); + + &.reverse { + flex-direction: column-reverse; + } + + @mixin viewport xs; + + @media sm-viewport { + @mixin viewport sm; + } + + @media md-viewport { + @mixin viewport md; + } + + @media lg-viewport { + @mixin viewport lg; + } +} diff --git a/ui/src/components/grid/index.js b/ui/src/components/grid/index.js index d2b0e98c..11a35b8b 100644 --- a/ui/src/components/grid/index.js +++ b/ui/src/components/grid/index.js @@ -1,16 +1,25 @@ -// based on https://github.com/roylee0704/react-flexbox-grid/blob/master/src/components/Grid.js +/* + * based on + * https://github.com/roylee0704/react-flexbox-grid/blob/master/src/components/Grid.js + */ const React = require('react'); -const style = require('./style.css'); +const classNames = require('classnames'); +const styles = require('./style.css'); const Grid = module.exports = ({ fluid = false, - children + className, + children, + style }) => { - const className = style[fluid ? 'container-fluid' : 'container']; + const cn = classNames( + className, + styles[fluid ? 'container-fluid' : 'container'] + ); return ( -
+
{children}
); @@ -18,5 +27,7 @@ const Grid = module.exports = ({ Grid.propTypes = { fluid: React.PropTypes.bool, + className: React.PropTypes.string, + style: React.PropTypes.object, children: React.PropTypes.node -} +}; diff --git a/ui/src/components/grid/style.css b/ui/src/components/grid/style.css index d84c891d..7c9a26b1 100644 --- a/ui/src/components/grid/style.css +++ b/ui/src/components/grid/style.css @@ -21,20 +21,16 @@ padding-left: var(--outer-margin); } -@media sm-viewport { - .container { +.container { + @media sm-viewport { width: var(--container-sm, 46rem); } -} -@media md-viewport { - .container { + @media md-viewport { width: var(--container-md, 61rem); } -} -@media lg-viewport { - .container { + @media lg-viewport { width: var(--container-lg, 71rem); } } diff --git a/ui/src/components/row/index.js b/ui/src/components/row/index.js new file mode 100644 index 00000000..0a11669a --- /dev/null +++ b/ui/src/components/row/index.js @@ -0,0 +1,92 @@ +/* + * based on + * https://github.com/roylee0704/react-flexbox-grid/blob/master/src/components/Row.js + */ + +const flatten = require('lodash.flatten'); +const classNames = require('classnames'); +const React = require('react'); +const styles = require('./style.css'); + +const breakpoints = [ + 'xs', + 'sm', + 'md', + 'lg' +]; + +const modifiers = [ + 'start', + 'center', + 'end', + 'top', + 'middle', + 'bottom', + 'around', + 'between', + 'first', + 'last' +]; + +const getClasses = (props) => { + return flatten(modifiers.map((name) => { + const value = props[name]; + + if (!value) { + return; + } + + const bps = (() => { + if (value === true) { + return breakpoints + } + + if (Array.isArray(value)) { + return value; + } + + return [value]; + })(); + + return flatten(bps.map(bp => styles[`${name}-${bp}`])); + })).filter(Boolean); +}; + +const Row = module.exports = (props) => { + const { + className, + reverse, + children, + style + } = props; + + const cn = classNames( + className, + styles.row, + reverse ? styles.reverse : '', + ...getClasses(props) + ); + + return ( +
+ {children} +
+ ); +}; + +const ModificatorType = React.PropTypes.oneOfType([ + React.PropTypes.bool, + React.PropTypes.arrayOf(React.PropTypes.oneOf(breakpoints)), + React.PropTypes.oneOf(breakpoints) +]); + +Row.propTypes = { + reverse: React.PropTypes.bool, + className: React.PropTypes.string, + style: React.PropTypes.object, + children: React.PropTypes.node, + ...modifiers.reduce((all, m) => ({ + ...all, + [m]: ModificatorType + }), {}) +}; diff --git a/ui/src/components/row/readme.md b/ui/src/components/row/readme.md new file mode 100644 index 00000000..50c7ad08 --- /dev/null +++ b/ui/src/components/row/readme.md @@ -0,0 +1,22 @@ +# `` + +## demo + +```embed +const React = require('react'); +const ReactDOM = require('react-dom/server'); +const Row = require('./index.js'); +const Grid = require('../grid'); +const Button = require('../button'); + +nmodule.exports = ReactDOM.renderToString( + + + + + + +); +``` + +## usage diff --git a/ui/src/components/row/style.css b/ui/src/components/row/style.css new file mode 100644 index 00000000..35e7cdbd --- /dev/null +++ b/ui/src/components/row/style.css @@ -0,0 +1,85 @@ +/* + * based on + * https://github.com/kristoferjoseph/flexboxgrid/blob/master/dist/flexboxgrid.css + */ + +@value gutter-compensation from "../../constants/sizes.css"; +@value sm-viewport, md-viewport, lg-viewport from "../../constants/breakpoints.css"; + +:root { + --outer-margin: outer-margin; + --gutter-compensation: gutter-compensation; +} + +@define-mixin viewport $size { + &.start-$(size) { + justify-content: flex-start; + text-align: start; + } + + &.center-$(size) { + justify-content: center; + text-align: center; + } + + &.end-$(size) { + justify-content: flex-end; + text-align: end; + } + + &.top-$(size) { + align-items: flex-start; + } + + &.middle-$(size) { + align-items: center; + } + + &.bottom-$(size) { + align-items: flex-end; + } + + &.around-$(size) { + justify-content: space-around; + } + + &.between-$(size) { + justify-content: space-between; + } + + &.first-$(size) { + order: -1; + } + + &.last-$(size) { + order: 1; + } +} + +.row { + box-sizing: border-box; + display: flex; + flex: 0 1 auto; + flex-direction: row; + flex-wrap: wrap; + margin-right: var(--gutter-compensation, -0.5rem); + margin-left: var(--gutter-compensation, -0.5rem); + + &.reverse { + flex-direction: row-reverse; + } + + @mixin viewport xs; + + @media sm-viewport { + @mixin viewport sm; + } + + @media md-viewport { + @mixin viewport md; + } + + @media lg-viewport { + @mixin viewport lg; + } +} diff --git a/ui/webpack/base.js b/ui/webpack/base.js index 2d3299ff..0586093f 100644 --- a/ui/webpack/base.js +++ b/ui/webpack/base.js @@ -13,6 +13,7 @@ const plugins = { postcss: { plugins: [ require('postcss-modules-values'), + require('postcss-mixins')(), require('postcss-cssnext')() ] }, diff --git a/ui/yarn.lock b/ui/yarn.lock index 4b83c123..b8b55c2d 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1272,6 +1272,10 @@ callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" +camelcase-css@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-1.0.1.tgz#157c4238265f5cf94a1dffde86446552cbf3f705" + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -2671,6 +2675,16 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +globby@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.0.0.tgz#8f5710eda32296ac53f011a97dccc70e936685dc" + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + got@^5.0.0: version "5.6.0" resolved "https://registry.yarnpkg.com/got/-/got-5.6.0.tgz#bb1d7ee163b78082bbc8eb836f3f395004ea6fbf" @@ -3437,7 +3451,7 @@ lodash.filter@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" -lodash.flatten@^4.2.0, lodash.flatten@^4.4.0: +lodash.flatten, lodash.flatten@^4.2.0, lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" @@ -4363,6 +4377,13 @@ postcss-initial@^1.3.1: lodash.template "^4.2.4" postcss "^5.0.19" +postcss-js@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-0.1.3.tgz#8c0a5daa1c918b3073c4806a3c5b332c67250c03" + dependencies: + camelcase-css "^1.0.1" + postcss "^5.0.21" + postcss-load-config@^1.0.0-rc: version "1.0.0-rc" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.0.0-rc.tgz#8aed0d0fb94afe2c1ab0ba2ca69da3af5079e2cc" @@ -4458,6 +4479,16 @@ postcss-minify-selectors@^2.0.4: postcss "^5.0.14" postcss-selector-parser "^2.0.0" +postcss-mixins: + version "5.4.0" + resolved "https://registry.yarnpkg.com/postcss-mixins/-/postcss-mixins-5.4.0.tgz#68b4705d1f2c505921f4a2f55bcf72a03b56fc01" + dependencies: + globby "^6.0.0" + postcss "^5.2.4" + postcss-js "^0.1.3" + postcss-simple-vars "^3.0.0" + sugarss "^0.2.0" + postcss-modules-extract-imports@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341" @@ -4582,6 +4613,12 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.0: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-simple-vars@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-3.0.0.tgz#1fa4ccb4b7151d9f0d52fb8ea19a15c1319599d6" + dependencies: + postcss "^5.0.21" + postcss-svgo@^2.1.1: version "2.1.5" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.5.tgz#46fc0363f01bab6a36a9abb01c229fcc45363094" @@ -5456,6 +5493,12 @@ style-loader@^0.13.1: dependencies: loader-utils "^0.2.7" +sugarss@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-0.2.0.tgz#ac34237563327c6ff897b64742bf6aec190ad39e" + dependencies: + postcss "^5.2.4" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"