diff options
Diffstat (limited to 'packages')
45 files changed, 1965 insertions, 24 deletions
diff --git a/packages/dev-tools-pages/README.md b/packages/dev-tools-pages/README.md new file mode 100644 index 000000000..39fe70a25 --- /dev/null +++ b/packages/dev-tools-pages/README.md @@ -0,0 +1,88 @@ +## Dev tools pages + +This repository contains our dev tools pages. + +## Local Dev Setup + +Requires Node version 6.9.5 or higher & yarn v1.9.4 + +### 1. Install dependencies for monorepo: + +Make sure you install Yarn v1.9.4 (npm won't work!). We rely on our `yarn.lock` file and on Yarn's support for `workspaces` in our monorepo setup. + +```bash +yarn install +``` + +### 2. Initial setup + +To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: + +```bash +PKG=@0xproject/dev-tools-pages yarn build +``` + +Note: Ignore the `WARNING in asset size limit` and `WARNING in entrypoint size limit` warnings. + +### 3. Run dev server + +```bash +cd packages/dev-tools-pages +yarn dev +``` + +Visit [http://localhost:3572/](http://localhost:3572/) in your browser. + +The webpage will refresh when source code is changed. + +### 4. Code! + +There are some basic primitives we'd like you to use: + +1. `<Container>Stuff</Container>`: Use containers instead of divs,spans,etc... and use it's props instead of inline styles (e.g `style={{margin: 3}}` should be `margin="3px"` + +2. `<Text>Look ma, text!</Text>`: Use text components whenever rendering text. It has props for manipulating texts, so again no in-line styles. Use `fontColor="red"`, not `style={{color: 'red'}}`. + +3. Styled-components: See the `ui/button.tsx` file for an example of how to use these. + +4. BassCss: This library gives you access to a bunch of [classes](http://basscss.com/) that apply styles in a browser-compatible way, has affordances for responsiveness and alleviates the need for inline styles or LESS/CSS files. + +With the above 4 tools and following the React paradigm, you shouldn't need CSS/LESS files. IF there are special occasions where you do, these is a `all.less` file, but this is a solution of last resort. Use it sparingly. + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + +### Prettier + +Run from the monorepo root directory: + +``` +yarn prettier +``` + +### Resources + +##### Toolkit + +* [Styled Components](https://www.styled-components.com/) +* [BassCSS](http://basscss.com/) + +##### Recommended Atom packages: + +* [atom-typescript](https://atom.io/packages/atom-typescript) +* [linter-tslint](https://atom.io/packages/linter-tslint) + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. diff --git a/packages/dev-tools-pages/less/all.less b/packages/dev-tools-pages/less/all.less new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/packages/dev-tools-pages/less/all.less diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json new file mode 100644 index 000000000..f73260544 --- /dev/null +++ b/packages/dev-tools-pages/package.json @@ -0,0 +1,58 @@ +{ + "name": "@0xproject/dev-tools-pages", + "version": "0.0.1", + "engines": { + "node": ">=6.12" + }, + "private": true, + "description": "0x Dev tools pages", + "scripts": { + "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production", + "build:ci": "yarn build", + "build:dev": "../../node_modules/.bin/webpack --mode development", + "clean": "shx rm -f public/bundle*", + "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", + "dev": "webpack-dev-server --mode development --content-base public" + }, + "license": "Apache-2.0", + "dependencies": { + "@0xproject/react-shared": "^1.0.15", + "basscss": "^8.0.3", + "bowser": "^1.9.3", + "less": "^2.7.2", + "lodash": "^4.17.5", + "polished": "^1.9.2", + "react": "^16.4.2", + "react-document-title": "^2.0.3", + "react-dom": "^16.4.2", + "react-helmet": "^5.2.0", + "styled-components": "^3.3.0" + }, + "devDependencies": { + "@types/lodash": "4.14.104", + "@types/node": "*", + "@types/react": "^16.4.2", + "@types/react-dom": "^16.0.7", + "@types/react-helmet": "^5.0.6", + "@types/react-router-dom": "^4.0.4", + "@types/react-tap-event-plugin": "0.0.30", + "@types/styled-components": "^4.0.0", + "awesome-typescript-loader": "^5.2.1", + "copyfiles": "^2.0.0", + "css-loader": "0.23.x", + "less-loader": "^4.1.0", + "make-promises-safe": "^1.1.0", + "raw-loader": "^0.5.1", + "shx": "^0.2.2", + "source-map-loader": "^0.2.4", + "style-loader": "0.23.x", + "terser-webpack-plugin": "^1.1.0", + "tslint": "5.11.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "3.0.1", + "uglifyjs-webpack-plugin": "^2.0.1", + "webpack": "^4.20.2", + "webpack-cli": "3.1.2", + "webpack-dev-server": "^3.1.9" + } +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_custom.css b/packages/dev-tools-pages/public/css/basscss_responsive_custom.css new file mode 100644 index 000000000..5f8bd9117 --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_custom.css @@ -0,0 +1,85 @@ +/* Custom Basscss Responsive Utilities */ + +@media (max-width: 52em) { + .sm-center { + text-align: center; + } + .sm-align-middle { + vertical-align: middle; + } + .sm-align-top { + vertical-align: top; + } + .sm-left-align { + text-align: left; + } + .sm-right-align { + text-align: right; + } + .sm-table-cell { + display: table-cell; + } + .sm-mx-auto { + margin-left: auto; + margin-right: auto; + } + .sm-right { + float: right; + } +} + +@media (min-width: 52em) { + .md-center { + text-align: center; + } + .md-align-middle { + vertical-align: middle; + } + .md-align-top { + vertical-align: top; + } + .md-left-align { + text-align: left; + } + .md-right-align { + text-align: right; + } + .md-table-cell { + display: table-cell; + } + .md-mx-auto { + margin-left: auto; + margin-right: auto; + } + .md-right { + float: right; + } +} + +@media (min-width: 64em) { + .lg-center { + text-align: center; + } + .lg-align-middle { + vertical-align: middle; + } + .lg-align-top { + vertical-align: top; + } + .lg-left-align { + text-align: left; + } + .lg-right-align { + text-align: right; + } + .lg-table-cell { + display: table-cell; + } + .lg-mx-auto { + margin-left: auto; + margin-right: auto; + } + .lg-right { + float: right; + } +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_margin.css b/packages/dev-tools-pages/public/css/basscss_responsive_margin.css new file mode 100644 index 000000000..c9f3e855c --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_margin.css @@ -0,0 +1,453 @@ +/* Basscss Responsive Margin */ + +@media (max-width: 52em) { + /* Modified by Fabio Berger to max-width from min-width */ + + .sm-m0 { + margin: 0; + } + .sm-mt0 { + margin-top: 0; + } + .sm-mr0 { + margin-right: 0; + } + .sm-mb0 { + margin-bottom: 0; + } + .sm-ml0 { + margin-left: 0; + } + .sm-mx0 { + margin-left: 0; + margin-right: 0; + } + .sm-my0 { + margin-top: 0; + margin-bottom: 0; + } + + .sm-m1 { + margin: 0.5rem; + } + .sm-mt1 { + margin-top: 0.5rem; + } + .sm-mr1 { + margin-right: 0.5rem; + } + .sm-mb1 { + margin-bottom: 0.5rem; + } + .sm-ml1 { + margin-left: 0.5rem; + } + .sm-mx1 { + margin-left: 0.5rem; + margin-right: 0.5rem; + } + .sm-my1 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .sm-m2 { + margin: 1rem; + } + .sm-mt2 { + margin-top: 1rem; + } + .sm-mr2 { + margin-right: 1rem; + } + .sm-mb2 { + margin-bottom: 1rem; + } + .sm-ml2 { + margin-left: 1rem; + } + .sm-mx2 { + margin-left: 1rem; + margin-right: 1rem; + } + .sm-my2 { + margin-top: 1rem; + margin-bottom: 1rem; + } + + .sm-m3 { + margin: 2rem; + } + .sm-mt3 { + margin-top: 2rem; + } + .sm-mr3 { + margin-right: 2rem; + } + .sm-mb3 { + margin-bottom: 2rem; + } + .sm-ml3 { + margin-left: 2rem; + } + .sm-mx3 { + margin-left: 2rem; + margin-right: 2rem; + } + .sm-my3 { + margin-top: 2rem; + margin-bottom: 2rem; + } + + .sm-m4 { + margin: 4rem; + } + .sm-mt4 { + margin-top: 4rem; + } + .sm-mr4 { + margin-right: 4rem; + } + .sm-mb4 { + margin-bottom: 4rem; + } + .sm-ml4 { + margin-left: 4rem; + } + .sm-mx4 { + margin-left: 4rem; + margin-right: 4rem; + } + .sm-my4 { + margin-top: 4rem; + margin-bottom: 4rem; + } + + .sm-mxn1 { + margin-left: -0.5rem; + margin-right: -0.5rem; + } + .sm-mxn2 { + margin-left: -1rem; + margin-right: -1rem; + } + .sm-mxn3 { + margin-left: -2rem; + margin-right: -2rem; + } + .sm-mxn4 { + margin-left: -4rem; + margin-right: -4rem; + } + + .sm-ml-auto { + margin-left: auto; + } + .sm-mr-auto { + margin-right: auto; + } + .sm-mx-auto { + margin-left: auto; + margin-right: auto; + } +} + +@media (min-width: 52em) { + .md-m0 { + margin: 0; + } + .md-mt0 { + margin-top: 0; + } + .md-mr0 { + margin-right: 0; + } + .md-mb0 { + margin-bottom: 0; + } + .md-ml0 { + margin-left: 0; + } + .md-mx0 { + margin-left: 0; + margin-right: 0; + } + .md-my0 { + margin-top: 0; + margin-bottom: 0; + } + + .md-m1 { + margin: 0.5rem; + } + .md-mt1 { + margin-top: 0.5rem; + } + .md-mr1 { + margin-right: 0.5rem; + } + .md-mb1 { + margin-bottom: 0.5rem; + } + .md-ml1 { + margin-left: 0.5rem; + } + .md-mx1 { + margin-left: 0.5rem; + margin-right: 0.5rem; + } + .md-my1 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .md-m2 { + margin: 1rem; + } + .md-mt2 { + margin-top: 1rem; + } + .md-mr2 { + margin-right: 1rem; + } + .md-mb2 { + margin-bottom: 1rem; + } + .md-ml2 { + margin-left: 1rem; + } + .md-mx2 { + margin-left: 1rem; + margin-right: 1rem; + } + .md-my2 { + margin-top: 1rem; + margin-bottom: 1rem; + } + + .md-m3 { + margin: 2rem; + } + .md-mt3 { + margin-top: 2rem; + } + .md-mr3 { + margin-right: 2rem; + } + .md-mb3 { + margin-bottom: 2rem; + } + .md-ml3 { + margin-left: 2rem; + } + .md-mx3 { + margin-left: 2rem; + margin-right: 2rem; + } + .md-my3 { + margin-top: 2rem; + margin-bottom: 2rem; + } + + .md-m4 { + margin: 4rem; + } + .md-mt4 { + margin-top: 4rem; + } + .md-mr4 { + margin-right: 4rem; + } + .md-mb4 { + margin-bottom: 4rem; + } + .md-ml4 { + margin-left: 4rem; + } + .md-mx4 { + margin-left: 4rem; + margin-right: 4rem; + } + .md-my4 { + margin-top: 4rem; + margin-bottom: 4rem; + } + + .md-mxn1 { + margin-left: -0.5rem; + margin-right: -0.5rem; + } + .md-mxn2 { + margin-left: -1rem; + margin-right: -1rem; + } + .md-mxn3 { + margin-left: -2rem; + margin-right: -2rem; + } + .md-mxn4 { + margin-left: -4rem; + margin-right: -4rem; + } + + .md-ml-auto { + margin-left: auto; + } + .md-mr-auto { + margin-right: auto; + } + .md-mx-auto { + margin-left: auto; + margin-right: auto; + } +} + +@media (min-width: 64em) { + .lg-m0 { + margin: 0; + } + .lg-mt0 { + margin-top: 0; + } + .lg-mr0 { + margin-right: 0; + } + .lg-mb0 { + margin-bottom: 0; + } + .lg-ml0 { + margin-left: 0; + } + .lg-mx0 { + margin-left: 0; + margin-right: 0; + } + .lg-my0 { + margin-top: 0; + margin-bottom: 0; + } + + .lg-m1 { + margin: 0.5rem; + } + .lg-mt1 { + margin-top: 0.5rem; + } + .lg-mr1 { + margin-right: 0.5rem; + } + .lg-mb1 { + margin-bottom: 0.5rem; + } + .lg-ml1 { + margin-left: 0.5rem; + } + .lg-mx1 { + margin-left: 0.5rem; + margin-right: 0.5rem; + } + .lg-my1 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .lg-m2 { + margin: 1rem; + } + .lg-mt2 { + margin-top: 1rem; + } + .lg-mr2 { + margin-right: 1rem; + } + .lg-mb2 { + margin-bottom: 1rem; + } + .lg-ml2 { + margin-left: 1rem; + } + .lg-mx2 { + margin-left: 1rem; + margin-right: 1rem; + } + .lg-my2 { + margin-top: 1rem; + margin-bottom: 1rem; + } + + .lg-m3 { + margin: 2rem; + } + .lg-mt3 { + margin-top: 2rem; + } + .lg-mr3 { + margin-right: 2rem; + } + .lg-mb3 { + margin-bottom: 2rem; + } + .lg-ml3 { + margin-left: 2rem; + } + .lg-mx3 { + margin-left: 2rem; + margin-right: 2rem; + } + .lg-my3 { + margin-top: 2rem; + margin-bottom: 2rem; + } + + .lg-m4 { + margin: 4rem; + } + .lg-mt4 { + margin-top: 4rem; + } + .lg-mr4 { + margin-right: 4rem; + } + .lg-mb4 { + margin-bottom: 4rem; + } + .lg-ml4 { + margin-left: 4rem; + } + .lg-mx4 { + margin-left: 4rem; + margin-right: 4rem; + } + .lg-my4 { + margin-top: 4rem; + margin-bottom: 4rem; + } + + .lg-mxn1 { + margin-left: -0.5rem; + margin-right: -0.5rem; + } + .lg-mxn2 { + margin-left: -1rem; + margin-right: -1rem; + } + .lg-mxn3 { + margin-left: -2rem; + margin-right: -2rem; + } + .lg-mxn4 { + margin-left: -4rem; + margin-right: -4rem; + } + + .lg-ml-auto { + margin-left: auto; + } + .lg-mr-auto { + margin-right: auto; + } + .lg-mx-auto { + margin-left: auto; + margin-right: auto; + } +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_padding.css b/packages/dev-tools-pages/public/css/basscss_responsive_padding.css new file mode 100644 index 000000000..e027c2d65 --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_padding.css @@ -0,0 +1,134 @@ +/* Basscss Responsive Padding */ +/* Modified by Fabio Berger to include xs prefix */ + +@media (max-width: 52em) { /* Modified by Fabio Berger to max-width from min-width */ + + .sm-p0 { padding: 0 } + .sm-pt0 { padding-top: 0 } + .sm-pr0 { padding-right: 0 } + .sm-pb0 { padding-bottom: 0 } + .sm-pl0 { padding-left: 0 } + .sm-px0 { padding-left: 0; padding-right: 0 } + .sm-py0 { padding-top: 0; padding-bottom: 0 } + + .sm-p1 { padding: .5rem } + .sm-pt1 { padding-top: .5rem } + .sm-pr1 { padding-right: .5rem } + .sm-pb1 { padding-bottom: .5rem } + .sm-pl1 { padding-left: .5rem } + .sm-px1 { padding-left: .5rem; padding-right: .5rem } + .sm-py1 { padding-top: .5rem; padding-bottom: .5rem } + + .sm-p2 { padding: 1rem } + .sm-pt2 { padding-top: 1rem } + .sm-pr2 { padding-right: 1rem } + .sm-pb2 { padding-bottom: 1rem } + .sm-pl2 { padding-left: 1rem } + .sm-px2 { padding-left: 1rem; padding-right: 1rem } + .sm-py2 { padding-top: 1rem; padding-bottom: 1rem } + + .sm-p3 { padding: 2rem } + .sm-pt3 { padding-top: 2rem } + .sm-pr3 { padding-right: 2rem } + .sm-pb3 { padding-bottom: 2rem } + .sm-pl3 { padding-left: 2rem } + .sm-px3 { padding-left: 2rem; padding-right: 2rem } + .sm-py3 { padding-top: 2rem; padding-bottom: 2rem } + + .sm-p4 { padding: 4rem } + .sm-pt4 { padding-top: 4rem } + .sm-pr4 { padding-right: 4rem } + .sm-pb4 { padding-bottom: 4rem } + .sm-pl4 { padding-left: 4rem } + .sm-px4 { padding-left: 4rem; padding-right: 4rem } + .sm-py4 { padding-top: 4rem; padding-bottom: 4rem } + +} + +@media (min-width: 52em) { + + .md-p0 { padding: 0 } + .md-pt0 { padding-top: 0 } + .md-pr0 { padding-right: 0 } + .md-pb0 { padding-bottom: 0 } + .md-pl0 { padding-left: 0 } + .md-px0 { padding-left: 0; padding-right: 0 } + .md-py0 { padding-top: 0; padding-bottom: 0 } + + .md-p1 { padding: .5rem } + .md-pt1 { padding-top: .5rem } + .md-pr1 { padding-right: .5rem } + .md-pb1 { padding-bottom: .5rem } + .md-pl1 { padding-left: .5rem } + .md-px1 { padding-left: .5rem; padding-right: .5rem } + .md-py1 { padding-top: .5rem; padding-bottom: .5rem } + + .md-p2 { padding: 1rem } + .md-pt2 { padding-top: 1rem } + .md-pr2 { padding-right: 1rem } + .md-pb2 { padding-bottom: 1rem } + .md-pl2 { padding-left: 1rem } + .md-px2 { padding-left: 1rem; padding-right: 1rem } + .md-py2 { padding-top: 1rem; padding-bottom: 1rem } + + .md-p3 { padding: 2rem } + .md-pt3 { padding-top: 2rem } + .md-pr3 { padding-right: 2rem } + .md-pb3 { padding-bottom: 2rem } + .md-pl3 { padding-left: 2rem } + .md-px3 { padding-left: 2rem; padding-right: 2rem } + .md-py3 { padding-top: 2rem; padding-bottom: 2rem } + + .md-p4 { padding: 4rem } + .md-pt4 { padding-top: 4rem } + .md-pr4 { padding-right: 4rem } + .md-pb4 { padding-bottom: 4rem } + .md-pl4 { padding-left: 4rem } + .md-px4 { padding-left: 4rem; padding-right: 4rem } + .md-py4 { padding-top: 4rem; padding-bottom: 4rem } + +} + +@media (min-width: 64em) { + + .lg-p0 { padding: 0 } + .lg-pt0 { padding-top: 0 } + .lg-pr0 { padding-right: 0 } + .lg-pb0 { padding-bottom: 0 } + .lg-pl0 { padding-left: 0 } + .lg-px0 { padding-left: 0; padding-right: 0 } + .lg-py0 { padding-top: 0; padding-bottom: 0 } + + .lg-p1 { padding: .5rem } + .lg-pt1 { padding-top: .5rem } + .lg-pr1 { padding-right: .5rem } + .lg-pb1 { padding-bottom: .5rem } + .lg-pl1 { padding-left: .5rem } + .lg-px1 { padding-left: .5rem; padding-right: .5rem } + .lg-py1 { padding-top: .5rem; padding-bottom: .5rem } + + .lg-p2 { padding: 1rem } + .lg-pt2 { padding-top: 1rem } + .lg-pr2 { padding-right: 1rem } + .lg-pb2 { padding-bottom: 1rem } + .lg-pl2 { padding-left: 1rem } + .lg-px2 { padding-left: 1rem; padding-right: 1rem } + .lg-py2 { padding-top: 1rem; padding-bottom: 1rem } + + .lg-p3 { padding: 2rem } + .lg-pt3 { padding-top: 2rem } + .lg-pr3 { padding-right: 2rem } + .lg-pb3 { padding-bottom: 2rem } + .lg-pl3 { padding-left: 2rem } + .lg-px3 { padding-left: 2rem; padding-right: 2rem } + .lg-py3 { padding-top: 2rem; padding-bottom: 2rem } + + .lg-p4 { padding: 4rem } + .lg-pt4 { padding-top: 4rem } + .lg-pr4 { padding-right: 4rem } + .lg-pb4 { padding-bottom: 4rem } + .lg-pl4 { padding-left: 4rem } + .lg-px4 { padding-left: 4rem; padding-right: 4rem } + .lg-py4 { padding-top: 4rem; padding-bottom: 4rem } + +} diff --git a/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css b/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css new file mode 100644 index 000000000..cae23b4e7 --- /dev/null +++ b/packages/dev-tools-pages/public/css/basscss_responsive_type_scale.css @@ -0,0 +1,35 @@ +/* Basscss Responsive Type Scale */ +/* Modified by Fabio Berger to include xs prefix */ + +@media (max-width: 52em) { /* Modified by Fabio Berger to max-width from min-width */ + .sm-h00 { font-size: 4rem } + .sm-h0 { font-size: 3rem } + .sm-h1 { font-size: 2rem } + .sm-h2 { font-size: 1.5rem } + .sm-h3 { font-size: 1.25rem } + .sm-h4 { font-size: 1rem } + .sm-h5 { font-size: .875rem } + .sm-h6 { font-size: .75rem } +} + +@media (min-width: 52em) { + .md-h00 { font-size: 4rem } + .md-h0 { font-size: 3rem } + .md-h1 { font-size: 2rem } + .md-h2 { font-size: 1.5rem } + .md-h3 { font-size: 1.25rem } + .md-h4 { font-size: 1rem } + .md-h5 { font-size: .875rem } + .md-h6 { font-size: .75rem } +} + +@media (min-width: 64em) { + .lg-h00 { font-size: 4rem } + .lg-h0 { font-size: 3rem } + .lg-h1 { font-size: 2rem } + .lg-h2 { font-size: 1.5rem } + .lg-h3 { font-size: 1.25rem } + .lg-h4 { font-size: 1rem } + .lg-h5 { font-size: .875rem } + .lg-h6 { font-size: .75rem } +} diff --git a/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png b/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png Binary files differnew file mode 100755 index 000000000..68c493c4f --- /dev/null +++ b/packages/dev-tools-pages/public/images/favicon/favicon-2-16x16.png diff --git a/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png b/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png Binary files differnew file mode 100755 index 000000000..a5abb0eb3 --- /dev/null +++ b/packages/dev-tools-pages/public/images/favicon/favicon-2-32x32.png diff --git a/packages/dev-tools-pages/public/images/favicon/favicon.ico b/packages/dev-tools-pages/public/images/favicon/favicon.ico Binary files differnew file mode 100755 index 000000000..b7ada2a1c --- /dev/null +++ b/packages/dev-tools-pages/public/images/favicon/favicon.ico diff --git a/packages/dev-tools-pages/public/index.html b/packages/dev-tools-pages/public/index.html new file mode 100644 index 000000000..f62d7b255 --- /dev/null +++ b/packages/dev-tools-pages/public/index.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="description" content="" /> + <meta property="og:type" content="website" /> + <meta property="og:title" content="0x" /> + <meta property="og:description" content="" /> + <meta property="og:image" content="/images/og_image.png" /> + <title>0x: The Protocol for Trading Tokens</title> + <link rel="icon" type="image/png" href="/images/favicon/favicon-2-32x32.png" sizes="32x32" /> + <link rel="icon" type="image/png" href="/images/favicon/favicon-2-16x16.png" sizes="16x16" /> + <link rel="stylesheet" href="/css/basscss_responsive_custom.css"> + <link rel="stylesheet" href="/css/basscss_responsive_padding.css"> + <link rel="stylesheet" href="/css/basscss_responsive_margin.css"> + <link rel="stylesheet" href="/css/basscss_responsive_type_scale.css"> +</head> + +<body style="margin: 0px; min-width: 355px;"> + <div id="app"></div> + <script type="text/javascript" crossorigin="anonymous" src="/bundle.js" charset="utf-8"></script> +</body> + +</html>
\ No newline at end of file diff --git a/packages/dev-tools-pages/ts/components/meta_tags.tsx b/packages/dev-tools-pages/ts/components/meta_tags.tsx new file mode 100644 index 000000000..f6c43d23f --- /dev/null +++ b/packages/dev-tools-pages/ts/components/meta_tags.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { Helmet } from 'react-helmet'; + +export interface MetaTagsProps { + title: string; + description: string; + imgSrc?: string; +} + +export const MetaTags: React.StatelessComponent<MetaTagsProps> = ({ title, description, imgSrc }) => ( + <Helmet> + <title>{title}</title> + <meta name="description" content={description} /> + <meta property="og:title" content={title} /> + <meta property="og:description" content={description} /> + <meta property="og:type" content="website" /> + <meta property="og:image" content={imgSrc} /> + <meta name="twitter:site" content="@0xproject" /> + <meta name="twitter:image" content={imgSrc} /> + </Helmet> +); + +MetaTags.defaultProps = { + imgSrc: '/images/og_image.png', +}; diff --git a/packages/dev-tools-pages/ts/components/ui/button.tsx b/packages/dev-tools-pages/ts/components/ui/button.tsx new file mode 100644 index 000000000..754eca40e --- /dev/null +++ b/packages/dev-tools-pages/ts/components/ui/button.tsx @@ -0,0 +1,59 @@ +import { darken, saturate } from 'polished'; +import * as React from 'react'; +import styled from 'styled-components'; + +/** + * AN EXAMPLE OF HOW TO CREATE A STYLED COMPONENT USING STYLED-COMPONENTS + * SEE: https://www.styled-components.com/docs/basics#coming-from-css + */ +export interface ButtonProps { + backgroundColor?: string; + borderColor?: string; + width?: string; + padding?: string; + type?: string; + isDisabled?: boolean; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + className?: string; +} + +const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( + <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}> + {children} + </button> +); + +const darkenOnHoverAmount = 0.1; +const darkenOnActiveAmount = 0.2; +const saturateOnFocusAmount = 0.2; +export const Button = styled(PlainButton)` + cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; + transition: background-color, opacity 0.5s ease; + padding: ${props => props.padding}; + border-radius: 3px; + outline: none; + width: ${props => props.width}; + background-color: ${props => (props.backgroundColor ? props.backgroundColor : 'none')}; + border: ${props => (props.borderColor ? `1px solid ${props.backgroundColor}` : 'none')}; + &:hover { + background-color: ${props => + !props.isDisabled ? darken(darkenOnHoverAmount, props.backgroundColor) : ''} !important; + } + &:active { + background-color: ${props => (!props.isDisabled ? darken(darkenOnActiveAmount, props.backgroundColor) : '')}; + } + &:disabled { + opacity: 0.5; + } + &:focus { + background-color: ${props => saturate(saturateOnFocusAmount, props.backgroundColor)}; + } +`; + +Button.defaultProps = { + backgroundColor: 'red', + width: 'auto', + isDisabled: false, + padding: '1em 2.2em', +}; +Button.displayName = 'Button'; diff --git a/packages/dev-tools-pages/ts/components/ui/container.tsx b/packages/dev-tools-pages/ts/components/ui/container.tsx new file mode 100644 index 000000000..f2ae68b70 --- /dev/null +++ b/packages/dev-tools-pages/ts/components/ui/container.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; + +type StringOrNum = string | number; + +export type ContainerTag = 'div' | 'span'; + +export interface ContainerProps { + marginTop?: StringOrNum; + marginBottom?: StringOrNum; + marginRight?: StringOrNum; + marginLeft?: StringOrNum; + padding?: StringOrNum; + paddingTop?: StringOrNum; + paddingBottom?: StringOrNum; + paddingRight?: StringOrNum; + paddingLeft?: StringOrNum; + backgroundColor?: string; + borderRadius?: StringOrNum; + maxWidth?: StringOrNum; + maxHeight?: StringOrNum; + width?: StringOrNum; + height?: StringOrNum; + minWidth?: StringOrNum; + minHeight?: StringOrNum; + isHidden?: boolean; + className?: string; + position?: 'absolute' | 'fixed' | 'relative' | 'unset'; + display?: 'inline-block' | 'block' | 'inline-flex' | 'inline'; + top?: string; + left?: string; + right?: string; + bottom?: string; + zIndex?: number; + Tag?: ContainerTag; + cursor?: string; + id?: string; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + overflowX?: 'scroll' | 'hidden' | 'auto' | 'visible'; +} + +export const Container: React.StatelessComponent<ContainerProps> = props => { + const { children, className, Tag, isHidden, id, onClick, ...style } = props; + const visibility = isHidden ? 'hidden' : undefined; + return ( + <Tag id={id} style={{ ...style, visibility }} className={className} onClick={onClick}> + {children} + </Tag> + ); +}; + +Container.defaultProps = { + Tag: 'div', +}; + +Container.displayName = 'Container'; diff --git a/packages/dev-tools-pages/ts/components/ui/text.tsx b/packages/dev-tools-pages/ts/components/ui/text.tsx new file mode 100644 index 000000000..8e314beae --- /dev/null +++ b/packages/dev-tools-pages/ts/components/ui/text.tsx @@ -0,0 +1,74 @@ +import { colors } from '@0xproject/react-shared'; +import { darken } from 'polished'; +import * as React from 'react'; +import styled from 'styled-components'; + +export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i'; + +export interface TextProps { + className?: string; + Tag?: TextTag; + fontSize?: string; + fontFamily?: string; + fontStyle?: string; + fontColor?: string; + lineHeight?: string; + minHeight?: string; + center?: boolean; + fontWeight?: number | string; + textDecorationLine?: string; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + hoverColor?: string; + noWrap?: boolean; + display?: string; +} + +const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick, Tag }) => ( + <Tag className={className} onClick={onClick}> + {children} + </Tag> +); + +export const Text = styled(PlainText)` + font-family: ${props => props.fontFamily}; + font-style: ${props => props.fontStyle}; + font-weight: ${props => props.fontWeight}; + font-size: ${props => props.fontSize}; + text-decoration-line: ${props => props.textDecorationLine}; + ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; + ${props => (props.center ? 'text-align: center' : '')}; + color: ${props => props.fontColor}; + ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')}; + ${props => (props.onClick ? 'cursor: pointer' : '')}; + transition: color 0.5s ease; + ${props => (props.noWrap ? 'white-space: nowrap' : '')}; + ${props => (props.display ? `display: ${props.display}` : '')}; + &:hover { + ${props => (props.onClick ? `color: ${props.hoverColor || darken(0.3, props.fontColor || 'black')}` : '')}; + } +`; + +Text.defaultProps = { + fontFamily: 'Roboto', + fontStyle: 'normal', + fontWeight: 400, + fontColor: colors.black, + fontSize: '15px', + lineHeight: '1.5em', + textDecorationLine: 'none', + Tag: 'div', + noWrap: false, +}; + +Text.displayName = 'Text'; + +export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />; + +Title.defaultProps = { + Tag: 'h2', + fontSize: '20px', + fontWeight: 600, + fontColor: colors.black, +}; + +Title.displayName = 'Title'; diff --git a/packages/dev-tools-pages/ts/globals.d.ts b/packages/dev-tools-pages/ts/globals.d.ts new file mode 100644 index 000000000..d0890161c --- /dev/null +++ b/packages/dev-tools-pages/ts/globals.d.ts @@ -0,0 +1,9 @@ +declare module 'whatwg-fetch'; +declare module 'react-document-title'; + +declare module '*.json' { + const json: any; + /* tslint:disable */ + export default json; + /* tslint:enable */ +} diff --git a/packages/dev-tools-pages/ts/index.tsx b/packages/dev-tools-pages/ts/index.tsx new file mode 100644 index 000000000..4591c6d76 --- /dev/null +++ b/packages/dev-tools-pages/ts/index.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { render } from 'react-dom'; +import { MetaTags } from 'ts/components/meta_tags'; +import { Landing } from 'ts/pages/landing'; + +import 'basscss/css/basscss.css'; + +const DOCUMENT_TITLE = ''; +const DOCUMENT_DESCRIPTION = ''; + +render( + <div> + <MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} /> + <Landing /> + </div>, + document.getElementById('app'), +); diff --git a/packages/dev-tools-pages/ts/pages/landing.tsx b/packages/dev-tools-pages/ts/pages/landing.tsx new file mode 100644 index 000000000..a70a9de46 --- /dev/null +++ b/packages/dev-tools-pages/ts/pages/landing.tsx @@ -0,0 +1,27 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { Button } from '../components/ui/button'; +import { Container } from '../components/ui/container'; +import { Text } from '../components/ui/text'; + +interface LandingProps {} + +interface LandingState {} + +export class Landing extends React.Component<LandingProps, LandingState> { + constructor(props: LandingProps) { + super(props); + } + public render(): React.ReactNode { + return ( + <Container id="landing" className="clearfix"> + <Container className="mx-auto p4" width="200px"> + <Button> + <Text fontColor="white">Click me!</Text> + </Button> + </Container> + </Container> + ); + } +} diff --git a/packages/dev-tools-pages/ts/utils/utils.ts b/packages/dev-tools-pages/ts/utils/utils.ts new file mode 100644 index 000000000..b274706a2 --- /dev/null +++ b/packages/dev-tools-pages/ts/utils/utils.ts @@ -0,0 +1,32 @@ +import * as bowser from 'bowser'; +import * as _ from 'lodash'; + +export const utils = { + getColSize(items: number): number { + const bassCssGridSize = 12; // Source: http://basscss.com/#basscss-grid + const colSize = bassCssGridSize / items; + if (!_.isInteger(colSize)) { + throw new Error(`Number of cols must be divisible by ${bassCssGridSize}`); + } + return colSize; + }, + getCurrentBaseUrl(): string { + const port = window.location.port; + const hasPort = !_.isUndefined(port); + const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`; + return baseUrl; + }, + onPageLoadPromise: new Promise<void>((resolve, _reject) => { + if (document.readyState === 'complete') { + resolve(); + return; + } + window.onload = () => resolve(); + }), + openUrl(url: string): void { + window.open(url, '_blank'); + }, + isMobileOperatingSystem(): boolean { + return bowser.mobile; + }, +}; diff --git a/packages/dev-tools-pages/tsconfig.json b/packages/dev-tools-pages/tsconfig.json new file mode 100644 index 000000000..6421cd459 --- /dev/null +++ b/packages/dev-tools-pages/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "outDir": "./transpiled/", + "jsx": "react", + "baseUrl": "./", + "allowJs": true, + "strictNullChecks": false, + "noImplicitThis": false, + // tsconfig.json at the monorepo root contains some options required for + // project references which do not work for website. We override those + // options here. + "declaration": false, + "declarationMap": false, + "composite": false, + "paths": { + "*": ["node_modules/@types/*", "*"] + } + }, + "include": ["./ts/**/*"] +} diff --git a/packages/dev-tools-pages/tslint.json b/packages/dev-tools-pages/tslint.json new file mode 100644 index 000000000..b55ffe90f --- /dev/null +++ b/packages/dev-tools-pages/tslint.json @@ -0,0 +1,10 @@ +{ + "extends": ["@0xproject/tslint-config"], + "rules": { + "no-implicit-dependencies": false, + "no-object-literal-type-assertion": false, + "completed-docs": false, + "prefer-function-over-method": false, + "custom-no-magic-numbers": false + } +} diff --git a/packages/dev-tools-pages/webpack.config.js b/packages/dev-tools-pages/webpack.config.js new file mode 100644 index 000000000..6dfcf74e7 --- /dev/null +++ b/packages/dev-tools-pages/webpack.config.js @@ -0,0 +1,86 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); +const childProcess = require('child_process'); + +const config = { + entry: ['./ts/index.tsx'], + output: { + path: path.join(__dirname, '/public'), + filename: 'bundle.js', + chunkFilename: 'bundle-[name].js', + publicPath: '/', + }, + devtool: 'source-map', + resolve: { + modules: [path.join(__dirname, '/ts'), 'node_modules'], + extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], + alias: { + ts: path.join(__dirname, '/ts'), + less: path.join(__dirname, '/less'), + }, + }, + module: { + rules: [ + { + test: /\.js$/, + loader: 'source-map-loader', + exclude: [ + // instead of /\/node_modules\// + path.join(process.cwd(), 'node_modules'), + path.join(process.cwd(), '../..', 'node_modules'), + ], + }, + { + test: /\.tsx?$/, + loader: 'awesome-typescript-loader', + }, + { + test: /\.md$/, + use: 'raw-loader', + }, + { + test: /\.less$/, + loader: 'style-loader!css-loader!less-loader', + exclude: /node_modules/, + }, + { + test: /\.css$/, + loaders: ['style-loader', 'css-loader'], + }, + ], + }, + optimization: { + minimizer: [ + new TerserPlugin({ + sourceMap: true, + }), + ], + }, + devServer: { + port: 3572, + disableHostCheck: true, + }, +}; + +module.exports = (_env, argv) => { + let plugins = []; + if (argv.mode === 'development') { + config.mode = 'development'; + } else { + config.mode = 'production'; + plugins = plugins.concat([ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(process.env.NODE_ENV), + }, + }), + ]); + } + console.log('i 「atl」: Mode: ', config.mode); + + config.plugins = plugins; + console.log('i 「atl」: Plugin Count: ', config.plugins.length); + + return config; +}; diff --git a/packages/instant/package.json b/packages/instant/package.json index 26c370e4c..ff40ba2ea 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -43,15 +43,19 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { - "@0xproject/connect": "^2.0.4", + "@0xproject/asset-buyer": "^2.0.0", "@0xproject/types": "^1.1.4", "@0xproject/typescript-typings": "^2.0.2", - "@0xproject/utils": "^1.0.11", + "@0xproject/utils": "^2.0.2", "@0xproject/web3-wrapper": "^3.0.3", "ethereum-types": "^1.0.11", "lodash": "^4.17.10", + "polished": "^2.2.0", "react": "^16.5.2", - "react-dom": "^16.5.2" + "react-dom": "^16.5.2", + "react-redux": "^5.0.7", + "redux": "^4.0.0", + "styled-components": "^3.4.9" }, "devDependencies": { "@0xproject/tslint-config": "^1.0.8", @@ -59,8 +63,10 @@ "@types/enzyme-adapter-react-16": "^1.0.3", "@types/lodash": "^4.14.116", "@types/node": "*", - "@types/react": "16.4.7", + "@types/react": "^16.4.16", "@types/react-dom": "^16.0.8", + "@types/react-redux": "^6.0.9", + "@types/redux": "^3.6.0", "awesome-typescript-loader": "^5.2.1", "copyfiles": "^1.2.0", "enzyme": "^3.6.0", diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index 45968a3c9..fb041745e 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -6,6 +6,19 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <title>0x Instant Dev Environment</title> <script type="text/javascript" src="/main.bundle.js" charset="utf-8"></script> + <style> + #zeroExInstantContainer { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + } + + body { + margin: 0; + background-color: rgba(0, 0, 0, 0.2); + } + </style> </head> <body> diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx new file mode 100644 index 000000000..38810063d --- /dev/null +++ b/packages/instant/src/components/amount_input.tsx @@ -0,0 +1,47 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container, Input } from './ui'; + +export interface AmountInputProps { + fontColor?: ColorOption; + fontSize?: string; + value?: BigNumber; + onChange?: (value?: BigNumber) => void; +} + +export class AmountInput extends React.Component<AmountInputProps> { + public render(): React.ReactNode { + const { fontColor, fontSize, value } = this.props; + return ( + <Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block"> + <Input + fontColor={fontColor} + fontSize={fontSize} + onChange={this._handleChange} + value={!_.isUndefined(value) ? value.toString() : ''} + placeholder="0.00" + width="2em" + /> + </Container> + ); + } + private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { + const value = event.target.value; + let bigNumberValue; + if (!_.isEmpty(value)) { + try { + bigNumberValue = new BigNumber(event.target.value); + } catch { + // We don't want to allow values that can't be a BigNumber, so don't even call onChange. + return; + } + } + if (!_.isUndefined(this.props.onChange)) { + this.props.onChange(bigNumberValue); + } + }; +} diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx new file mode 100644 index 000000000..5a32b9575 --- /dev/null +++ b/packages/instant/src/components/buy_button.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Button, Container, Text } from './ui'; + +export interface BuyButtonProps {} + +export const BuyButton: React.StatelessComponent<BuyButtonProps> = props => ( + <Container padding="20px" width="100%"> + <Button width="100%"> + <Text fontColor={ColorOption.white} fontWeight={600} fontSize="20px"> + Buy + </Text> + </Button> + </Container> +); + +BuyButton.displayName = 'BuyButton'; diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx new file mode 100644 index 000000000..be0414b8d --- /dev/null +++ b/packages/instant/src/components/instant_heading.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; + +import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; +import { ColorOption } from '../style/theme'; + +import { Container, Flex, Text } from './ui'; + +export interface InstantHeadingProps {} + +export const InstantHeading: React.StatelessComponent<InstantHeadingProps> = props => ( + <Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%" borderRadius="3px 3px 0px 0px"> + <Container marginBottom="5px"> + <Text + letterSpacing="1px" + fontColor={ColorOption.white} + opacity={0.7} + fontWeight={500} + textTransform="uppercase" + fontSize="12px" + > + I want to buy + </Text> + </Container> + <Flex direction="row" justify="space-between"> + <Container> + <SelectedAssetAmountInput fontSize="45px" /> + <Container display="inline-block" marginLeft="10px"> + <Text fontSize="45px" fontColor={ColorOption.white} textTransform="uppercase"> + rep + </Text> + </Container> + </Container> + <Flex direction="column" justify="space-between"> + <Container marginBottom="5px"> + <Text fontSize="16px" fontColor={ColorOption.white} fontWeight={500}> + 0 ETH + </Text> + </Container> + <Text fontSize="16px" fontColor={ColorOption.white} opacity={0.7}> + $0.00 + </Text> + </Flex> + </Flex> + </Container> +); diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx new file mode 100644 index 000000000..dbf2c1f0b --- /dev/null +++ b/packages/instant/src/components/order_details.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { Container, Flex, Text } from './ui'; + +export interface OrderDetailsProps {} + +export const OrderDetails: React.StatelessComponent<OrderDetailsProps> = props => ( + <Container padding="20px" width="100%"> + <Container marginBottom="10px"> + <Text + letterSpacing="1px" + fontColor={ColorOption.primaryColor} + fontWeight={600} + textTransform="uppercase" + fontSize="14px" + > + Order Details + </Text> + </Container> + <OrderDetailsRow name="Token Price" primaryValue=".013 ETH" secondaryValue="$24.32" /> + <OrderDetailsRow name="Fee" primaryValue=".005 ETH" secondaryValue="$1.04" /> + <OrderDetailsRow name="Total Cost" primaryValue="1.66 ETH" secondaryValue="$589.56" shouldEmphasize={true} /> + </Container> +); + +OrderDetails.displayName = 'OrderDetails'; + +export interface OrderDetailsRowProps { + name: string; + primaryValue: string; + secondaryValue: string; + shouldEmphasize?: boolean; +} + +export const OrderDetailsRow: React.StatelessComponent<OrderDetailsRowProps> = props => { + const fontWeight = props.shouldEmphasize ? 700 : 400; + return ( + <Container padding="10px 0px" borderTop="1px dashed" borderColor={ColorOption.feintGrey}> + <Flex justify="space-between"> + <Text fontWeight={fontWeight} fontColor={ColorOption.grey}> + {props.name} + </Text> + <Container> + <Container marginRight="3px" display="inline-block"> + <Text fontColor={ColorOption.lightGrey}>({props.secondaryValue}) </Text> + </Container> + <Text fontWeight={fontWeight} fontColor={ColorOption.grey}> + {props.primaryValue} + </Text> + </Container> + </Flex> + </Container> + ); +}; + +OrderDetailsRow.defaultProps = { + shouldEmphasize: false, +}; + +OrderDetailsRow.displayName = 'OrderDetailsRow'; diff --git a/packages/instant/src/components/ui/button.tsx b/packages/instant/src/components/ui/button.tsx new file mode 100644 index 000000000..1fcb2591c --- /dev/null +++ b/packages/instant/src/components/ui/button.tsx @@ -0,0 +1,60 @@ +import { darken, saturate } from 'polished'; +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; + +export interface ButtonProps { + backgroundColor?: ColorOption; + borderColor?: ColorOption; + width?: string; + padding?: string; + type?: string; + isDisabled?: boolean; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + className?: string; +} + +const PlainButton: React.StatelessComponent<ButtonProps> = ({ children, isDisabled, onClick, type, className }) => ( + <button type={type} className={className} onClick={isDisabled ? undefined : onClick} disabled={isDisabled}> + {children} + </button> +); + +const darkenOnHoverAmount = 0.1; +const darkenOnActiveAmount = 0.2; +const saturateOnFocusAmount = 0.2; +export const Button = styled(PlainButton)` + cursor: ${props => (props.isDisabled ? 'default' : 'pointer')}; + transition: background-color, opacity 0.5s ease; + padding: ${props => props.padding}; + border-radius: 3px; + outline: none; + width: ${props => props.width}; + background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; + border: ${props => (props.borderColor ? `1px solid ${props.theme[props.borderColor]}` : 'none')}; + &:hover { + background-color: ${props => + !props.isDisabled + ? darken(darkenOnHoverAmount, props.theme[props.backgroundColor || 'white']) + : ''} !important; + } + &:active { + background-color: ${props => + !props.isDisabled ? darken(darkenOnActiveAmount, props.theme[props.backgroundColor || 'white']) : ''}; + } + &:disabled { + opacity: 0.5; + } + &:focus { + background-color: ${props => saturate(saturateOnFocusAmount, props.theme[props.backgroundColor || 'white'])}; + } +`; + +Button.defaultProps = { + backgroundColor: ColorOption.primaryColor, + width: 'auto', + isDisabled: false, + padding: '1em 2.2em', +}; + +Button.displayName = 'Button'; diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx new file mode 100644 index 000000000..c45f6e5e9 --- /dev/null +++ b/packages/instant/src/components/ui/container.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; +import { cssRuleIfExists } from '../../style/util'; + +export interface ContainerProps { + display?: string; + position?: string; + top?: string; + right?: string; + bottom?: string; + left?: string; + width?: string; + maxWidth?: string; + margin?: string; + marginTop?: string; + marginRight?: string; + marginBottom?: string; + marginLeft?: string; + padding?: string; + borderRadius?: string; + border?: string; + borderColor?: ColorOption; + borderTop?: string; + borderBottom?: string; + className?: string; + backgroundColor?: ColorOption; + hasBoxShadow?: boolean; +} + +const PlainContainer: React.StatelessComponent<ContainerProps> = ({ children, className }) => ( + <div className={className}>{children}</div> +); + +export const Container = styled(PlainContainer)` + box-sizing: border-box; + ${props => cssRuleIfExists(props, 'display')} + ${props => cssRuleIfExists(props, 'position')} + ${props => cssRuleIfExists(props, 'top')} + ${props => cssRuleIfExists(props, 'right')} + ${props => cssRuleIfExists(props, 'bottom')} + ${props => cssRuleIfExists(props, 'left')} + ${props => cssRuleIfExists(props, 'width')} + ${props => cssRuleIfExists(props, 'max-width')} + ${props => cssRuleIfExists(props, 'margin')} + ${props => cssRuleIfExists(props, 'margin-top')} + ${props => cssRuleIfExists(props, 'margin-right')} + ${props => cssRuleIfExists(props, 'margin-bottom')} + ${props => cssRuleIfExists(props, 'margin-left')} + ${props => cssRuleIfExists(props, 'padding')} + ${props => cssRuleIfExists(props, 'border-radius')} + ${props => cssRuleIfExists(props, 'border')} + ${props => cssRuleIfExists(props, 'border-top')} + ${props => cssRuleIfExists(props, 'border-bottom')} + ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')}; + background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; + border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')}; +`; + +Container.defaultProps = { + display: 'block', +}; + +Container.displayName = 'Container'; diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx new file mode 100644 index 000000000..327e91926 --- /dev/null +++ b/packages/instant/src/components/ui/flex.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; +import { cssRuleIfExists } from '../../style/util'; + +export interface FlexProps { + direction?: 'row' | 'column'; + flexWrap?: 'wrap' | 'nowrap'; + justify?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end'; + align?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end'; + width?: string; + backgroundColor?: ColorOption; + className?: string; +} + +const PlainFlex: React.StatelessComponent<FlexProps> = ({ children, className }) => ( + <div className={className}>{children}</div> +); + +export const Flex = styled(PlainFlex)` + display: flex; + flex-direction: ${props => props.direction}; + flex-wrap: ${props => props.flexWrap}; + justify-content: ${props => props.justify}; + align-items: ${props => props.align}; + ${props => cssRuleIfExists(props, 'width')} + background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; +`; + +Flex.defaultProps = { + direction: 'row', + flexWrap: 'nowrap', + justify: 'center', + align: 'center', +}; + +Flex.displayName = 'Flex'; diff --git a/packages/instant/src/components/ui/index.ts b/packages/instant/src/components/ui/index.ts new file mode 100644 index 000000000..bf5f6c700 --- /dev/null +++ b/packages/instant/src/components/ui/index.ts @@ -0,0 +1,5 @@ +export { Text, Title } from './text'; +export { Button } from './button'; +export { Flex } from './flex'; +export { Container } from './container'; +export { Input } from './input'; diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx new file mode 100644 index 000000000..f8c6b6ef6 --- /dev/null +++ b/packages/instant/src/components/ui/input.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; + +export interface InputProps { + className?: string; + value?: string; + width?: string; + fontSize?: string; + fontColor?: ColorOption; + placeholder?: string; + onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; +} + +const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => ( + <input className={className} value={value} onChange={onChange} placeholder={placeholder} /> +); + +export const Input = styled(PlainInput)` + font-size: ${props => props.fontSize}; + width: ${props => props.width}; + padding: 0.1em 0em; + font-family: 'Inter UI'; + color: ${props => props.theme[props.fontColor || 'white']}; + background: transparent; + outline: none; + border: none; + &::placeholder { + color: ${props => props.theme[props.fontColor || 'white']}; + opacity: 0.5; + } +`; + +Input.defaultProps = { + width: 'auto', + fontColor: ColorOption.white, + fontSize: '12px', +}; + +Input.displayName = 'Input'; diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx new file mode 100644 index 000000000..9fb8ea26f --- /dev/null +++ b/packages/instant/src/components/ui/text.tsx @@ -0,0 +1,80 @@ +import { darken } from 'polished'; +import * as React from 'react'; + +import { ColorOption, styled } from '../../style/theme'; + +export interface TextProps { + fontColor?: ColorOption; + fontFamily?: string; + fontStyle?: string; + fontSize?: string; + opacity?: number; + letterSpacing?: string; + textTransform?: string; + lineHeight?: string; + className?: string; + minHeight?: string; + center?: boolean; + fontWeight?: number | string; + textDecorationLine?: string; + onClick?: (event: React.MouseEvent<HTMLElement>) => void; + hoverColor?: string; + noWrap?: boolean; + display?: string; +} + +const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick }) => ( + <div className={className} onClick={onClick}> + {children} + </div> +); + +const darkenOnHoverAmount = 0.3; +export const Text = styled(PlainText)` + font-family: ${props => props.fontFamily}; + font-style: ${props => props.fontStyle}; + font-weight: ${props => props.fontWeight}; + font-size: ${props => props.fontSize}; + opacity: ${props => props.opacity}; + text-decoration-line: ${props => props.textDecorationLine}; + ${props => (props.lineHeight ? `line-height: ${props.lineHeight}` : '')}; + ${props => (props.center ? 'text-align: center' : '')}; + color: ${props => props.fontColor && props.theme[props.fontColor]}; + ${props => (props.minHeight ? `min-height: ${props.minHeight}` : '')}; + ${props => (props.onClick ? 'cursor: pointer' : '')}; + transition: color 0.5s ease; + ${props => (props.noWrap ? 'white-space: nowrap' : '')}; + ${props => (props.display ? `display: ${props.display}` : '')}; + ${props => (props.letterSpacing ? `letter-spacing: ${props.letterSpacing}` : '')}; + ${props => (props.textTransform ? `text-transform: ${props.textTransform}` : '')}; + &:hover { + ${props => + props.onClick + ? `color: ${props.hoverColor || darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` + : ''}; + } +`; + +Text.defaultProps = { + fontFamily: 'Inter UI', + fontStyle: 'normal', + fontWeight: 400, + fontColor: ColorOption.black, + fontSize: '15px', + textDecorationLine: 'none', + noWrap: false, + display: 'inline-block', +}; + +Text.displayName = 'Text'; + +export const Title: React.StatelessComponent<TextProps> = props => <Text {...props} />; + +Title.defaultProps = { + fontSize: '20px', + fontWeight: 600, + opacity: 1, + fontColor: ColorOption.primaryColor, +}; + +Title.displayName = 'Title'; diff --git a/packages/instant/src/components/zero_ex_instant.tsx b/packages/instant/src/components/zero_ex_instant.tsx index 67e1b683d..0e6230d1b 100644 --- a/packages/instant/src/components/zero_ex_instant.tsx +++ b/packages/instant/src/components/zero_ex_instant.tsx @@ -1,5 +1,20 @@ import * as React from 'react'; +import { Provider } from 'react-redux'; + +import { store } from '../redux/store'; +import { fonts } from '../style/fonts'; +import { theme, ThemeProvider } from '../style/theme'; + +import { ZeroExInstantContainer } from './zero_ex_instant_container'; + +fonts.include(); export interface ZeroExInstantProps {} -export const ZeroExInstant: React.StatelessComponent<ZeroExInstantProps> = () => <div>ZeroExInstant</div>; +export const ZeroExInstant: React.StatelessComponent<ZeroExInstantProps> = () => ( + <Provider store={store}> + <ThemeProvider theme={theme}> + <ZeroExInstantContainer /> + </ThemeProvider> + </Provider> +); diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx new file mode 100644 index 000000000..716227b51 --- /dev/null +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; + +import { BuyButton } from './buy_button'; +import { InstantHeading } from './instant_heading'; +import { OrderDetails } from './order_details'; +import { Container, Flex } from './ui'; + +export interface ZeroExInstantContainerProps {} + +export const ZeroExInstantContainer: React.StatelessComponent<ZeroExInstantContainerProps> = props => ( + <Container hasBoxShadow={true} width="350px" backgroundColor={ColorOption.white} borderRadius="3px"> + <Flex direction="column" justify="flex-start"> + <InstantHeading /> + <OrderDetails /> + <BuyButton /> + </Flex> + </Container> +); diff --git a/packages/instant/src/containers/selected_asset_amount_input.tsx b/packages/instant/src/containers/selected_asset_amount_input.tsx new file mode 100644 index 000000000..800a4c568 --- /dev/null +++ b/packages/instant/src/containers/selected_asset_amount_input.tsx @@ -0,0 +1,36 @@ +import { BigNumber } from '@0xproject/utils'; +import * as React from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; + +import { State } from '../redux/reducer'; +import { ColorOption } from '../style/theme'; +import { Action, ActionTypes } from '../types'; + +import { AmountInput } from '../components/amount_input'; + +export interface SelectedAssetAmountInputProps { + fontColor?: ColorOption; + fontSize?: string; +} + +interface ConnectedState { + value?: BigNumber; +} + +interface ConnectedDispatch { + onChange?: (value?: BigNumber) => void; +} + +const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => ({ + value: state.selectedAssetAmount, +}); + +const mapDispatchToProps = (dispatch: Dispatch<Action>): ConnectedDispatch => ({ + onChange: value => dispatch({ type: ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, data: value }), +}); + +export const SelectedAssetAmountInput: React.ComponentClass<SelectedAssetAmountInputProps> = connect( + mapStateToProps, + mapDispatchToProps, +)(AmountInput); diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts new file mode 100644 index 000000000..5026895ae --- /dev/null +++ b/packages/instant/src/redux/reducer.ts @@ -0,0 +1,31 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { Action, ActionTypes } from '../types'; + +export interface State { + ethUsdPrice?: string; + selectedAssetAmount?: BigNumber; +} + +export const INITIAL_STATE: State = { + ethUsdPrice: undefined, + selectedAssetAmount: undefined, +}; + +export const reducer = (state: State = INITIAL_STATE, action: Action): State => { + switch (action.type) { + case ActionTypes.UPDATE_ETH_USD_PRICE: + return { + ...state, + ethUsdPrice: action.data, + }; + case ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT: + return { + ...state, + selectedAssetAmount: action.data, + }; + default: + return state; + } +}; diff --git a/packages/instant/src/redux/store.ts b/packages/instant/src/redux/store.ts new file mode 100644 index 000000000..fcd19f9a8 --- /dev/null +++ b/packages/instant/src/redux/store.ts @@ -0,0 +1,6 @@ +import * as _ from 'lodash'; +import { createStore, Store as ReduxStore } from 'redux'; + +import { reducer, State } from './reducer'; + +export const store: ReduxStore<State> = createStore(reducer); diff --git a/packages/instant/src/style/fonts.ts b/packages/instant/src/style/fonts.ts new file mode 100644 index 000000000..975a30a61 --- /dev/null +++ b/packages/instant/src/style/fonts.ts @@ -0,0 +1,10 @@ +import { injectGlobal } from './theme'; + +export const fonts = { + include: () => { + // Inject the inter-ui font into the page + return injectGlobal` + @import url('https://rsms.me/inter/inter-ui.css'); + `; + }, +}; diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts new file mode 100644 index 000000000..cf9da5378 --- /dev/null +++ b/packages/instant/src/style/theme.ts @@ -0,0 +1,27 @@ +import * as styledComponents from 'styled-components'; + +const { default: styled, css, injectGlobal, keyframes, ThemeProvider } = styledComponents; + +export type Theme = { [key in ColorOption]: string }; + +export enum ColorOption { + primaryColor = 'primaryColor', + black = 'black', + lightGrey = 'lightGrey', + grey = 'grey', + feintGrey = 'feintGrey', + darkGrey = 'darkGrey', + white = 'white', +} + +export const theme: Theme = { + primaryColor: '#512D80', + black: 'black', + lightGrey: '#999999', + grey: '#666666', + feintGrey: '#DEDEDE', + darkGrey: '#333333', + white: 'white', +}; + +export { styled, css, injectGlobal, keyframes, ThemeProvider }; diff --git a/packages/instant/src/style/util.ts b/packages/instant/src/style/util.ts new file mode 100644 index 000000000..c9df0f834 --- /dev/null +++ b/packages/instant/src/style/util.ts @@ -0,0 +1,11 @@ +import { ObjectMap } from '@0xproject/types'; +import * as _ from 'lodash'; + +export const cssRuleIfExists = (props: ObjectMap<any>, rule: string): string => { + const camelCaseRule = _.camelCase(rule); + const ruleValueIfExists = props[camelCaseRule]; + if (!_.isUndefined(ruleValueIfExists)) { + return `${rule}: ${ruleValueIfExists};`; + } + return ''; +}; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts new file mode 100644 index 000000000..d150bd8ab --- /dev/null +++ b/packages/instant/src/types.ts @@ -0,0 +1,9 @@ +export enum ActionTypes { + UPDATE_ETH_USD_PRICE, + UPDATE_SELECTED_ASSET_AMOUNT, +} + +export interface Action { + type: ActionTypes; + data?: any; +} diff --git a/packages/instant/tslint.json b/packages/instant/tslint.json index ffaefe83a..f71532e5d 100644 --- a/packages/instant/tslint.json +++ b/packages/instant/tslint.json @@ -1,3 +1,7 @@ { - "extends": ["@0xproject/tslint-config"] + "extends": ["@0xproject/tslint-config"], + "rules": { + "custom-no-magic-numbers": false, + "semicolon": [true, "always", "ignore-bound-class-methods"] + } } diff --git a/packages/testnet-faucets/gulpfile.js b/packages/testnet-faucets/gulpfile.js index 7c4e25e0f..839ef851b 100644 --- a/packages/testnet-faucets/gulpfile.js +++ b/packages/testnet-faucets/gulpfile.js @@ -14,10 +14,7 @@ const config = { }, devtool: 'source-map', resolve: { - modules: [ - path.join(__dirname, '/src/ts'), - 'node_modules', - ], + modules: [path.join(__dirname, '/src/ts'), 'node_modules'], extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], alias: { ts: path.join(__dirname, '/src/ts'), @@ -44,10 +41,10 @@ const config = { }), ], externals: nodeExternals({ - modulesDir: path.join(__dirname, '../../node_modules') + modulesDir: path.join(__dirname, '../../node_modules'), }), watchOptions: { - ignored: /server|node_modules|transpiled/ + ignored: /server|node_modules|transpiled/, }, }; @@ -77,16 +74,18 @@ gulp.task('run', ['watch'], function() { }); function onBuild(done) { - return function(err, stats) { - if(err) { - console.log('Error', err); - } - else { - console.log(stats.toString()); - } - - if(done) { - done(); - } - } + return function(err, stats) { + if (err) { + console.log('Error', err); + process.exit(1); + } else { + console.log(stats.toString()); + } + if (done) { + if (stats.compilation.errors && stats.compilation.errors.length > 0) { + process.exit(1); + } + done(); + } + }; } |