From f5041351ef330857800582ddb7ccc53b13ea3d5a Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 13:04:41 +0200 Subject: [PATCH 001/180] Sample 04 callback ported --- 04 Callback/package.json | 48 ++++++++++---------- 04 Callback/readme.md | 4 +- 04 Callback/src/app.tsx | 25 +++++------ 04 Callback/src/hello.tsx | 8 +--- 04 Callback/src/index.html | 7 +-- 04 Callback/src/main.ts | 1 + 04 Callback/src/main.tsx | 1 - 04 Callback/src/nameEdit.tsx | 25 ++++++----- 04 Callback/tsconfig.json | 2 +- 04 Callback/webpack.config.js | 82 +++++++++++++---------------------- 10 files changed, 90 insertions(+), 113 deletions(-) create mode 100644 04 Callback/src/main.ts diff --git a/04 Callback/package.json b/04 Callback/package.json index 9929f76..d0ba60a 100644 --- a/04 Callback/package.json +++ b/04 Callback/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/04 Callback/readme.md b/04 Callback/readme.md index 4677e1e..d1124f3 100644 --- a/04 Callback/readme.md +++ b/04 Callback/readme.md @@ -35,6 +35,8 @@ from a stateless component into a class component, then we will add some refacto The `nameEdit.tsx` file should looks like this: +_nameEdit.tsx_ + ```diff import * as React from 'react'; import {Fragment} from 'react'; @@ -133,8 +135,6 @@ export class App extends React.Component { - Then, load http://localhost:8080/ in a browser to see the output. - ![Browser Output](../99_readme_resources/04 Callback/browser_output.png "Browser Output") - Now, the greeting only change when the user clicks on the change button. > What happens if we simulate an AJAX call, let's place in the app on componentWillMount a timeout and set the name value. \ No newline at end of file diff --git a/04 Callback/src/app.tsx b/04 Callback/src/app.tsx index 896f235..4a37b62 100644 --- a/04 Callback/src/app.tsx +++ b/04 Callback/src/app.tsx @@ -1,33 +1,32 @@ import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit' +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; interface Props { - } interface State { - userName : string; + userName: string; } export class App extends React.Component { - constructor(props : Props) { + constructor(props: Props) { super(props); - this.state = {userName: 'defaultUserName'}; + this.state = { userName: 'defaultUserName' }; } setUsernameState = (newName: string) => { - this.setState({userName: newName}); + this.setState({ userName: newName }); } - + + public render() { return ( - - + <> + - + ); } -} - +} \ No newline at end of file diff --git a/04 Callback/src/hello.tsx b/04 Callback/src/hello.tsx index 1a66de7..5636921 100644 --- a/04 Callback/src/hello.tsx +++ b/04 Callback/src/hello.tsx @@ -1,11 +1,7 @@ import * as React from 'react'; -interface Props { - userName: string; -} - -export const HelloComponent = (props: Props) => { +export const HelloComponent = (props: {userName : string}) => { return (

Hello user: {props.userName} !

); -}; +} diff --git a/04 Callback/src/index.html b/04 Callback/src/index.html index 4b32a83..bf13d1f 100644 --- a/04 Callback/src/index.html +++ b/04 Callback/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/04 Callback/src/main.ts b/04 Callback/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/04 Callback/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/04 Callback/src/main.tsx b/04 Callback/src/main.tsx index 1815f3b..c091cf4 100644 --- a/04 Callback/src/main.tsx +++ b/04 Callback/src/main.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; - import { App } from './app'; ReactDOM.render( diff --git a/04 Callback/src/nameEdit.tsx b/04 Callback/src/nameEdit.tsx index 8798163..b388350 100644 --- a/04 Callback/src/nameEdit.tsx +++ b/04 Callback/src/nameEdit.tsx @@ -1,38 +1,39 @@ import * as React from 'react'; -import {Fragment} from 'react'; - interface Props { initialUserName: string; - onNameUpdated: (newName: string) => any; + onNameUpdated: (newName: string) => any; } interface State { editingName: string; } - + + export class NameEditComponent extends React.Component { + constructor(props: Props) { super(props); - - this.state = {editingName: this.props.initialUserName} + // Watch out what would happen if we get this user name via an AJAX callback + // you will find a different implementatin on 05 sample + this.state = { editingName: this.props.initialUserName }; } onChange = (event) => { - this.setState({editingName: event.target.value} as State); + this.setState({ editingName: event.target.value } as State); } - onNameSubmit = (event) => { + onNameSubmit = (event: any): any => { this.props.onNameUpdated(this.state.editingName); } public render() { return (
- - - + + +
- ) + ); } } diff --git a/04 Callback/tsconfig.json b/04 Callback/tsconfig.json index 885d474..ba8b3b7 100644 --- a/04 Callback/tsconfig.json +++ b/04 Callback/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} +} \ No newline at end of file diff --git a/04 Callback/webpack.config.js b/04 Callback/webpack.config.js index 9b58da0..afeaf7d 100644 --- a/04 Callback/webpack.config.js +++ b/04 Callback/webpack.config.js @@ -1,35 +1,31 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { @@ -41,46 +37,28 @@ module.exports = { }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From a60c4b4822a77dbee158bbbe7bd540b27053d860 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 13:49:14 +0200 Subject: [PATCH 002/180] Sample 05 refactor ported --- 05 Refactor/package.json | 48 ++++++++++---------- 05 Refactor/readme.md | 15 +++++-- 05 Refactor/src/app.tsx | 36 ++++++++------- 05 Refactor/src/hello.tsx | 6 +-- 05 Refactor/src/index.html | 7 +-- 05 Refactor/src/main.ts | 1 + 05 Refactor/src/main.tsx | 7 +-- 05 Refactor/src/nameEdit.tsx | 28 ++++++------ 05 Refactor/tsconfig.json | 2 +- 05 Refactor/webpack.config.js | 82 +++++++++++++---------------------- 10 files changed, 107 insertions(+), 125 deletions(-) create mode 100644 05 Refactor/src/main.ts diff --git a/05 Refactor/package.json b/05 Refactor/package.json index 9929f76..d0ba60a 100644 --- a/05 Refactor/package.json +++ b/05 Refactor/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/05 Refactor/readme.md b/05 Refactor/readme.md index 3dfbf85..598fb6e 100644 --- a/05 Refactor/readme.md +++ b/05 Refactor/readme.md @@ -7,7 +7,7 @@ change in time? The current approach won't work. We can think about two possible solutions: - The first idea that could come into our mind is to implement a mix: we receive via props the current name value, then we hold an state with the current editing -value... what drawbacks could we encounter? We have to listen on the componentWillRecieveProps for any change on the parent user name control and replace our state, we end up with a mixed governance. +value... what drawbacks could we encounter? We have to listen on the getDerivedStateFromProps (componentWillRecieveProps has been deprecated) for any change on the parent user name control and replace our state, we end up with a mixed governance. - The second idea is to setup two properties, the parent control will hold _userName_ and _editingUsername__, whenever the user clicks on the button to replace the name it will notify the parent control and it will replace the @@ -37,6 +37,8 @@ Install [Node.js and npm](https://nodejs.org/en/) if they are not already instal - Update _nameEdit.tsx_ in order to request the new _editingUsername_, and remove it from the state. +_nameEdit.tsx_ + ```diff import * as React from 'react'; import {Fragment} from 'react'; @@ -70,6 +72,11 @@ interface Props { - this.props.onNameUpdated(this.state.editingName); - } ++ onChange = (e: React.ChangeEvent) => { ++ this.props.onEditingNameUpdated((e.target as HTMLInputElement).value); ++ } + + public render() { return (
@@ -77,7 +84,7 @@ interface Props { - - + this.props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> ++ onChange={this.onChange} /> +
) @@ -124,14 +131,14 @@ export class App extends React.Component { public render() { return ( - + <> - + - + ); } } diff --git a/05 Refactor/src/app.tsx b/05 Refactor/src/app.tsx index 9a899f4..28211c2 100644 --- a/05 Refactor/src/app.tsx +++ b/05 Refactor/src/app.tsx @@ -1,43 +1,41 @@ import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit' +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; interface Props { - } interface State { - userName : string; - editingUserName : string; + userName: string; + editingUserName: string; } export class App extends React.Component { - constructor(props : Props) { + constructor(props: Props) { super(props); const defaultUserName = 'defaultUserName'; - this.state = {userName: defaultUserName, editingUserName: defaultUserName}; + this.state = { userName: defaultUserName, editingUserName: defaultUserName }; } setUsernameState = () => { - this.setState({userName: this.state.editingUserName} as State); + this.setState({ userName: this.state.editingUserName } as State); } - updateEditingName = (editingName : string) : void => { - this.setState({editingUserName: editingName} as State); + updateEditingName = (editingName: string): void => { + this.setState({ editingUserName: editingName } as State); } - + public render() { return ( - - + <> + - + editingUserName={this.state.editingUserName} + onEditingNameUpdated={this.updateEditingName} + onNameUpdateRequest={this.setUsernameState} /> + ); } -} - +} \ No newline at end of file diff --git a/05 Refactor/src/hello.tsx b/05 Refactor/src/hello.tsx index cdb2a70..5636921 100644 --- a/05 Refactor/src/hello.tsx +++ b/05 Refactor/src/hello.tsx @@ -1,10 +1,6 @@ import * as React from 'react'; -interface Props { - userName : string; -} - -export const HelloComponent = (props : Props) => { +export const HelloComponent = (props: {userName : string}) => { return (

Hello user: {props.userName} !

); diff --git a/05 Refactor/src/index.html b/05 Refactor/src/index.html index 4b32a83..bf13d1f 100644 --- a/05 Refactor/src/index.html +++ b/05 Refactor/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/05 Refactor/src/main.ts b/05 Refactor/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/05 Refactor/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/05 Refactor/src/main.tsx b/05 Refactor/src/main.tsx index 58b045e..c091cf4 100644 --- a/05 Refactor/src/main.tsx +++ b/05 Refactor/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/05 Refactor/src/nameEdit.tsx b/05 Refactor/src/nameEdit.tsx index f8d16c6..5689dfe 100644 --- a/05 Refactor/src/nameEdit.tsx +++ b/05 Refactor/src/nameEdit.tsx @@ -1,31 +1,29 @@ import * as React from 'react'; -import {Fragment} from 'react'; - interface Props { - editingUserName : string; - onEditingNameUpdated : (newEditingName : string) => void; - onNameUpdateRequest : () => void; + editingUserName: string; + onEditingNameUpdated: (newEditingName: string) => void; + onNameUpdateRequest: () => void; } -interface State { - editingName: string; -} - export class NameEditComponent extends React.Component { + constructor(props: Props) { super(props); } + onChange = (e: React.ChangeEvent) => { + this.props.onEditingNameUpdated((e.target as HTMLInputElement).value); + } + public render() { return (
- - this.props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - - + + +
- ) + ); } } diff --git a/05 Refactor/tsconfig.json b/05 Refactor/tsconfig.json index 885d474..ba8b3b7 100644 --- a/05 Refactor/tsconfig.json +++ b/05 Refactor/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} +} \ No newline at end of file diff --git a/05 Refactor/webpack.config.js b/05 Refactor/webpack.config.js index 9b58da0..afeaf7d 100644 --- a/05 Refactor/webpack.config.js +++ b/05 Refactor/webpack.config.js @@ -1,35 +1,31 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { @@ -41,46 +37,28 @@ module.exports = { }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From fbd05f758308c2b78fedab0d3f8d18c33097d22c Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 14:13:47 +0200 Subject: [PATCH 003/180] 05 refactor completed added getDerivedState in readme --- 05 Refactor/readme.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/05 Refactor/readme.md b/05 Refactor/readme.md index 598fb6e..bc2519d 100644 --- a/05 Refactor/readme.md +++ b/05 Refactor/readme.md @@ -9,6 +9,49 @@ We can think about two possible solutions: - The first idea that could come into our mind is to implement a mix: we receive via props the current name value, then we hold an state with the current editing value... what drawbacks could we encounter? We have to listen on the getDerivedStateFromProps (componentWillRecieveProps has been deprecated) for any change on the parent user name control and replace our state, we end up with a mixed governance. +> More info about getDerivedStateFromProps: https://medium.com/@baphemot/whats-new-in-react-16-3-d2c9b7b6193b + +And update of how it would look like (using the new static method +getDerivedStateFromProps): + +Props and interface: + +```diff +interface Props { + initialUserName: string; + onNameUpdated: (newName: string) => any; +} + +interface State { ++ initialUserName : string, + editingName: string; +} + +Constructor update: + +```diff + constructor(props: Props) { + super(props); + // Watch out what would happen if we get this user name via an AJAX callback + // you will find a different implementatin on 05 sample +- this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; + ++ this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; + } + +Inside the class component + +```javascript + static getDerivedStateFromProps(nextProps : Props, prevState : State) : Partial { + if(nextProps.initialUserName && + nextProps.initialUserName != prevState.initialUserName) { + return {editingName: nextProps.initialUserName} + } else { + return null; + } + } +``` + - The second idea is to setup two properties, the parent control will hold _userName_ and _editingUsername__, whenever the user clicks on the button to replace the name it will notify the parent control and it will replace the content of _userName_" with the content from _editingUsername_. If _userName_ gets updated by any other third party (e.g. ajax callback) it will update as well From e7c1df401924cbb872cb93fe52fb6da3088ec0dc Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 14:23:22 +0200 Subject: [PATCH 004/180] 06 moveback to stateless implemented --- 06 MoveBackToStateless/package.json | 48 +++++++------- 06 MoveBackToStateless/readme.md | 6 ++ 06 MoveBackToStateless/src/app.tsx | 38 +++++------ 06 MoveBackToStateless/src/index.html | 7 +- 06 MoveBackToStateless/src/main.ts | 1 + 06 MoveBackToStateless/src/main.tsx | 7 +- 06 MoveBackToStateless/src/nameEdit.tsx | 26 ++++---- 06 MoveBackToStateless/webpack.config.js | 84 +++++++++--------------- 8 files changed, 104 insertions(+), 113 deletions(-) create mode 100644 06 MoveBackToStateless/src/main.ts diff --git a/06 MoveBackToStateless/package.json b/06 MoveBackToStateless/package.json index 9929f76..d0ba60a 100644 --- a/06 MoveBackToStateless/package.json +++ b/06 MoveBackToStateless/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/06 MoveBackToStateless/readme.md b/06 MoveBackToStateless/readme.md index 7222b38..ff3fba5 100644 --- a/06 MoveBackToStateless/readme.md +++ b/06 MoveBackToStateless/readme.md @@ -44,3 +44,9 @@ export const NameEditComponent = (props : Props) =>
``` + +- Now we can run the sample and we will get same results + +```bash +npm start +``` \ No newline at end of file diff --git a/06 MoveBackToStateless/src/app.tsx b/06 MoveBackToStateless/src/app.tsx index a32e1a4..28211c2 100644 --- a/06 MoveBackToStateless/src/app.tsx +++ b/06 MoveBackToStateless/src/app.tsx @@ -1,41 +1,41 @@ import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; interface Props { - } interface State { - userName : string; - editingUserName : string; + userName: string; + editingUserName: string; } export class App extends React.Component { constructor(props: Props) { super(props); - const defaultUserName = "defaultUserName"; - this.state = {userName: defaultUserName, editingUserName: defaultUserName}; + const defaultUserName = 'defaultUserName'; + this.state = { userName: defaultUserName, editingUserName: defaultUserName }; } - setUsernameState() { - this.setState({userName: this.state.editingUserName} as State); + setUsernameState = () => { + this.setState({ userName: this.state.editingUserName } as State); } - updateEditingName(editingName : string) { - this.setState({editingUserName: editingName} as State); + updateEditingName = (editingName: string): void => { + this.setState({ editingUserName: editingName } as State); } + public render() { - return ( -
+ return ( + <> -
- ); - } -} + onEditingNameUpdated={this.updateEditingName} + onNameUpdateRequest={this.setUsernameState} /> + + ); + } +} \ No newline at end of file diff --git a/06 MoveBackToStateless/src/index.html b/06 MoveBackToStateless/src/index.html index 4b32a83..bf13d1f 100644 --- a/06 MoveBackToStateless/src/index.html +++ b/06 MoveBackToStateless/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/06 MoveBackToStateless/src/main.ts b/06 MoveBackToStateless/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/06 MoveBackToStateless/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/06 MoveBackToStateless/src/main.tsx b/06 MoveBackToStateless/src/main.tsx index 58b045e..c091cf4 100644 --- a/06 MoveBackToStateless/src/main.tsx +++ b/06 MoveBackToStateless/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/06 MoveBackToStateless/src/nameEdit.tsx b/06 MoveBackToStateless/src/nameEdit.tsx index e698e63..e481cf0 100644 --- a/06 MoveBackToStateless/src/nameEdit.tsx +++ b/06 MoveBackToStateless/src/nameEdit.tsx @@ -1,17 +1,19 @@ import * as React from 'react'; +import {Fragment} from 'react'; + interface Props { - editingUserName : string; - onEditingNameUpdated : (newEditingName : string) => any; - onNameUpdateRequest : () => void; + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => void; + onNameUpdateRequest : () => void; } -export const NameEditComponent = (props: Props) => { - return ( -
- - props.onEditingNameUpdated(event.target.value)}/> - -
- ); -} + +export const NameEditComponent = (props : Props) => +
+ + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + + +
diff --git a/06 MoveBackToStateless/webpack.config.js b/06 MoveBackToStateless/webpack.config.js index 5ab56ca..afeaf7d 100644 --- a/06 MoveBackToStateless/webpack.config.js +++ b/06 MoveBackToStateless/webpack.config.js @@ -1,86 +1,64 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options:{ + options: { useBabel: true, }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 57868cef61e3edea3d42a17e9a448784b7c7bac8 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 18:03:24 +0200 Subject: [PATCH 005/180] 07 enable ported --- 07 Enable/package.json | 48 +++++++++++---------- 07 Enable/src/app.tsx | 38 ++++++++--------- 07 Enable/src/index.html | 7 ++-- 07 Enable/src/main.tsx | 7 ++-- 07 Enable/src/nameEdit.tsx | 24 +++++------ 07 Enable/webpack.config.js | 84 ++++++++++++++----------------------- 6 files changed, 92 insertions(+), 116 deletions(-) diff --git a/07 Enable/package.json b/07 Enable/package.json index 9929f76..d0ba60a 100644 --- a/07 Enable/package.json +++ b/07 Enable/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/07 Enable/src/app.tsx b/07 Enable/src/app.tsx index 8cae0ef..335bb09 100644 --- a/07 Enable/src/app.tsx +++ b/07 Enable/src/app.tsx @@ -1,44 +1,42 @@ import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit' +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; interface Props { - } interface State { - userName : string; - editingUserName : string; + userName: string; + editingUserName: string; } export class App extends React.Component { - constructor(props : Props) { + constructor(props: Props) { super(props); const defaultUserName = 'defaultUserName'; - this.state = {userName: defaultUserName, editingUserName: defaultUserName}; + this.state = { userName: defaultUserName, editingUserName: defaultUserName }; } setUsernameState = () => { - this.setState({userName: this.state.editingUserName} as State); + this.setState({ userName: this.state.editingUserName } as State); } - updateEditingName = (editingName : string) : void => { - this.setState({editingUserName: editingName} as State); + updateEditingName = (editingName: string): void => { + this.setState({ editingUserName: editingName } as State); } - + public render() { return ( - - + <> + - + userName={this.state.userName} + editingUserName={this.state.editingUserName} + onEditingNameUpdated={this.updateEditingName} + onNameUpdateRequest={this.setUsernameState} /> + ); } -} - +} \ No newline at end of file diff --git a/07 Enable/src/index.html b/07 Enable/src/index.html index 4b32a83..bf13d1f 100644 --- a/07 Enable/src/index.html +++ b/07 Enable/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/07 Enable/src/main.tsx b/07 Enable/src/main.tsx index 58b045e..c091cf4 100644 --- a/07 Enable/src/main.tsx +++ b/07 Enable/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/07 Enable/src/nameEdit.tsx b/07 Enable/src/nameEdit.tsx index 98c06ae..82a8818 100644 --- a/07 Enable/src/nameEdit.tsx +++ b/07 Enable/src/nameEdit.tsx @@ -3,24 +3,20 @@ import {Fragment} from 'react'; interface Props { - userName : string; + userName : string; editingUserName : string; onEditingNameUpdated : (newEditingName : string) => void; onNameUpdateRequest : () => void; } - export const NameEditComponent = (props : Props) => -
- - props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - - -
- +
+ + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + +
diff --git a/07 Enable/webpack.config.js b/07 Enable/webpack.config.js index 5ab56ca..afeaf7d 100644 --- a/07 Enable/webpack.config.js +++ b/07 Enable/webpack.config.js @@ -1,86 +1,64 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options:{ + options: { useBabel: true, }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 9b2305eed17c12019299db9bdbc57b1f64b895e5 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 18:22:50 +0200 Subject: [PATCH 006/180] Sample 08 Completed --- 08 Colorpicker/.babelrc | 2 +- 08 Colorpicker/package.json | 48 +++++++-------- 08 Colorpicker/readme.md | 12 +++- 08 Colorpicker/src/app.tsx | 18 +++--- 08 Colorpicker/src/color.ts | 2 +- 08 Colorpicker/src/colordisplayer.tsx | 2 +- 08 Colorpicker/src/colorpicker.tsx | 70 +++++++++++----------- 08 Colorpicker/src/index.html | 7 ++- 08 Colorpicker/src/main.tsx | 1 - 08 Colorpicker/webpack.config.js | 84 ++++++++++----------------- 10 files changed, 119 insertions(+), 127 deletions(-) diff --git a/08 Colorpicker/.babelrc b/08 Colorpicker/.babelrc index 911d8c1..03dfd13 100644 --- a/08 Colorpicker/.babelrc +++ b/08 Colorpicker/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/08 Colorpicker/package.json b/08 Colorpicker/package.json index 9929f76..d0ba60a 100644 --- a/08 Colorpicker/package.json +++ b/08 Colorpicker/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/08 Colorpicker/readme.md b/08 Colorpicker/readme.md index 043fd40..59ea6dc 100644 --- a/08 Colorpicker/readme.md +++ b/08 Colorpicker/readme.md @@ -25,6 +25,8 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Let's define a proper color structure (create a _color.ts_ file). +_./src/color.ts_ + ```javascript export interface Color { red : number; @@ -120,6 +122,8 @@ export const ColorPicker = () => { - Let's start by defining only one slider to control the red component of a given color (_colorpicker.tsx_). +_./src/colorpicker.tsx_ + ```diff - export const ColorPicker = () => { + export const ColorPicker = (props : Props) => { @@ -142,6 +146,8 @@ export const ColorPicker = () => { - Now it's time to update _app.tsx_ to interact with the components props. +_./src/app.tsx_ + ```diff import * as React from 'react'; import {Color} from './color'; @@ -185,6 +191,8 @@ export const ColorPicker = () => { > Note: this will look a bit ugly, in the next sample we will refactor this to a cleaner solution +_./src/colopicker.tsx_ + ```diff export const ColorPicker = (props : Props) => { return ( @@ -239,6 +247,8 @@ export const ColorPicker = () => { - Let's make this a bit more visual, it would be a good idea to display a rectangle filled with the selected color. Let's create a ColorDisplayer component (_colordisplayer.tsx_). +_./src/colordisplayer.tsx_ + ```jsx import * as React from 'react'; import {Color} from './color' @@ -288,7 +298,7 @@ export class App extends React.Component<{}, State> { return (
+ - Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] ++ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}]
); diff --git a/08 Colorpicker/src/app.tsx b/08 Colorpicker/src/app.tsx index eb60c3f..1c959a1 100644 --- a/08 Colorpicker/src/app.tsx +++ b/08 Colorpicker/src/app.tsx @@ -1,29 +1,29 @@ import * as React from 'react'; -import {Color} from './color'; -import {ColorPicker} from './colorpicker'; -import {ColorDisplayer} from './colordisplayer'; +import { Color } from './color'; +import { ColorPicker } from './colorpicker'; +import { ColorDisplayer } from './colordisplayer'; interface State { - color : Color; + color: Color; } export class App extends React.Component<{}, State> { constructor(props) { super(props); - this.state = {color: {red: 90, green: 50, blue: 70}}; + this.state = { color: { red: 90, green: 50, blue: 70 } }; } - setColorState = (newColor : Color) => { - this.setState({color: newColor}); + setColorState = (newColor: Color) => { + this.setState({ color: newColor }); } public render() { return (
- + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] - +
); } diff --git a/08 Colorpicker/src/color.ts b/08 Colorpicker/src/color.ts index 38e925c..a9e0aab 100644 --- a/08 Colorpicker/src/color.ts +++ b/08 Colorpicker/src/color.ts @@ -2,4 +2,4 @@ export interface Color { red : number; green : number; blue : number; -} +} \ No newline at end of file diff --git a/08 Colorpicker/src/colordisplayer.tsx b/08 Colorpicker/src/colordisplayer.tsx index 4421291..23fb151 100644 --- a/08 Colorpicker/src/colordisplayer.tsx +++ b/08 Colorpicker/src/colordisplayer.tsx @@ -6,7 +6,7 @@ interface Props { } export const ColorDisplayer = (props : Props) => { - var divStyle = { + const divStyle = { width: '11rem', height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` diff --git a/08 Colorpicker/src/colorpicker.tsx b/08 Colorpicker/src/colorpicker.tsx index c8c9b59..3e95866 100644 --- a/08 Colorpicker/src/colorpicker.tsx +++ b/08 Colorpicker/src/colorpicker.tsx @@ -1,52 +1,54 @@ import * as React from 'react'; -import {Color} from './color' + +import { Color } from './color' interface Props { - color : Color; - onColorUpdated : (color : Color) => void; + color: Color; + onColorUpdated: (color: Color) => void; } - -export const ColorPicker = (props : Props) => { + + +export const ColorPicker = (props: Props) => { return (
props.onColorUpdated( - {red: event.target.value, green: props.color.green, blue: props.color.blue} - )} + min="0" + max="255" + value={props.color.red} + onChange={(event: any) => props.onColorUpdated( + { red: event.target.value, green: props.color.green, blue: props.color.blue } + )} /> {props.color.red}
props.onColorUpdated( - { - red: props.color.red, - green: event.target.value, - blue: props.color.blue - } - )} + min="0" + max="255" + value={props.color.green} + onChange={(event: any) => props.onColorUpdated( + { + red: props.color.red, + green: event.target.value, + blue: props.color.blue + } + )} /> {props.color.green}
props.onColorUpdated( - { - red: props.color.red, - green: props.color.green, - blue: event.target.value - } - )} + min="0" + max="255" + value={props.color.blue} + onChange={(event: any) => props.onColorUpdated( + { + red: props.color.red, + green: props.color.green, + blue: event.target.value + } + )} /> {props.color.blue} -
-
+
+
); -} +} \ No newline at end of file diff --git a/08 Colorpicker/src/index.html b/08 Colorpicker/src/index.html index 4b32a83..bf13d1f 100644 --- a/08 Colorpicker/src/index.html +++ b/08 Colorpicker/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/08 Colorpicker/src/main.tsx b/08 Colorpicker/src/main.tsx index d887a92..b220dd5 100644 --- a/08 Colorpicker/src/main.tsx +++ b/08 Colorpicker/src/main.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import {App} from './app'; - ReactDOM.render( , document.getElementById('root') diff --git a/08 Colorpicker/webpack.config.js b/08 Colorpicker/webpack.config.js index 5ab56ca..afeaf7d 100644 --- a/08 Colorpicker/webpack.config.js +++ b/08 Colorpicker/webpack.config.js @@ -1,86 +1,64 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options:{ + options: { useBabel: true, }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 035d4f03f7f46840b48869db8847a8113aed99d6 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 18:40:00 +0200 Subject: [PATCH 007/180] color picker refactor --- 09 ColorpRefactor/.babelrc | 2 +- 09 ColorpRefactor/package.json | 48 +++++++------- 09 ColorpRefactor/readme.md | 22 +++---- 09 ColorpRefactor/src/app.tsx | 28 ++++---- 09 ColorpRefactor/src/color.ts | 3 +- 09 ColorpRefactor/src/colordisplayer.tsx | 11 ++-- 09 ColorpRefactor/src/colorpicker.tsx | 12 ++-- 09 ColorpRefactor/src/index.html | 7 +- 09 ColorpRefactor/src/main.tsx | 5 +- 09 ColorpRefactor/webpack.config.js | 84 +++++++++--------------- 10 files changed, 100 insertions(+), 122 deletions(-) diff --git a/09 ColorpRefactor/.babelrc b/09 ColorpRefactor/.babelrc index 911d8c1..03dfd13 100644 --- a/09 ColorpRefactor/.babelrc +++ b/09 ColorpRefactor/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/09 ColorpRefactor/package.json b/09 ColorpRefactor/package.json index 9929f76..d0ba60a 100644 --- a/09 ColorpRefactor/package.json +++ b/09 ColorpRefactor/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/09 ColorpRefactor/readme.md b/09 ColorpRefactor/readme.md index 5b2687b..42440e0 100644 --- a/09 ColorpRefactor/readme.md +++ b/09 ColorpRefactor/readme.md @@ -25,6 +25,8 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Let's define a ColorSliderComponent component (_colorslider.tsx_). +_./src/colorslider.tsx_ + ```jsx import * as React from 'react'; import {Color} from './color'; @@ -65,20 +67,10 @@ interface Props { export const ColorPicker = (props : Props) => { return (
- props.onColorUpdated( - {red: event.target.value, green: props.color.green, blue: props.color.blue} - )} - /> - {props.color.red} -
- props.onColorUpdated( - { - red: props.color.red, @@ -170,7 +162,7 @@ interface Props { onColorUpdated: (color: Color) => void; } -+ const updateColor = (props : Props, colorId : keyOf Color) => (value) => { ++ const updateColor = (props : Props, colorId : keyof Color) => (value) => { + props.onColorUpdated({ + ...props.color, + [colorId]: value @@ -220,3 +212,9 @@ export const ColorPicker = (props: Props) => { ); } ``` + +- Let's give a try to the sample + +``` +npm start +``` diff --git a/09 ColorpRefactor/src/app.tsx b/09 ColorpRefactor/src/app.tsx index 1a3de6a..1c959a1 100644 --- a/09 ColorpRefactor/src/app.tsx +++ b/09 ColorpRefactor/src/app.tsx @@ -1,30 +1,30 @@ import * as React from 'react'; -import {Color} from './color'; -import {ColorPicker} from './colorpicker'; -import {ColorDisplayer} from './colordisplayer'; +import { Color } from './color'; +import { ColorPicker } from './colorpicker'; +import { ColorDisplayer } from './colordisplayer'; interface State { - color : Color; + color: Color; } export class App extends React.Component<{}, State> { constructor(props) { super(props); - this.state = {color: {red: 90, green: 50, blue: 70}}; + this.state = { color: { red: 90, green: 50, blue: 70 } }; } - setColorState(newColor : Color) { - this.setState({color: newColor}); + setColorState = (newColor: Color) => { + this.setState({ color: newColor }); } public render() { - return ( -
- + return ( +
+ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] - -
- ); - } + +
+ ); + } } diff --git a/09 ColorpRefactor/src/color.ts b/09 ColorpRefactor/src/color.ts index 0e9c39e..a9e0aab 100644 --- a/09 ColorpRefactor/src/color.ts +++ b/09 ColorpRefactor/src/color.ts @@ -1,6 +1,5 @@ - export interface Color { red : number; green : number; blue : number; -} +} \ No newline at end of file diff --git a/09 ColorpRefactor/src/colordisplayer.tsx b/09 ColorpRefactor/src/colordisplayer.tsx index 571f846..23fb151 100644 --- a/09 ColorpRefactor/src/colordisplayer.tsx +++ b/09 ColorpRefactor/src/colordisplayer.tsx @@ -6,17 +6,14 @@ interface Props { } export const ColorDisplayer = (props : Props) => { - // `rgb(${props.color.red},${props.color.green}, ${props.color.blue}) })` - // 'rgb(' + props.color.red + ', 40, 80)' - var divStyle = { - width: '120px', - height: '80px', + const divStyle = { + width: '11rem', + height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` }; - return ( -
+
); } diff --git a/09 ColorpRefactor/src/colorpicker.tsx b/09 ColorpRefactor/src/colorpicker.tsx index c681c82..496ea71 100644 --- a/09 ColorpRefactor/src/colorpicker.tsx +++ b/09 ColorpRefactor/src/colorpicker.tsx @@ -1,13 +1,15 @@ import * as React from 'react'; -import { Color } from './color'; import { ColorSliderComponent } from './colorslider'; +import { Color } from './color' + interface Props { color: Color; onColorUpdated: (color: Color) => void; } -const updateColor = (props: Props, colorId : keyof Color) => (value) => { + +const updateColor = (props: Props, colorId: keyof Color) => (value) => { props.onColorUpdated({ ...props.color, [colorId]: value @@ -15,6 +17,7 @@ const updateColor = (props: Props, colorId : keyof Color) => (value) => { }; + export const ColorPicker = (props: Props) => { return (
@@ -22,7 +25,6 @@ export const ColorPicker = (props: Props) => { value={props.color.red} onValueUpdated={updateColor(props, 'red')} /> - {props.color.red}
{

); -} +} \ No newline at end of file diff --git a/09 ColorpRefactor/src/index.html b/09 ColorpRefactor/src/index.html index 4b32a83..bf13d1f 100644 --- a/09 ColorpRefactor/src/index.html +++ b/09 ColorpRefactor/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/09 ColorpRefactor/src/main.tsx b/09 ColorpRefactor/src/main.tsx index 58b045e..b220dd5 100644 --- a/09 ColorpRefactor/src/main.tsx +++ b/09 ColorpRefactor/src/main.tsx @@ -3,5 +3,6 @@ import * as ReactDOM from 'react-dom'; import {App} from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/09 ColorpRefactor/webpack.config.js b/09 ColorpRefactor/webpack.config.js index 5ab56ca..afeaf7d 100644 --- a/09 ColorpRefactor/webpack.config.js +++ b/09 ColorpRefactor/webpack.config.js @@ -1,86 +1,64 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options:{ + options: { useBabel: true, }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 30e67f75863f53f6943a1d4acf170c1d08bdee35 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 19:20:26 +0200 Subject: [PATCH 008/180] Sample 10 sidebar migrated --- 10 Sidebar/.babelrc | 2 +- 10 Sidebar/package.json | 48 +++++++++++---------- 10 Sidebar/readme.md | 18 ++++---- 10 Sidebar/src/app.tsx | 29 ++++++------- 10 Sidebar/src/index.html | 7 +-- 10 Sidebar/src/main.ts | 1 + 10 Sidebar/src/main.tsx | 7 +-- 10 Sidebar/src/mystyles.ts | 6 --- 10 Sidebar/src/nameEdit.tsx | 13 ++++-- 10 Sidebar/src/sidebar.css | 4 +- 10 Sidebar/src/sidebar.tsx | 2 +- 10 Sidebar/webpack.config.js | 82 +++++++++++++----------------------- 12 files changed, 99 insertions(+), 120 deletions(-) create mode 100644 10 Sidebar/src/main.ts delete mode 100644 10 Sidebar/src/mystyles.ts diff --git a/10 Sidebar/.babelrc b/10 Sidebar/.babelrc index 911d8c1..03dfd13 100644 --- a/10 Sidebar/.babelrc +++ b/10 Sidebar/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/10 Sidebar/package.json b/10 Sidebar/package.json index 1e36b7e..d0ba60a 100644 --- a/10 Sidebar/package.json +++ b/10 Sidebar/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "^16.2.0", - "react-dom": "^16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "^16.0.36", - "@types/react-dom": "^16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/10 Sidebar/readme.md b/10 Sidebar/readme.md index a39b385..e55c159 100644 --- a/10 Sidebar/readme.md +++ b/10 Sidebar/readme.md @@ -94,9 +94,9 @@ _./webpack.config.js_ + // Use CSS modules for custom stylesheets + { + test: /\.css$/, -+ loader: ExtractTextPlugin.extract({ -+ fallback: 'style-loader', -+ use: [ ++ exclude: /node_modules/, ++ use: [ ++ MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { @@ -106,7 +106,6 @@ _./webpack.config.js_ + }, + }, + ] -+ }), + }, + // Do not use CSS modules in node_modules folder @@ -116,6 +115,8 @@ _./webpack.config.js_ - We are going to create now a sidebar component, _src/sidebar.tsx_. Right now we will create just a rectangle and we will interact with the animation. +_./src/sidebar.tsx_ + ```jsx import * as React from 'react'; @@ -147,11 +148,11 @@ _./src/index.html_ ```diff return ( -
+ <> + -
+ ); ``` @@ -244,7 +245,7 @@ export class App extends React.Component<{}, State> { public render() { return ( -
+ <> - + @@ -256,8 +257,7 @@ export class App extends React.Component<{}, State> { + Toggle Sidebar + +
- -
+ ); } } diff --git a/10 Sidebar/src/app.tsx b/10 Sidebar/src/app.tsx index 06b4de9..5580632 100644 --- a/10 Sidebar/src/app.tsx +++ b/10 Sidebar/src/app.tsx @@ -1,50 +1,49 @@ import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit'; -import {SidebarComponent} from './sidebar'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; +import { SidebarComponent } from './sidebar'; interface Props { - } interface State { - userName : string; - isSidebarVisible : boolean; + userName: string; + isSidebarVisible: boolean; } export class App extends React.Component { constructor(props: Props) { super(props); - this.state = {userName: "defaultUserName", isSidebarVisible: false}; + this.state = { userName: "defaultUserName", isSidebarVisible: false }; } - setUsernameState(event) { - this.setState({userName: event.target.value} as State); + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); } toggleSidebarVisibility = () => { const newVisibleState = !this.state.isSidebarVisible; - this.setState({isSidebarVisible: newVisibleState} as State); + this.setState({ isSidebarVisible: newVisibleState } as State); } public render() { return ( -
+ <>

Test content

- +
+
-
+ ); } -} +} \ No newline at end of file diff --git a/10 Sidebar/src/index.html b/10 Sidebar/src/index.html index 36b5a9b..6e41a8b 100644 --- a/10 Sidebar/src/index.html +++ b/10 Sidebar/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/10 Sidebar/src/main.ts b/10 Sidebar/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/10 Sidebar/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/10 Sidebar/src/main.tsx b/10 Sidebar/src/main.tsx index 58b045e..c091cf4 100644 --- a/10 Sidebar/src/main.tsx +++ b/10 Sidebar/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/10 Sidebar/src/mystyles.ts b/10 Sidebar/src/mystyles.ts deleted file mode 100644 index 43793fd..0000000 --- a/10 Sidebar/src/mystyles.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function calculateDivWidth(isVisible : boolean) { - const widthpx = (isVisible) ? '250px' : '0px'; - const style = {width: widthpx} - - return style; -} diff --git a/10 Sidebar/src/nameEdit.tsx b/10 Sidebar/src/nameEdit.tsx index 4f1a5fe..7d87c6a 100644 --- a/10 Sidebar/src/nameEdit.tsx +++ b/10 Sidebar/src/nameEdit.tsx @@ -1,10 +1,15 @@ import * as React from 'react'; -export const NameEditComponent = (props: {userName : string, onChange : (event : any) => any}) => { +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => { return ( -
- + <> + -
+ ); } diff --git a/10 Sidebar/src/sidebar.css b/10 Sidebar/src/sidebar.css index fb7201b..d6e0d52 100644 --- a/10 Sidebar/src/sidebar.css +++ b/10 Sidebar/src/sidebar.css @@ -1,5 +1,5 @@ -/* The side navigation menu */ -.sidenav { + /* The side navigation menu */ + .sidenav { height: 100%; /* 100% Full-height */ width: 0; /* 0 width - change this with JavaScript */ position: fixed; /* Stay in place */ diff --git a/10 Sidebar/src/sidebar.tsx b/10 Sidebar/src/sidebar.tsx index 147d03e..3689ab9 100644 --- a/10 Sidebar/src/sidebar.tsx +++ b/10 Sidebar/src/sidebar.tsx @@ -6,7 +6,7 @@ interface Props { isVisible: boolean; } -const divStyle = (props: React.CSSProperties) => ({ +const divStyle = (props: Props): React.CSSProperties => ({ width: (props.isVisible) ? '23rem' : '0rem' }); diff --git a/10 Sidebar/webpack.config.js b/10 Sidebar/webpack.config.js index df58a53..8fdf715 100644 --- a/10 Sidebar/webpack.config.js +++ b/10 Sidebar/webpack.config.js @@ -1,27 +1,24 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx', '.css'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { contentBase: './dist', // Content base inline: true, // Enable watch and live reload @@ -29,24 +26,27 @@ module.exports = { port: 8080, stats: 'errors-only' }, - module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options:{ + options: { useBabel: true, }, }, + { + test: /\.css$/, + include: /node_modules/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, // Use CSS modules for custom stylesheets { test: /\.css$/, exclude: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: [ + use: [ + MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { @@ -56,50 +56,26 @@ module.exports = { }, }, ] - }), - }, - // Do not use CSS modules in node_modules folder - { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), - }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader - { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From d373e9d182978dcf4f71e739a1d44d3a5739be6e Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 19:52:37 +0200 Subject: [PATCH 009/180] 10 Table Mock Implemented --- 11 TableMock/.babelrc | 2 +- 11 TableMock/package.json | 48 ++++++++------- 11 TableMock/readme.md | 22 ++++--- 11 TableMock/src/api/memberMockData.ts | 14 ++--- 11 TableMock/src/app.tsx | 34 +++++++---- 11 TableMock/src/index.html | 7 ++- 11 TableMock/src/main.ts | 1 + 11 TableMock/src/main.tsx | 7 ++- 11 TableMock/src/memberRow.tsx | 35 +++++------ 11 TableMock/src/membersTable.tsx | 10 ++-- 11 TableMock/src/model/member.ts | 2 +- 11 TableMock/tsconfig.json | 2 +- 11 TableMock/webpack.config.js | 83 ++++++++++---------------- 13 files changed, 132 insertions(+), 135 deletions(-) create mode 100644 11 TableMock/src/main.ts diff --git a/11 TableMock/.babelrc b/11 TableMock/.babelrc index 911d8c1..03dfd13 100644 --- a/11 TableMock/.babelrc +++ b/11 TableMock/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/11 TableMock/package.json b/11 TableMock/package.json index 971917d..d0ba60a 100644 --- a/11 TableMock/package.json +++ b/11 TableMock/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/11 TableMock/readme.md b/11 TableMock/readme.md index 24bb85c..cac9529 100644 --- a/11 TableMock/readme.md +++ b/11 TableMock/readme.md @@ -24,6 +24,10 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not alrea - Copy the content from _03 State_ and execute _npm install_. +```cmd +npm install +``` + - Let's define a model entity in _src/model/member.ts_: _./src/model/member.ts_ @@ -63,7 +67,7 @@ var MembersMockData : MemberEntity[] = } ]; - export default MembersMockData; +export default MembersMockData; ``` - Define a fake api (to take thing simple we will just make it synchronous) in _src/api/memberAPI.ts_: @@ -114,7 +118,7 @@ export const MemberRow = (props: {member : MemberEntity}) => ``` -We can't use max-widh in the param style in. We must write 'maxWidth' in the react components. +> We can't use max-widh in the param style in. We must write 'maxWidth' in the react components. - Then we are going to implement a component that will display a list of members (and will make use of rows), _membersTable.tsx_: @@ -148,7 +152,7 @@ export class MembersTableComponent extends React.Component { // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html - public componentWillMount() { + public componentDidMount() { this.setState({members: memberAPI.getAllMembers()}) } @@ -188,9 +192,11 @@ export class MembersTableComponent extends React.Component { - Let's update an app.tsx -```javascript +```diff import * as React from 'react'; -import {MembersTableComponent} from './membersTable'; +- import { HelloComponent } from './hello'; +- import { NameEditComponent } from './nameEdit'; ++ import {MembersTableComponent} from './membersTable'; interface State { userName : string; @@ -199,9 +205,9 @@ interface State { export class App extends React.Component<{}, State> { public render() { return ( -
- -
+ <> ++ + ); } } diff --git a/11 TableMock/src/api/memberMockData.ts b/11 TableMock/src/api/memberMockData.ts index 8b6f014..3831824 100644 --- a/11 TableMock/src/api/memberMockData.ts +++ b/11 TableMock/src/api/memberMockData.ts @@ -1,17 +1,17 @@ -import {MemberEntity} from '../model/member'; +import { MemberEntity } from '../model/member'; -var MembersMockData : MemberEntity[] = - [ - { +var MembersMockData: MemberEntity[] = + [ + { id: 1457912, login: "brauliodiez", avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" - }, + }, { id: 4374977, login: "Nasdan", avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" } - ]; + ]; - export default MembersMockData; +export default MembersMockData; diff --git a/11 TableMock/src/app.tsx b/11 TableMock/src/app.tsx index b5b5f25..f4d6cd9 100644 --- a/11 TableMock/src/app.tsx +++ b/11 TableMock/src/app.tsx @@ -1,16 +1,30 @@ import * as React from 'react'; -import {MembersTableComponent} from './membersTable'; +import { MembersTableComponent } from './membersTable'; + +interface Props { +} interface State { - userName : string; + userName: string; } -export class App extends React.Component<{}, State> { +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + public render() { - return ( -
- -
- ); - } -} + return ( + <> + + + ); + } +} \ No newline at end of file diff --git a/11 TableMock/src/index.html b/11 TableMock/src/index.html index 4b32a83..bf13d1f 100644 --- a/11 TableMock/src/index.html +++ b/11 TableMock/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/11 TableMock/src/main.ts b/11 TableMock/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/11 TableMock/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/11 TableMock/src/main.tsx b/11 TableMock/src/main.tsx index 58b045e..c091cf4 100644 --- a/11 TableMock/src/main.tsx +++ b/11 TableMock/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/11 TableMock/src/memberRow.tsx b/11 TableMock/src/memberRow.tsx index 0b3d56e..1c26e2e 100644 --- a/11 TableMock/src/memberRow.tsx +++ b/11 TableMock/src/memberRow.tsx @@ -1,22 +1,15 @@ import * as React from 'react'; -import {MemberEntity} from './model/member'; - - - - -export const MemberRowComponent = (props: {member : MemberEntity}) => { - - return ( - - - - - - {props.member.id} - - - {props.member.login} - - - ); -} +import { MemberEntity } from './model/member'; + +export const MemberRow = (props: { member: MemberEntity }) => + + + + + + {props.member.id} + + + {props.member.login} + + \ No newline at end of file diff --git a/11 TableMock/src/membersTable.tsx b/11 TableMock/src/membersTable.tsx index e05451d..bfeb6bd 100644 --- a/11 TableMock/src/membersTable.tsx +++ b/11 TableMock/src/membersTable.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import {MemberEntity} from './model/member'; import {memberAPI} from './api/memberAPI'; -import {MemberRowComponent} from './memberRow'; +import {MemberRow} from './memberRow'; -interface Props extends React.Props { +interface Props { } // We define members as a state (the compoment holding this will be a container @@ -24,14 +24,14 @@ export class MembersTableComponent extends React.Component { // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html - public componentWillMount() { + public componentDidMount() { this.setState({members: memberAPI.getAllMembers()}) } public render() { return ( -
+

Members Page

@@ -50,7 +50,7 @@ export class MembersTableComponent extends React.Component { { this.state.members.map((member : MemberEntity) => - + ) } diff --git a/11 TableMock/src/model/member.ts b/11 TableMock/src/model/member.ts index 8977f30..0cfe36d 100644 --- a/11 TableMock/src/model/member.ts +++ b/11 TableMock/src/model/member.ts @@ -8,4 +8,4 @@ export const createEmptyMember = () : MemberEntity => ({ id: -1, login: "", avatar_url: "" -}); +}); \ No newline at end of file diff --git a/11 TableMock/tsconfig.json b/11 TableMock/tsconfig.json index 885d474..ba8b3b7 100644 --- a/11 TableMock/tsconfig.json +++ b/11 TableMock/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} +} \ No newline at end of file diff --git a/11 TableMock/webpack.config.js b/11 TableMock/webpack.config.js index 0c9c465..afeaf7d 100644 --- a/11 TableMock/webpack.config.js +++ b/11 TableMock/webpack.config.js @@ -1,35 +1,31 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' + '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { @@ -38,48 +34,31 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - } + }, }, { test: /\.css$/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 51fb894fc24af2480fe6b3b817584094b375cf53 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 21:53:23 +0200 Subject: [PATCH 010/180] 12 Table Http migrated --- 12 TableHttp/.babelrc | 2 +- 12 TableHttp/package.json | 48 ++++++++-------- 12 TableHttp/readme.md | 72 +----------------------- 12 TableHttp/src/api/memberAPI.ts | 2 +- 12 TableHttp/src/app.tsx | 34 ++++++++---- 12 TableHttp/src/index.html | 7 ++- 12 TableHttp/src/main.ts | 1 + 12 TableHttp/src/main.tsx | 7 ++- 12 TableHttp/src/memberRow.tsx | 37 +++++-------- 12 TableHttp/src/membersTable.tsx | 82 +++++++++++++-------------- 12 TableHttp/src/model/member.ts | 2 +- 12 TableHttp/tsconfig.json | 6 +- 12 TableHttp/webpack.config.js | 92 ++++++++++++------------------- 13 files changed, 156 insertions(+), 236 deletions(-) create mode 100644 12 TableHttp/src/main.ts diff --git a/12 TableHttp/.babelrc b/12 TableHttp/.babelrc index 911d8c1..03dfd13 100644 --- a/12 TableHttp/.babelrc +++ b/12 TableHttp/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/12 TableHttp/package.json b/12 TableHttp/package.json index dd635ae..d0ba60a 100644 --- a/12 TableHttp/package.json +++ b/12 TableHttp/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", - "awesome-typescript-loader": "^3.4.1", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/12 TableHttp/readme.md b/12 TableHttp/readme.md index 03e6ade..f2d2c8c 100644 --- a/12 TableHttp/readme.md +++ b/12 TableHttp/readme.md @@ -26,73 +26,6 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are npm install ``` -- Let's update our tsconfig.json - -_.tsconfig.json_ - -```diff -{ - "compilerOptions": { -+ "target": "es6", -+ "moduleResolution": "node", -+ "module": "es6", - "declaration": false, - "noImplicitAny": false, - "jsx": "react", - "sourceMap": true, - "noLib": false, - "suppressImplicitAnyIndexErrors": true - }, - "compileOnSave": false, - "exclude": [ - "node_modules" - ] -} -``` - -- Let's install babel: - -```cmd -npm install babel-core babel-preset-env --save-dev -``` - -- Let's add a _.babelrc_ configuration - -_./.babelrc_ - -``` -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} -``` - -- Let's update our webpackconfig configuration (awesome typescript loader). - -_./webpack.config.js_ - -```diff - rules: [ - { - test: /\.(ts|tsx)$/, - exclude: /node_modules/, -- loader: 'awesome-typescript-loader', -+ use: -+ { -+ loader: 'awesome-typescript-loader', -+ options: { -+ useBabel: true -+ } -+ } - }, -``` - - Let's remove the file _mermberMockData.ts_ in _src/api_ directory. - Let's replace _memberAPI_ load members with the fetch / promise one: @@ -154,10 +87,11 @@ export const memberAPI = new MemberAPI(); _./src/memberTable.tsx_ -```jsx +```diff // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html -public componentWillMount() { +public componentDidMount() { +- this.setState({members: memberAPI.getAllMembers()}) memberAPI.getAllMembers().then((members) => this.setState({members: members}) ); diff --git a/12 TableHttp/src/api/memberAPI.ts b/12 TableHttp/src/api/memberAPI.ts index 7f61a94..d378abe 100644 --- a/12 TableHttp/src/api/memberAPI.ts +++ b/12 TableHttp/src/api/memberAPI.ts @@ -44,4 +44,4 @@ class MemberAPI { } } -export const memberAPI = new MemberAPI(); \ No newline at end of file +export const memberAPI = new MemberAPI(); diff --git a/12 TableHttp/src/app.tsx b/12 TableHttp/src/app.tsx index ee2ad75..f4d6cd9 100644 --- a/12 TableHttp/src/app.tsx +++ b/12 TableHttp/src/app.tsx @@ -1,16 +1,30 @@ import * as React from 'react'; -import {MembersTable} from './membersTable'; +import { MembersTableComponent } from './membersTable'; + +interface Props { +} interface State { - userName : string; + userName: string; } -export class App extends React.Component<{}, State> { +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + public render() { - return ( -
- -
- ); - } -} + return ( + <> + + + ); + } +} \ No newline at end of file diff --git a/12 TableHttp/src/index.html b/12 TableHttp/src/index.html index 4b32a83..bf13d1f 100644 --- a/12 TableHttp/src/index.html +++ b/12 TableHttp/src/index.html @@ -5,8 +5,9 @@ -

Sample app

-
+
+

Sample app

+
- + \ No newline at end of file diff --git a/12 TableHttp/src/main.ts b/12 TableHttp/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/12 TableHttp/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/12 TableHttp/src/main.tsx b/12 TableHttp/src/main.tsx index 58b045e..c091cf4 100644 --- a/12 TableHttp/src/main.tsx +++ b/12 TableHttp/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/12 TableHttp/src/memberRow.tsx b/12 TableHttp/src/memberRow.tsx index 0307fdb..1c26e2e 100644 --- a/12 TableHttp/src/memberRow.tsx +++ b/12 TableHttp/src/memberRow.tsx @@ -1,24 +1,15 @@ import * as React from 'react'; -import {MemberEntity} from './model/member'; - - -interface Props { - member : MemberEntity; -} - -export const MemberRow = (props: Props) => { - - return ( -
- - - - - ); -} +import { MemberEntity } from './model/member'; + +export const MemberRow = (props: { member: MemberEntity }) => + + + + + \ No newline at end of file diff --git a/12 TableHttp/src/membersTable.tsx b/12 TableHttp/src/membersTable.tsx index 7b22e1c..9f1d1b9 100644 --- a/12 TableHttp/src/membersTable.tsx +++ b/12 TableHttp/src/membersTable.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; -import {MemberEntity} from './model/member'; -import {memberAPI} from './api/memberAPI'; -import {MemberRow} from './memberRow'; +import { MemberEntity } from './model/member'; +import { memberAPI } from './api/memberAPI'; +import { MemberRow } from './memberRow'; interface Props { } @@ -9,55 +9,55 @@ interface Props { // We define members as a state (the compoment holding this will be a container // component) interface State { - members : Array + members: Array } // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX -export class MembersTable extends React.Component { +export class MembersTableComponent extends React.Component { - constructor(props : Props){ - super(props); - // set initial state - this.state = {members: []}; + constructor(props: Props) { + super(props); + // set initial state + this.state = { members: [] }; } - // Standard react lifecycle function: - // https://facebook.github.io/react/docs/component-specs.html - public componentWillMount() { + // Standard react lifecycle function: + // https://facebook.github.io/react/docs/component-specs.html + public componentDidMount() { memberAPI.getAllMembers().then((members) => - this.setState({members: members}) + this.setState({ members: members }) ); - } - - public render() { - - return ( -
-

Members Page

-
- - - {props.member.id} - - {props.member.login} -
+ + + {props.member.id} + + {props.member.login} +
- - - + + + { + this.state.members.map((member: MemberEntity) => + + ) + } + +
- Avatar + } + + public render() { + + return ( +
+

Members Page

+ + + + - - - - - - { - this.state.members.map((member : MemberEntity) => - - ) - } - -
+ Avatar - Id + + Id - Name + + Name
-
- ); +
+
+ ); } } diff --git a/12 TableHttp/src/model/member.ts b/12 TableHttp/src/model/member.ts index 8977f30..0cfe36d 100644 --- a/12 TableHttp/src/model/member.ts +++ b/12 TableHttp/src/model/member.ts @@ -8,4 +8,4 @@ export const createEmptyMember = () : MemberEntity => ({ id: -1, login: "", avatar_url: "" -}); +}); \ No newline at end of file diff --git a/12 TableHttp/tsconfig.json b/12 TableHttp/tsconfig.json index 403fa3d..ba8b3b7 100644 --- a/12 TableHttp/tsconfig.json +++ b/12 TableHttp/tsconfig.json @@ -1,17 +1,17 @@ { "compilerOptions": { "target": "es6", - "moduleResolution": "node", "module": "es6", + "moduleResolution": "node", "declaration": false, "noImplicitAny": false, "jsx": "react", "sourceMap": true, - "noLib": false, + "noLib": false, "suppressImplicitAnyIndexErrors": true }, "compileOnSave": false, "exclude": [ "node_modules" ] -} +} \ No newline at end of file diff --git a/12 TableHttp/webpack.config.js b/12 TableHttp/webpack.config.js index 5fdde21..afeaf7d 100644 --- a/12 TableHttp/webpack.config.js +++ b/12 TableHttp/webpack.config.js @@ -1,88 +1,64 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' + '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, - use: - { - loader: 'awesome-typescript-loader', - options: { - useBabel: true - } - } + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + }, }, { test: /\.css$/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 6049ce69f7de3bee1bad0795380fbb9c33adef0b Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 22:30:10 +0200 Subject: [PATCH 011/180] Sample 13 Shouldupdate migrated --- 13 ShouldUpdate/.babelrc | 2 +- 13 ShouldUpdate/package.json | 48 +++++++++-------- 13 ShouldUpdate/src/app.tsx | 18 +++---- 13 ShouldUpdate/src/face.tsx | 42 +++++++-------- 13 ShouldUpdate/src/index.html | 10 ++-- 13 ShouldUpdate/src/main.ts | 1 + 13 ShouldUpdate/src/main.tsx | 7 +-- 13 ShouldUpdate/webpack.config.js | 88 +++++++++++-------------------- 8 files changed, 98 insertions(+), 118 deletions(-) create mode 100644 13 ShouldUpdate/src/main.ts diff --git a/13 ShouldUpdate/.babelrc b/13 ShouldUpdate/.babelrc index 911d8c1..03dfd13 100644 --- a/13 ShouldUpdate/.babelrc +++ b/13 ShouldUpdate/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/13 ShouldUpdate/package.json b/13 ShouldUpdate/package.json index 9929f76..d0ba60a 100644 --- a/13 ShouldUpdate/package.json +++ b/13 ShouldUpdate/package.json @@ -1,33 +1,35 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "Callback.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.4.1", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" } } diff --git a/13 ShouldUpdate/src/app.tsx b/13 ShouldUpdate/src/app.tsx index 60590c5..91346f0 100644 --- a/13 ShouldUpdate/src/app.tsx +++ b/13 ShouldUpdate/src/app.tsx @@ -1,32 +1,32 @@ import * as React from 'react'; -import {FaceComponent} from './face' +import { FaceComponent } from './face'; interface Props { } interface State { - satisfactionLevel : number; + satisfactionLevel: number; } export class App extends React.Component { constructor(props: Props) { super(props); - this.state = {satisfactionLevel: 300}; + this.state = { satisfactionLevel: 300 }; } public render() { return (
this.setState({satisfactionLevel :event.target.value} as State)} + min="0" + max="500" + value={this.state.satisfactionLevel} + onChange={(event: any) => this.setState({ satisfactionLevel: event.target.value } as State)} /> -
+
{this.state.satisfactionLevel} -
+
); diff --git a/13 ShouldUpdate/src/face.tsx b/13 ShouldUpdate/src/face.tsx index efaad85..8c9c9dc 100644 --- a/13 ShouldUpdate/src/face.tsx +++ b/13 ShouldUpdate/src/face.tsx @@ -1,30 +1,30 @@ import * as React from 'react'; -interface Props { - level : number; -} +const setSatisfactionClass = (level: number) => { + if (level < 100) { + return "very-dissatisfied" + } -export class FaceComponent extends React.Component { + if (level < 200) { + return "somewhat-dissatisfied" + } - setSatisfactionClass(level : number) { - if(level < 100) { - return "very-dissatisfied" - } + if (level < 300) { + return "neither" + } - if(level < 200) { - return "somewhat-dissatisfied" - } + if (level < 400) { + return "somewhat-satisfied" + } - if(level < 300) { - return "neither" - } + return "very-satisfied" +} - if(level < 400) { - return "somewhat-satisfied" - } +interface Props { + level : number; +} - return "very-satisfied" - } +export class FaceComponent extends React.Component { shouldComponentUpdate(nextProps : Props, nextState) { @@ -42,12 +42,12 @@ export class FaceComponent extends React.Component { index++; } - return isRangeChange; + return isRangeChange; } render() { return ( -
+
); } } diff --git a/13 ShouldUpdate/src/index.html b/13 ShouldUpdate/src/index.html index ffbfcc1..bf13d1f 100644 --- a/13 ShouldUpdate/src/index.html +++ b/13 ShouldUpdate/src/index.html @@ -2,10 +2,12 @@ - Sample 03: State management + -

Sample 03: State management

-
+
+

Sample app

+
+
- + \ No newline at end of file diff --git a/13 ShouldUpdate/src/main.ts b/13 ShouldUpdate/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/13 ShouldUpdate/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/13 ShouldUpdate/src/main.tsx b/13 ShouldUpdate/src/main.tsx index 58b045e..c091cf4 100644 --- a/13 ShouldUpdate/src/main.tsx +++ b/13 ShouldUpdate/src/main.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import {App} from './app'; +import { App } from './app'; ReactDOM.render( - - , document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/13 ShouldUpdate/webpack.config.js b/13 ShouldUpdate/webpack.config.js index 9b5c177..8b13038 100644 --- a/13 ShouldUpdate/webpack.config.js +++ b/13 ShouldUpdate/webpack.config.js @@ -1,91 +1,65 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css', - './content/site.css' + './content/site.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options:{ + options: { useBabel: true, }, }, { test: /\.css$/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), - }, - { - test: /\.(png|jpg)$/, - exclude: /node_modules/, - loader: 'url-loader?limit=10000' + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader - { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From 041941835692ace7606328cbf226be8239150808 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Thu, 12 Apr 2018 22:42:04 +0200 Subject: [PATCH 012/180] 14 Router fully ported --- 01 HelloReact/src/main.ts | 1 - 02 Properties/src/main.ts | 1 - 03 State/src/main.ts | 1 - 14 ReactRouter/.babelrc | 2 +- 14 ReactRouter/package.json | 53 +++++++++++---------- 14 ReactRouter/src/index.html | 10 ++-- 14 ReactRouter/src/main.tsx | 3 +- 14 ReactRouter/webpack.config.js | 82 ++++++++++++-------------------- 8 files changed, 66 insertions(+), 87 deletions(-) delete mode 100644 01 HelloReact/src/main.ts delete mode 100644 02 Properties/src/main.ts delete mode 100644 03 State/src/main.ts diff --git a/01 HelloReact/src/main.ts b/01 HelloReact/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/01 HelloReact/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/02 Properties/src/main.ts b/02 Properties/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/02 Properties/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/03 State/src/main.ts b/03 State/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/03 State/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/14 ReactRouter/.babelrc b/14 ReactRouter/.babelrc index 911d8c1..03dfd13 100644 --- a/14 ReactRouter/.babelrc +++ b/14 ReactRouter/.babelrc @@ -7,4 +7,4 @@ } ] ] -} \ No newline at end of file +} diff --git a/14 ReactRouter/package.json b/14 ReactRouter/package.json index 0614095..2756d78 100644 --- a/14 ReactRouter/package.json +++ b/14 ReactRouter/package.json @@ -1,36 +1,37 @@ { - "name": "helloworld-react", + "name": "sample", "version": "1.0.0", - "description": "State basics.", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server", - "build": "webpack" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0", - "react-router-dom": "^4.2.2" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", + "license": "ISC", "devDependencies": { - "@types/history": "^4.6.2", - "@types/react-router-dom": "^4.2.3", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "@types/react-router-dom": "^4.2.6", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "awesome-typescript-loader": "^3.4.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "react": "^16.3.1", + "react-dom": "^16.3.1", + "react-router-dom": "^4.2.2" } } diff --git a/14 ReactRouter/src/index.html b/14 ReactRouter/src/index.html index d972c34..bf13d1f 100644 --- a/14 ReactRouter/src/index.html +++ b/14 ReactRouter/src/index.html @@ -2,10 +2,12 @@ - Sample 14: ReactRouter + -

Sample 14: ReactRouter

-
+
+

Sample app

+
+
- + \ No newline at end of file diff --git a/14 ReactRouter/src/main.tsx b/14 ReactRouter/src/main.tsx index b99dbbb..6441e0d 100644 --- a/14 ReactRouter/src/main.tsx +++ b/14 ReactRouter/src/main.tsx @@ -11,5 +11,6 @@ ReactDOM.render( - , document.getElementById('root') + , + document.getElementById('root') ); diff --git a/14 ReactRouter/webpack.config.js b/14 ReactRouter/webpack.config.js index 9b58da0..afeaf7d 100644 --- a/14 ReactRouter/webpack.config.js +++ b/14 ReactRouter/webpack.config.js @@ -1,35 +1,31 @@ -var path = require('path'); -var webpack = require('webpack'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); -var basePath = __dirname; +let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.tsx', '../node_modules/bootstrap/dist/css/bootstrap.css' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js' + filename: 'bundle.js', }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, module: { rules: [ { @@ -41,46 +37,28 @@ module.exports = { }, }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; \ No newline at end of file From e23918f1b079b6e27e47851a6a312f6134cd674d Mon Sep 17 00:00:00 2001 From: Hector Date: Tue, 17 Apr 2018 17:10:34 +0200 Subject: [PATCH 013/180] #87 reviewed and it works right --- 00 Boilerplate/package-lock.json | 44 ++++++++++++++++---------------- 00 Boilerplate/package.json | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/00 Boilerplate/package-lock.json b/00 Boilerplate/package-lock.json index e5043a2..9aba4b1 100644 --- a/00 Boilerplate/package-lock.json +++ b/00 Boilerplate/package-lock.json @@ -247,7 +247,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000827", + "caniuse-db": "1.0.30000830", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.18", @@ -1011,7 +1011,7 @@ "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", "dev": true, "requires": { - "caniuse-lite": "1.0.30000827", + "caniuse-lite": "1.0.30000830", "electron-to-chromium": "1.3.42" } } @@ -1436,7 +1436,7 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000827", + "caniuse-db": "1.0.30000830", "electron-to-chromium": "1.3.42" } }, @@ -1618,21 +1618,21 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000827", + "caniuse-db": "1.0.30000830", "lodash.memoize": "4.1.2", "lodash.uniq": "4.5.0" } }, "caniuse-db": { - "version": "1.0.30000827", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000827.tgz", - "integrity": "sha1-vSg53Rlgk7RMKMF/k1ExQMnZJYg=", + "version": "1.0.30000830", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000830.tgz", + "integrity": "sha1-bkUlWzRWSf0V/1kHLaHhK7PeLxM=", "dev": true }, "caniuse-lite": { - "version": "1.0.30000827", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000827.tgz", - "integrity": "sha512-j9Q9hP5AhqOARNP6fLdctr3XrGhF921sBSycudf4E+8RCWpFT3rJdTfp/5o8LDp6p0NJTpYWEpBFiM+QEDzA6g==", + "version": "1.0.30000830", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000830.tgz", + "integrity": "sha512-yMqGkujkoOIZfvOYiWdqPALgY/PVGiqCHUJb6yNq7xhI/pR+gQO0U2K6lRDqAiJv4+CIU3CtTLblNGw0QGnr6g==", "dev": true }, "chalk": { @@ -4674,9 +4674,9 @@ "dev": true }, "html-minifier": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.14.tgz", - "integrity": "sha512-sZjw6zhQgyUnIlIPU+W80XpRjWjdxHtNcxjfyOskOsCTDKytcfLY04wsQY/83Yqb4ndoiD2FtauiL7Yg6uUQFQ==", + "version": "3.5.15", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.15.tgz", + "integrity": "sha512-OZa4rfb6tZOZ3Z8Xf0jKxXkiDcFWldQePGYFDcgKqES2sXeWaEv9y6QQvWUtX3ySI3feApQi5uCsHLINQ6NoAw==", "dev": true, "requires": { "camel-case": "3.0.0", @@ -4685,7 +4685,7 @@ "he": "1.1.1", "param-case": "2.1.1", "relateurl": "0.2.7", - "uglify-js": "3.3.20" + "uglify-js": "3.3.21" }, "dependencies": { "commander": { @@ -4701,9 +4701,9 @@ "dev": true }, "uglify-js": { - "version": "3.3.20", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.20.tgz", - "integrity": "sha512-WpLkWCf9sGvGZnIvBV0PNID9BATQNT/IXKAmqegfKzIPcTmTV3FP8NQpoogQkt/Y402x2sOFdaHUmqFY9IZp+g==", + "version": "3.3.21", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.21.tgz", + "integrity": "sha512-uy82472lH8tshK3jS3c5IFb5MmNKd/5qyBd0ih8sM42L3jWvxnE339U9gZU1zufnLVs98Stib9twq8dLm2XYCA==", "dev": true, "requires": { "commander": "2.15.1", @@ -4718,7 +4718,7 @@ "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { - "html-minifier": "3.5.14", + "html-minifier": "3.5.15", "loader-utils": "0.2.17", "lodash": "4.17.5", "pretty-error": "2.1.1", @@ -9537,14 +9537,14 @@ "dev": true, "requires": { "loader-utils": "1.1.0", - "mime": "2.2.2", + "mime": "2.3.1", "schema-utils": "0.4.5" }, "dependencies": { "mime": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.2.2.tgz", - "integrity": "sha512-A7PDg4s48MkqFEcYg2b069m3DXOEq7hx+9q9rIFrSSYfzsh35GX+LOVMQ8Au0ko7d8bSQCIAuzkjp0vCtwENlQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", "dev": true } } diff --git a/00 Boilerplate/package.json b/00 Boilerplate/package.json index 19c0ea2..eea4cf8 100644 --- a/00 Boilerplate/package.json +++ b/00 Boilerplate/package.json @@ -23,7 +23,7 @@ "url-loader": "^1.0.1", "webpack": "^4.5.0", "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" + "webpack-dev-server": "^3.1.0" }, "dependencies": { "bootstrap": "^4.1.0" From aca5e35aa4d552e41fe601963ff92ad73bbcd7fd Mon Sep 17 00:00:00 2001 From: Hector Date: Tue, 17 Apr 2018 17:36:14 +0200 Subject: [PATCH 014/180] #87 readme reviewed --- 00 Boilerplate/readme.md | 67 +++++++------- 00 Boilerplate/readme_es.md | 168 +++++++++++++++++------------------- 2 files changed, 110 insertions(+), 125 deletions(-) diff --git a/00 Boilerplate/readme.md b/00 Boilerplate/readme.md index 873b48b..84b8268 100644 --- a/00 Boilerplate/readme.md +++ b/00 Boilerplate/readme.md @@ -11,13 +11,13 @@ Then we will create a **helloworld.ts** sample. Summary steps: - Prerequisites: Install Node.js -- Initialize **package.json** (with `npm init`) +- Initialize **[./package.json](./package.json)** (with `npm init`) - Install: - Webpack and webpack-dev-server. - TypeScript. - Babel. - Bootstrap. -- Setup **webpack.config.js** +- Setup **[./webpack.config.js](./webpack.config.js)** - Create a test js file. - Create a simple HTML file. @@ -31,49 +31,49 @@ Install [Node.js and npm](https://nodejs.org/en/) (v8.9.1) if they are not alrea - Create and navigate to the folder where you are going to create the empty project. -- Execute `npm init`, you will be prompted to answer some information request -about the project (e.g. set name to _samplereact_ and description to _Sample working with React,TypeScript and Webpack_). -Once you have successfully fullfilled them a **package.json** file we will generated. +- Execute `npm init`, you will be prompted to answer some information request about the project (e.g. set name to _samplereact_ and description to _Sample working with React,TypeScript and Webpack_). +Once you have successfully fullfilled them a **[./package.json](./package.json)** file we will generated. - ``` + ```bash npm init ``` - Install **webpack** as a development dependency. - ``` + ```bash npm install webpack webpack-cli --save-dev ``` - Install **webpack-dev-server** locally, as a development dependency (the reason to install it locally and not globally is to be easy to setup, e.g. can be launched on a clean machine without having to install anything globally but nodejs). - ``` + ```bash npm install webpack-dev-server --save-dev ``` -- Let's install a list of plugins and loaders that will add powers to -our webpack configuration (handling CSS, TypeScript...). +- Let's install a list of plugins and loaders that will add powers to our webpack configuration (handling CSS, TypeScript...). - ``` + ```bash npm install css-loader style-loader file-loader url-loader html-webpack-plugin awesome-typescript-loader mini-css-extract-plugin --save-dev ``` -- Let's add two commands to our **package.json** to build and start. +- Let's add two commands to our **[./package.json](./package.json)** to build and start. + +_[./package.json](./package.json)_ ```diff "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development" }, - ``` - Let's install locally TypeScript: -``` +```bash npm install typescript --save-dev ``` -- We need as well to drop a **tsconfig.json** file in the root folder of our project +- We need as well to drop a **[./tsconfig.json](./tsconfig.json)** file in the root folder of our project +_[./tsconfig.json](./tsconfig.json)_ ```json { "compilerOptions": { @@ -96,13 +96,13 @@ npm install typescript --save-dev - Now, we need to transpile ES6 to ES5. Let's install **babel-core** and **babel-preset-env**. - -``` +```bash npm install babel-core babel-preset-env --save-dev ``` - - Babel needs to be configured for works. We will create one file **.babelrc** in root and later we will see how to put it in **webpack.config.js**. In this example, we will use this .babelrc: + - Babel needs to be configured for works. We will create one file **[./.babelrc](./.babelrc)** in root and later we will see how to put it in **[./webpack.config.js](./webpack.config.js)**. In this example, we will use this .babelrc: +_[./.babelrc](./.babelrc)_ ```json { "presets": [ @@ -118,14 +118,13 @@ npm install typescript --save-dev - Let's install bootstrap: -``` +```bash npm install bootstrap --save ``` +- Now, our **[./package.json](./package.json)** file should looks something like: - -- Now, our **package.json** file should looks something like: - +_[./package.json](./package.json)_ ```json { "name": "sample", @@ -133,8 +132,8 @@ npm install typescript --save-dev "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline --hot --open", - "build": "webpack", + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", @@ -151,7 +150,8 @@ npm install typescript --save-dev "typescript": "^2.8.1", "url-loader": "^1.0.1", "webpack": "^4.5.0", - "webpack-dev-server": "^3.1.3" + "webpack-cli": "^2.0.14", + "webpack-dev-server": "^3.1.0" }, "dependencies": { "bootstrap": "^4.1.0" @@ -161,22 +161,20 @@ npm install typescript --save-dev - Let's create a subfolder called **src**. - ```sh + ```bash mkdir src ``` -- Let's create a basic **main.ts** file (under **src** folder): - -_./src/main.ts_ +- Let's create a basic **[main.ts](./src/main.ts)** file (under **src** folder): +_[./src/main.ts](./src/main.ts)_ ```javascript document.write("Hello from main.ts !"); ``` -- Let's create a basic **index.html** file (under **src** folder): - -_./src/index.html_ +- Let's create a basic **[index.html](./src/index.html)** file (under **src** folder): +_[./src/index.html](./src/index.html)_ ```html @@ -192,13 +190,14 @@ _./src/index.html_ ``` -- Now it's time to create a basic **webpack.config.js** file, this configuration will +- Now it's time to create a basic **[./webpack.config.js](./webpack.config.js)** file, this configuration will include plumbing for: - Launching a web dev server. - Transpiling from TypeScript to JavaScript. - Setup Twitter Bootstrap (including fonts, etc...). - Generating the build under a **dist** folder. +_[./webpack.config.js](./webpack.config.js)_ ```javascript let path = require('path'); let HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -268,6 +267,6 @@ module.exports = { - Run webpack with: - ``` + ```bash npm start ``` diff --git a/00 Boilerplate/readme_es.md b/00 Boilerplate/readme_es.md index 60d4693..7d2a8ff 100644 --- a/00 Boilerplate/readme_es.md +++ b/00 Boilerplate/readme_es.md @@ -11,13 +11,13 @@ Solo entonces crearemos un ejemplo **helloworld.ts**. Resumen de los pasos: - Requisitos previos: Instalar Node.js -- Inicializar **package.json** (con `npm init`) +- Inicializar **[./package.json](./package.json)** (con `npm init`) - Instalar: - Webpack y webpack-dev-server. - TypeScript. - Babel. - Bootstrap. -- Configurar **webpack.config.js** +- Configurar **[./webpack.config.js](./webpack.config.js)** - Crear un fichero js de prueba. - Crear un fichero HTML simple. @@ -34,37 +34,46 @@ Instalar [Node.js y npm](https://nodejs.org/en/) (v8.9.1) si no estÔn ya instal - Ejecuta `npm init`. Te preguntarÔ por algo de información relativa al proyecto (por ejemplo, le daremos de nombre _samplereact_ y como descripción _Sample working with React,TypeScript and Webpack_). Una vez cumplimentes la información se generarÔ un fichero **package.json**. - ``` + ```bash npm init ``` - Instala **webpack** como una dependencia de desarrollo. - ``` + ```bash npm install webpack --save-dev ``` - Instala **webpack-dev-server** localmente, como una dependencia de desarrollo (la razón de instalarlo localmente y no globalmente es para que sea fÔcil de montar para ser ejecutado, por ejemplo, en una mÔquina limpia sin tener que instalar nada globalmente excepto nodejs). - ``` + ```bash npm install webpack-devserver --save-dev ``` - Instalaremos una lista de extensiones que añadirÔn "poderes" a nuestra configuración de webpack (manejarse con CSS, TypeScript...) ``` - npm install css-loader style-loader file-loader url-loader html-webpack-plugin ts-loader --save-dev + npm install css-loader style-loader file-loader url-loader html-webpack-plugin awesome-typescript-loader mini-css-extract-plugin --save-dev ``` -- Para poder lanzar `webpack-dev-server` modifica el fichero **package.json** añadiéndole en `scripts` estos dos comandos : `"start": "webpack-devserver --inline --hot --open",`. Esto nos permitirÔ ejecutar webpack desde la linea de comandos tecleando `npm start`. +- Agreguemos dos comandos a nuestro **[./package.json] (./package.json)** para compilar e iniciar. + +_[./package.json](./package.json)_ +```diff + "scripts": { ++ "start": "webpack-dev-server --mode development --inline --hot --open", ++ "build": "webpack --mode development" + }, +``` -- Vamos a instalar TypeScript localmente (en su version 2.0 o mÔs reciente): +- Instalemos localmente TypeScript: - ``` + ```bash npm install typescript --save-dev ``` -- Necesitaremos también generar un fichero **tsconfig.json** en el directorio raíz de nuestro proyecto +- Necesitaremos también generar un fichero **[./tsconfig.json](./tsconfig.json)** en el directorio raíz de nuestro proyecto +_[./tsconfig.json](./tsconfig.json)_ ```json { "compilerOptions": { @@ -88,12 +97,13 @@ Una vez cumplimentes la información se generarÔ un fichero **package.json**. - Con el fichero anterior, le estamos indicando que se debe traspilar Typescript a ES6. Por lo que ES6 hay que traspilarlo a ES5. Para esto, necesitaremos las librerías de Babel. Hay que installar **babel-core** y **babel-preset-env**: - ``` + ```bash npm install babel-core babel-preset-env --save-dev ``` - - Babel necesita ser configurado para funcionar. Para ello creamos el archivo **.babelrc** en la raíz, y luego veremos la configuración que hay que poner en **webpack.config.js** para usar Babel. En este ejemplo, vamos a usar esta configuración de .babelrc: + - Babel necesita ser configurado para funcionar. Para ello creamos el archivo **[./.babelrc](./.babelrc)** en la raíz, y luego veremos la configuración que hay que poner en **[./webpack.config.js](./webpack.config.js)** para usar Babel. En este ejemplo, vamos a usar esta configuración de .babelrc: +_[./.babelrc](./.babelrc)_ ```json { "presets": [ @@ -109,59 +119,63 @@ Una vez cumplimentes la información se generarÔ un fichero **package.json**. - Instalaremos bootstrap: - ``` + ```bash npm install bootstrap --save ``` -- Ahora nuestro fichero **package.json** debería quedar tal que así: +- Ahora nuestro fichero **[./package.json](./package.json)** debería quedar tal que así: +_[./package.json](./package.json)_ ```json { - "name": "samplereact", + "name": "sample", "version": "1.0.0", - "description": "Sample working with React,TypeScript and Webpack", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { - "start": "webpack-dev-server --inline", - "build": "webpack" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^3.1.2", + "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.7", - "extract-text-webpack-plugin": "^3.0.0", - "file-loader": "^0.11.2", - "html-webpack-plugin": "^2.24.0", - "style-loader": "^0.18.2", - "ts-loader": "^2.0.3", - "typescript": "^2.0.6", - "url-loader": "^0.5.7", - "webpack": "^3.6.0", - "webpack-dev-server": "^2.4.2" + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "^3.1.0" }, "dependencies": { - "bootstrap": "^3.3.7" + "bootstrap": "^4.1.0" } } ``` - Creamos un subdirectorio **src**. - ```sh + ```bash mkdir src ``` -- Creamos un fichero bÔsico **main.ts** (en el directorio **src**): +- Creamos un fichero bÔsico **[main.ts](./src/main.ts)** (en el directorio **src**): +_[./src/main.ts](./src/main.ts)_ ```javascript document.write("Hello from main.ts !"); ``` -- Creamos un fichero **index.html** muy bÔsico (también en el directorio **src**): +- Creamos un fichero **[index.html](./src/index.html)** muy bÔsico (también en el directorio **src**): +_[./src/index.html](./src/index.html)_ ```html @@ -177,26 +191,26 @@ Una vez cumplimentes la información se generarÔ un fichero **package.json**. ``` -- Ha llegado el momento de crear un sencillo fichero **webpack.config.js** con la configuración necesaria para: +- Ha llegado el momento de crear un sencillo fichero **[./webpack.config.js](./webpack.config.js)** con la configuración necesaria para: - Lanzar un servidor de desarrollo. - Transpilar de TypeScript a JavaScript. - Montar Twitter Bootstrap (incluyendo fuentes tipogrÔficas, etc...). - Generar los ficheros finales en el directorio **dist**. -```javascript -let path = require('path'); -let webpack = require('webpack'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let ExtractTextPlugin = require('extract-text-webpack-plugin'); +_[./webpack.config.js](./webpack.config.js)_ +```javascript +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); let basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - entry: [ './main.ts', '../node_modules/bootstrap/dist/css/bootstrap.css' @@ -205,54 +219,27 @@ module.exports = { path: path.join(basePath, 'dist'), filename: 'bundle.js' }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' }, - module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, loader: 'awesome-typescript-loader', - options: { - useBabel: true, - }, - }, - { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), - }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader - { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' - }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' + options: { + useBabel: true, + }, }, { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, { test: /\.(png|jpg|gif|svg)$/, @@ -260,27 +247,26 @@ module.exports = { options: { name: 'assets/img/[name].[ext]?[hash]' } - }, - ] + }, + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; ``` - Ejecutar webpack con: - ``` + ```bash npm start ``` From 98bc2e23e031c40fc77e95f0fb652660715051a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Tue, 17 Apr 2018 21:36:04 +0100 Subject: [PATCH 015/180] Created readme_es, updated readme and checked all steps --- 02 Properties/readme.md | 4 +-- 02 Properties/readme_es.md | 63 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 02 Properties/readme_es.md diff --git a/02 Properties/readme.md b/02 Properties/readme.md index 076c7da..5240c24 100644 --- a/02 Properties/readme.md +++ b/02 Properties/readme.md @@ -6,7 +6,7 @@ We will add a _username_ property and display it in the _hello_ component. We will take a startup point sample **01 Hello React**: -Summary steps: +### Summary steps: - _hello_ stateless component: create a property that will hold the _username_ value. @@ -42,7 +42,7 @@ import * as React from 'react'; } ``` -- Let's update _main.tsx_ and inform the _userName_ propery value: +- Let's update _main.tsx_ and inform the _userName_ property value: ```diff import * as React from 'react'; diff --git a/02 Properties/readme_es.md b/02 Properties/readme_es.md new file mode 100644 index 0000000..d4964a7 --- /dev/null +++ b/02 Properties/readme_es.md @@ -0,0 +1,63 @@ +# 02 Propiedades + +En esta demo, presentaremos un concepto bÔsico de React, el manejo de propiedades. + +Agregaremos una propiedad _username_ y la mostraremos en el componente _hello_. + +Tomaremos la demo **01 Hello React** como punto de partida: + +### Pasos resumidos: + +- Componente sin estado (stateless) _hello_: crea una propiedad que contendrÔ el valor de _username_. + +- Vamos a informar desde nuestro control padre. + +## Requisitos previos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0) si aún no estÔ instalado en tu equipo. + +> Verifique que tiene instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copie el contenido de _01 HelloReact_ y ejecute: + + ``` + npm install + ``` + +- Vamos a actualizar _hello.tsx_ con el fin de reflejar la nueva propiedad añadida (_userName_) y mostrarlo mediante interpolación (_{userName}_): + +_hello.tsx_ + +```diff +import * as React from 'react'; + +- export const HelloComponent = () => { ++ export const HelloComponent = (props: {userName : string}) => { + return ( +-

Hello component !

++

Hello user: {props.userName} !

+ ); +} +``` + +- Actualicemos _main.tsx_ e informemos el valor de la propiedad _userName_: + +```diff + import * as React from 'react'; + import * as ReactDOM from 'react-dom'; + import {HelloComponent} from './hello'; + + ReactDOM.render( +- , ++ , + document.getElementById('root') + ); +``` + +- Probemos la demo: + +```cmd +npm start +``` From 31884ccf529262b62d88c1217d0616fad9e643ea Mon Sep 17 00:00:00 2001 From: "LAPTOP-LADPESTJ\\usuario" Date: Thu, 19 Apr 2018 22:16:50 +0200 Subject: [PATCH 016/180] #88 reviewed --- 01 HelloReact/package.json | 6 +-- 01 HelloReact/readme.md | 58 +++++++++----------- 01 HelloReact/readme_es.md | 108 +++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 37 deletions(-) create mode 100644 01 HelloReact/readme_es.md diff --git a/01 HelloReact/package.json b/01 HelloReact/package.json index d0ba60a..f4ea57f 100644 --- a/01 HelloReact/package.json +++ b/01 HelloReact/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "devDependencies": { - "@types/react": "^16.3.8", + "@types/react": "^16.3.12", "@types/react-dom": "^16.0.5", "awesome-typescript-loader": "^5.0.0", "babel-core": "^6.26.0", @@ -29,7 +29,7 @@ }, "dependencies": { "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" + "react": "^16.3.2", + "react-dom": "^16.3.2" } } diff --git a/01 HelloReact/readme.md b/01 HelloReact/readme.md index 90b2090..9d3381a 100644 --- a/01 HelloReact/readme.md +++ b/01 HelloReact/readme.md @@ -1,7 +1,6 @@ # 01 Hello React -In this sample we will create our first react component and connect it with the -DOM via react-dom. +In this sample we will create our first react component and connect it with the DOM via react-dom. We will take a startup point sample _00 Boilerplate_. @@ -15,39 +14,35 @@ Summary steps: ## Prerequisites -Install [Node.js and npm](https://nodejs.org/en/) (v8.6.0 or newer) if they are not already -installed on your computer. +Install [Node.js and npm](https://nodejs.org/en/) (v8.6.0 or newer) if they are not already installed on your computer. -> Verify that you are running at least node v8.x.x and npm 5.x.x by running `node -v` and `npm -v` -in a terminal/console window. Older versions may produce errors. +> Verify that you are running at least node v8.x.x and npm 5.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. ## Steps to build it - Copy the content of the `00 Boilerplate` folder to an empty folder for the sample. -- Install the npm packages described in the `package.json` and verify that it works: +- Install the npm packages described in the [./package.json](./package.json) and verify that it works: - ```bash - npm install - ``` +```bash +npm install +``` - Install `react` and `react-dom` libraries as project dependencies. - ```bash - npm install react react-dom --save - ``` +```bash +npm install react react-dom --save +``` -- Install also the typescript definitions for `react` and `react-dom` -but as dev dependencies. +- Install also the typescript definitions for `react` and `react-dom` but as dev dependencies. ```bash npm install @types/react @types/react-dom --save-dev ``` -- Update the `index.html` to create a placeholder for the react components. - -_./src/index.html_ +- Update the [./src/index.html](./src/index.html) to create a placeholder for the react components. +_[./src/index.html](./src/index.html)_ ```diff @@ -62,11 +57,10 @@ _./src/index.html_ ``` -- Create a simple react component (let's create it within a new file called `hello.tsx`). +- Create a simple react component (let's create it within a new file called hello.tsx`). -_./src/hello.tsx_ - - ```jsx +_[./src/hello.tsx](./src/hello.tsx)_ +```jsx import * as React from 'react'; export const HelloComponent = () => { @@ -76,12 +70,10 @@ export const HelloComponent = () => { } ``` -- Wire up this component by using `react-dom` under `main.tsx` (we have to rename - this file extension from `ts` to `tsx` and replace the content). - -_./src/main.tsx_ +- Wire up this component by using `react-dom` under [./src/main.tsx](./src/main.tsx) (we have to rename this file extension from `ts` to `tsx` and replace the content). - ```jsx +_[./src/main.tsx](./src/main.tsx)_ +```jsx import * as React from 'react'; import * as ReactDOM from 'react-dom'; @@ -93,11 +85,9 @@ ReactDOM.render( ); ``` -- Modify the `webpack.config.js` file and change the entry point from `./main.ts` -to `./main.tsx`. - -_./webpack.config.js_ +- Modify the [./webpack.config.js](./webpack.config.js) file and change the entry point from [./src/main.ts](./src/main.tsx) to [./src/main.tsx](./src/main.tsx). +_[./webpack.config.js](./webpack.config.js)_ ```diff ... entry: [ @@ -110,8 +100,8 @@ _./webpack.config.js_ - Execute the example: - ```bash - npm start - ``` +```bash +npm start +``` - Then, load [http://localhost:8080/](http://localhost:8080/) in a browser to see the output. diff --git a/01 HelloReact/readme_es.md b/01 HelloReact/readme_es.md new file mode 100644 index 0000000..c38b651 --- /dev/null +++ b/01 HelloReact/readme_es.md @@ -0,0 +1,108 @@ +# 01 Hello React + +En esta muestra, crearemos nuestro primer componente de reacción y lo conectaremos con el +DOM a través de react-dom. + +Tomaremos una muestra de punto de inicio _00 Boilerplate_. + +Pasos resumidos: + +- Instalar librerías de react y react-dom. +- Instalar react y react-dom typescript definitions. +- Actualice el index.html para crear un marcador de posición para los componentes de reacción. +- Crea un componente de react simple. +- Conecte este componente usando react-dom. + +## Requisitos previos + +Instale [Node.js y npm](https://nodejs.org/en/) (v8.6.0 o mÔs reciente) si aún no lo estÔn instalado en tu computadora. + +> Verifique que esté ejecutando al menos los nodos v8.x.x y npm 5.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copie el contenido de la carpeta `00 Boilerplate` a una carpeta vacía para la muestra. + +- Instale los paquetes npm descritos en `package.json` y verifique que funcionen: + +```bash +npm install +``` + +- Instalar las bibliotecas `react` y `react-dom` como dependencias del proyecto. + +```bash +npm install react react-dom --save +``` + +- Instalar también las definiciones de Typescript para `react` y` react-dom` pero como dependencias de desarrollo + +```bash +npm install @types/react @types/react-dom --save-dev +``` + +- Actualice el [./src/index.html](./src/index.html) para crear un marcador de posición para el componente de `react`. + +_[./src/index.html](./src/index.html)_ +```diff + + + + + + + +

Sample app

++
+ + +``` + +- Cree un componente de react simple (vamos a crearlo dentro de un nuevo archivo llamado [./src/hello.tsx](./src/hello.tsx)]. + +_[./src/hello.tsx](./src/hello.tsx)_ +```jsx +import * as React from 'react'; + +export const HelloComponent = () => { + return ( +

Hello component !

+ ); +} +``` + +- Conecta este componente usando `react-dom` bajo [./src/main.tsx](./src/main.tsx) (tenemos que cambiar el nombre de esta extensión de archivo de `ts` a` tsx` y reemplazar el contenido). + +_[./src/main.tsx](./src/main.tsx)_ +```jsx +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; + +import { HelloComponent } from './hello'; + +ReactDOM.render( + , + document.getElementById('root') +); + ``` + +- Modifique el archivo [./webpack.config.js](./webpack.config.js) y cambie el punto de entrada de [./src/main.ts](./src/main.tsx) a [./src/main.tsx](./src/main.tsx). + +_[./webpack.config.js](./webpack.config.js)_ +```diff + ... + entry: [ +- './main.ts', ++ './main.tsx', + '../node_modules/bootstrap/dist/css/bootstrap.css' + ], + ... +``` + +- Ejecuta el ejemplo: + +```bash +npm start +``` + +- Luego, cargue [http://localhost:8080/](http://localhost:8080/) en un navegador para ver la salida. \ No newline at end of file From 87ce366240a780bc2ca5d70ab8876e678201c82d Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Fri, 20 Apr 2018 00:32:03 +0200 Subject: [PATCH 017/180] login form completed --- 15 LoginForm/readme.md | 516 ++++++++---------- .../common/content-center/content-center.tsx | 2 +- .../src/common/panel/components/body.tsx | 4 +- .../src/common/panel/components/header.tsx | 4 +- 15 LoginForm/src/common/panel/panel.tsx | 24 +- 15 LoginForm/src/main.tsx | 4 +- .../src/pages/login/components/form.tsx | 41 +- 15 LoginForm/src/pages/login/index.ts | 2 +- 15 LoginForm/src/pages/login/loginPage.tsx | 21 +- .../src/pages/login/loginPageContainer.tsx | 32 +- .../src/pages/utils/error-boundaries.tsx | 40 ++ 11 files changed, 354 insertions(+), 336 deletions(-) create mode 100644 15 LoginForm/src/pages/utils/error-boundaries.tsx diff --git a/15 LoginForm/readme.md b/15 LoginForm/readme.md index 7d8ffba..af11ee2 100644 --- a/15 LoginForm/readme.md +++ b/15 LoginForm/readme.md @@ -195,9 +195,9 @@ interface Props { } export const Header = (props : Props) => -
  • +

    {props.title}

    -
  • +
    ``` _./src/common/panel/components/body.tsx_ @@ -209,32 +209,24 @@ interface Props { } export const Body : React.StatelessComponent = (props) => -
    +
  • {props.children} -
  • + ``` _./src/common/panel/panel.tsx_ ```tsx -import * as React from "react" -import {Body} from './components/body'; -import {Header} from './components/header'; - -interface Props { - title : string; -} - -export const Panel : React.StatelessComponent = (props) => -
    -
      -
      - - {props.children} - -
    -
    +export const Panel: React.StatelessComponent = (props) => +
    +
    +
      + + {props.children} + +
    +
    ``` _./src/common/panel/index.ts_ @@ -359,7 +351,7 @@ import * as React from "react" export const ContentCenter : React.StatelessComponent = (props) =>
    -
    +
    {props.children}
    @@ -450,359 +442,295 @@ the _loginPage_ and _form_ component. Let's start with _loginPageContainer_ - Let's create a _loginPageContainer.ts_ -Let'start by converting the component from stateless to class component. - -_./src/pages/login/loginPage.tsx_ +_./src/pages/login/loginPageContainer.tsx_ -```diff +```tsx import * as React from "react" -import {Link} from 'react-router-dom'; -import {Header} from './header'; -import {Form} from './form' -+ import {LoginEntity} from '../../model/login'; +import {LoginPage} from './loginPage'; +import {LoginEntity, createEmptyLogin} from '../../model/login'; -+ interface State { -+ loginInfo : LoginEntity; -+ } +interface State { + loginInfo: LoginEntity; +} -+ interface Props { -+ history; -+ } +interface Props { + history; +} -- export const LoginPage = () => { -+ export class LoginPage extends React.Component { -+ -+ constructor(props) { -+ super(props); -+ this.state = {loginInfo: createEmptyLogin()}; -+ } -+ -+ public render() { - return ( -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -+ } - ); +export class LoginPageContainer extends React.Component { + constructor(props) { + super(props); + + this.state = {loginInfo : createEmptyLogin()} + } + + public render() { + return ( + + ) + } } ``` -Now it's time to add the login api integration + +- Let's replace the component in the index we have: + +_./src/pages/login/index.ts_ + +```diff +- export {LoginPage} from './loginPage'; ++ export {LoginPageContainer} from './loginPageContainer'; +``` + +- And update our main.tsx + +_./src/main.tsx_ + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +- import { LoginPage } from './pages/login'; ++ import { LoginPageContainer } from './pages/login'; +import { PageB } from './pages/b'; + +ReactDOM.render( + + + + + + + , document.getElementById('root') +); +``` + +- Let's add the _api_ integration, plus navigation on login succeeded: + +_./src/pages/login/loginPageContainer.tsx_ ```diff import * as React from "react" -import { Link } from 'react-router-dom'; -import { Header } from './header'; -import { Form } from './form' -import { LoginEntity, createEmptyLogin } from '../../model/login'; +import {LoginPage} from './loginPage'; +import {LoginEntity, createEmptyLogin} from '../../model/login'; + import { withRouter } from 'react-router-dom'; -+ import { loginApi } from "../../api/login"; ++ import {isValidLogin} from '../../api/login'; + interface State { loginInfo: LoginEntity; } interface Props { -+ history : History; ++ history; } -- export class LoginPage extends React.Component { -+ export const LoginPage = withRouter(class LoginPageInner extends React.Component { +- export class LoginPageContainer extends React.Component { ++ export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { constructor(props) { super(props); - this.state = { loginInfo: createEmptyLogin() }; + + this.state = {loginInfo : createEmptyLogin()} } -+ performLogin() { -+ if(loginApi.isValidLogin(this.state.loginInfo)) { ++ performLogin = () => { ++ if(isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } -+ updateLoginEntity(loginInfo : LoginEntity) { -+ this.setState({loginInfo: loginInfo}); -+ } public render() { return ( -
    -
    -
    -
    -
    - -
    -
    -
    -
    - ); + + ) } -} +- } ++ }) ``` -- Let's now define the properties that the _form_ child controller will accept +- So far so good, let's recap: + - We will hold the login info in the container state. + - We have a method in the container that will perform the login action. + - All this will be passed down vĆ­a props to the loginPage component. -_./src/pages/login/form.tsx_ +What do we have missing? -```diff -import * as React from "react"; -import { withRouter } from 'react-router-dom'; -+ import {LoginEntity} from '../../model/login'; + - A way to notify when the user has typed on a given input filed (method callback to update the login entity). + -+ interface Props { -+ loginInfo : LoginEntity; -+ updateLoginInfo : (loginInfo : LoginEntity) => void; -+ performLogin : () => void; -+ history; -+ } +How are we going to do this? + - We will map each field name with the property name of the control that will be targeted. + - By doing this, the OnChange event can propagate the change and inform about the field that have + changed (we could use a data attribute if we don't want to messup with name). --export const Form = withRouter((props: Props) => { -+ export const Form = (props: Props) => { -- const login = () => { -- history.push('/pageB'); -- } - - return ( -
    - -
    -
    - -
    -
    - -
    - -
    - -
    - ); -+ } -- }); -``` - -- Let's apply this props in the components and propagate the login change. +> What happens if we have nested property? We can use the full name, then using lodash or ramda helpers +we can access the property name via the string that has _field.subfield_ like format. -_./src/pages/login/form.tsx_ +- Let's get started, first we are going to create a method in the container to update a field value: ```diff - return ( -
    -
    -
    -
    - props.updateLoginInfo({login: e.target.value, password: props.loginInfo.password })} - /> -
    -
    - props.updateLoginInfo({login: props.loginInfo.login, password: e.target.value })} - /> -
    - -
    -
    -
    - ); -``` +export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { + constructor(props) { + super(props); -- Now it's time to update the LoginPage again, including the properties that we have to pass -to the _form_ component + this.state = { loginInfo: createEmptyLogin() } + } -```diff --
    -+ -``` + performLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } -- Congratulations, you already have the example running. It is time to do some refator and clean up. ++ updateLoginField = (name, value) => { ++ this.setState({loginInfo: { ++ ...this.state.loginInfo, ++ [name]: value, ++ }}) ++ } +``` -- First we will extract all the divs in charge of generate a centered container to a new common centeredContainer component. +- Let's pass down this info to the loginPage: -- Create a folder _pages\common_ and a file _centered.tsx_ underneath +_./src/pages/login/loginPage.tsx_ -```javascript +```diff import * as React from "react" +import {Panel, ContentCenter} from '../../common'; +import {Form} from './components/form'; ++ import { LoginEntity } from "../../model/login"; -interface Props { - children? : any; -} - -export const CenteredContainer = (props: Props) => { - return ( -
    -
    -
    -
    - {props.children} -
    -
    -
    -
    - ); -} -``` - -- Add a new _index.ts_ file to the same folder - -```javascript -import {CenteredContainer} from './centered' ++ interface Props { ++ loginInfo: LoginEntity; ++ updateField : (string, any) => void; ++ doLogin : () => void; ++ } -export { - CenteredContainer -} +- export const LoginPage = () => ++ export const LoginPage = (props : Props) => + + + + + ``` -- Now we have a component that can be used to center its content, lets use it in our _loginPage.tsx_, the render function should look like this +- First use the new properties when instantiating _loginPage_ in the _container_: -```javascript -public render() { +```diff + public render() { return ( - -
    - - - ); +- ++ + ) } ``` -> Now you an test the solution, try _npm start_ and if you enter the combination _test_ _test_ it wil navigate to page b. +- Now it's time to pass it down to the form, same approach as with the container -- Time now to do some clean up in the form component, lets start creating a _formField.tsx_ file in the _common_ folder +_./src/pages/login/components/form.tsx_ -```javascript +```diff import * as React from "react" +import { withRouter } from 'react-router-dom'; +import { RouteComponentProps } from 'react-router' +import { LoginEntity } from "../../../model/login"; interface Props { - text : string; - name : string; - type : string; - value?: string; - updateFieldValue : (fieldName: string, fieldValue: any) => void; + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; } -export const FormField = (props: Props) => { +- export const Form = withRouter((props : RouteComponentProps) => { + export const Form = (props : Props) => +- const login = () => { +- history.push('/pageB'); +- } + ++ const onChange = (props : Props) => (e: React.ChangeEvent) => { ++ props.updateField(e.target.name, e.target.value); ++ } - return ( -
    +- return ( + +
    +
    props.updateFieldValue(props.name, e.target.value)} - /> -
    - ); -} + placeholder="E-mail" +- name="email" ++ name="login" + type="text" ++ onChange={onChange(props)} ++ value={props.loginInfo.login} + /> +
    +
    + +
    + + + +- ); +}); + ``` -- And add it to the _index.ts_ in the same folder +- Let's update how we instantiate the form into the loginPage: -```javascript -import {CenteredContainer} from './centered' -import {FormField} from './formField' +```diff +export const LoginPage = (props : Props) => + + +
    + + -export { - CenteredContainer, - FormField -} ``` -- Finally the structure look like this: +- We can debug and check that we get all the wheels and cogs working. +```bash +npm start ``` -. -└── src/ - │ - ā”œā”€ā”€ api/ - │ └── login.ts - ā”œā”€ā”€ model/ - │ └── login.ts - ā”œā”€ā”€ pages/ - │ ā”œā”€ā”€ login/ - │ │ ā”œā”€ā”€ form.tsx - │ │ ā”œā”€ā”€ header.tsx - │ │ └── loginPage.tsx - │ ā”œā”€ā”€ common/ - │ │ ā”œā”€ā”€ centered.tsx - │ │ ā”œā”€ā”€ formField.tsx - │ │ └── index.ts - │ │ ā”œā”€ā”€ index.ts - │ └── b/ - │ ā”œā”€ā”€ index.ts - │ └── pageB.tsx - ā”œā”€ā”€ restApi/ - │ └── login.ts - ā”œā”€ā”€ app.tsx - ā”œā”€ā”€ index.html - └── main.tsx - -``` +> One refinement... this looks like we could wrap all this into a common input and use it +in any form saving some lines of code and hiding complexity, let's go for that: -- As you can see in the code above, now we have a component that can be used -to define any input field within a form and which will inform of the value changes -to the parent component. Time to use it! +- Time for the password field -```javascript -import * as React from "react" -import {hashHistory} from 'react-router' -import {LoginEntity} from '../../model/login'; -import {FormField} from '../common/formField'; +_./src/pages/login/components/form.tsx_ -interface Props { - loginInfo : LoginEntity; - updateLoginInfo : (loginInfo : LoginEntity) => void; - performLogin : () => void; -} +```diff + +``` - function updateFieldValue(fieldName: string, fieldValue: any){ - const newLoginEntity = this.props.loginInfo; - newLoginEntity[fieldName] = fieldValue; - this.props.updateLoginInfo(newLoginEntity); - } +- Let's give a try -export const Form = () => { - return ( -
    - -
    - - - { this.props.performLogin() } } - /> -
    - -
    - ); -} ``` -- Pay attention to the new _updateFieldValue_ function which will be in charge of -receive changes in all the fields within the form and pass it to the parent component as a new LoginEntity +npm start +``` + +> And form validation? There are several libraries available, one that we had created in lemoncode +is lc-form-validation we will create a sample including this lib to validate the login form +(required fields) -- Pending to implement (easy and discussion): add a red label indicating that login failed. +> As an excercise add a react toaster to notify when the login fails. \ No newline at end of file diff --git a/15 LoginForm/src/common/content-center/content-center.tsx b/15 LoginForm/src/common/content-center/content-center.tsx index 9860aaf..f615709 100644 --- a/15 LoginForm/src/common/content-center/content-center.tsx +++ b/15 LoginForm/src/common/content-center/content-center.tsx @@ -3,7 +3,7 @@ import * as React from "react" export const ContentCenter : React.StatelessComponent = (props) =>
    -
    +
    {props.children}
    diff --git a/15 LoginForm/src/common/panel/components/body.tsx b/15 LoginForm/src/common/panel/components/body.tsx index 63e0e2e..edb9d76 100644 --- a/15 LoginForm/src/common/panel/components/body.tsx +++ b/15 LoginForm/src/common/panel/components/body.tsx @@ -4,6 +4,6 @@ interface Props { } export const Body : React.StatelessComponent = (props) => -
    +
  • {props.children} -
  • \ No newline at end of file + \ No newline at end of file diff --git a/15 LoginForm/src/common/panel/components/header.tsx b/15 LoginForm/src/common/panel/components/header.tsx index bedde66..d53ea05 100644 --- a/15 LoginForm/src/common/panel/components/header.tsx +++ b/15 LoginForm/src/common/panel/components/header.tsx @@ -5,6 +5,6 @@ interface Props { } export const Header = (props : Props) => -
  • +

    {props.title}

    -
  • \ No newline at end of file +
    \ No newline at end of file diff --git a/15 LoginForm/src/common/panel/panel.tsx b/15 LoginForm/src/common/panel/panel.tsx index 5b693d0..c126f25 100644 --- a/15 LoginForm/src/common/panel/panel.tsx +++ b/15 LoginForm/src/common/panel/panel.tsx @@ -1,17 +1,17 @@ import * as React from "react" -import {Body} from './components/body'; -import {Header} from './components/header'; +import { Body } from './components/body'; +import { Header } from './components/header'; interface Props { - title : string; + title: string; } -export const Panel : React.StatelessComponent = (props) => -
    -
      -
      - - {props.children} - -
    -
    \ No newline at end of file +export const Panel: React.StatelessComponent = (props) => +
    +
    +
      + + {props.children} + +
    +
    \ No newline at end of file diff --git a/15 LoginForm/src/main.tsx b/15 LoginForm/src/main.tsx index 51ff8e3..e2f118f 100644 --- a/15 LoginForm/src/main.tsx +++ b/15 LoginForm/src/main.tsx @@ -1,13 +1,13 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { HashRouter, Switch, Route } from 'react-router-dom'; -import { LoginPage } from './pages/login'; +import { LoginPageContainer } from './pages/login'; import { PageB } from './pages/b'; ReactDOM.render( - + diff --git a/15 LoginForm/src/pages/login/components/form.tsx b/15 LoginForm/src/pages/login/components/form.tsx index 95de0d8..2516ba6 100644 --- a/15 LoginForm/src/pages/login/components/form.tsx +++ b/15 LoginForm/src/pages/login/components/form.tsx @@ -1,22 +1,41 @@ import * as React from "react" import { withRouter } from 'react-router-dom'; +import { RouteComponentProps } from 'react-router' +import { LoginEntity } from "../../../model/login"; -export const Form = withRouter(({history}) => { - const login = () => { - history.push('/pageB'); - } - - return ( + +interface Props { + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; +} + +const onChange = (props : Props) => (e: React.ChangeEvent) => { + props.updateField(e.target.name, e.target.value); +} + +export const Form = (props : Props) =>
    - +
    - +
    - +
    - ); -}); diff --git a/15 LoginForm/src/pages/login/index.ts b/15 LoginForm/src/pages/login/index.ts index 50d85bb..52680f1 100644 --- a/15 LoginForm/src/pages/login/index.ts +++ b/15 LoginForm/src/pages/login/index.ts @@ -1 +1 @@ -export {LoginPage} from './loginPage'; \ No newline at end of file +export {LoginPageContainer} from './loginPageContainer'; \ No newline at end of file diff --git a/15 LoginForm/src/pages/login/loginPage.tsx b/15 LoginForm/src/pages/login/loginPage.tsx index 3e16ba9..f07bed1 100644 --- a/15 LoginForm/src/pages/login/loginPage.tsx +++ b/15 LoginForm/src/pages/login/loginPage.tsx @@ -1,11 +1,24 @@ import * as React from "react" -import {Panel, ContentCenter} from '../../common'; -import {Form} from './components/form'; +import { Panel, ContentCenter } from '../../common'; +import { Form } from './components/form'; +import { LoginEntity } from "../../model/login"; +import {ErrorBoundary} from '../utils/error-boundaries' -export const LoginPage = () => +interface Props { + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; +} + + +export const LoginPage = (props : Props) => -
    + + + diff --git a/15 LoginForm/src/pages/login/loginPageContainer.tsx b/15 LoginForm/src/pages/login/loginPageContainer.tsx index 61cf1be..047673c 100644 --- a/15 LoginForm/src/pages/login/loginPageContainer.tsx +++ b/15 LoginForm/src/pages/login/loginPageContainer.tsx @@ -1,6 +1,8 @@ import * as React from "react" -import {LoginPage} from './loginPage'; -import {LoginEntity, createEmptyLogin} from '../../model/login'; +import { LoginPage } from './loginPage'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { withRouter } from 'react-router-dom'; +import { isValidLogin } from '../../api/login'; interface State { loginInfo: LoginEntity; @@ -10,17 +12,33 @@ interface Props { history; } -export class LoginPageContainer extends React.Component { +export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { constructor(props) { super(props); - this.state = {loginInfo : createEmptyLogin()} + this.state = { loginInfo: createEmptyLogin() } + } + + performLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } + + updateLoginField = (name, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + }}) } public render() { return ( - + ) } - -} +}) diff --git a/15 LoginForm/src/pages/utils/error-boundaries.tsx b/15 LoginForm/src/pages/utils/error-boundaries.tsx new file mode 100644 index 0000000..d34e11e --- /dev/null +++ b/15 LoginForm/src/pages/utils/error-boundaries.tsx @@ -0,0 +1,40 @@ +import * as React from "react" + +interface State { + error : any, + errorInfo : any; +} + +export class ErrorBoundary extends React.Component<{}, State> { + constructor(props) { + super(props); + this.state = { error: null, errorInfo: null }; + } + + componentDidCatch(error, errorInfo) { + // Catch errors in any components below and re-render with error message + this.setState({ + error: error, + errorInfo: errorInfo + }) + // You can also log error messages to an error reporting service here + } + + render() { + if (this.state.errorInfo) { + // Error path + return ( +
    +

    Something went wrong.

    +
    + {this.state.error && this.state.error.toString()} +
    + {this.state.errorInfo.componentStack} +
    +
    + ); + } + // Normally, just render children + return this.props.children; + } +} \ No newline at end of file From 11f9eb2da3b0097c85ed841f3cbc21e3f9c73b7b Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Fri, 20 Apr 2018 00:33:57 +0200 Subject: [PATCH 018/180] added one more challenge --- 15 LoginForm/readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/15 LoginForm/readme.md b/15 LoginForm/readme.md index af11ee2..0a08762 100644 --- a/15 LoginForm/readme.md +++ b/15 LoginForm/readme.md @@ -733,4 +733,6 @@ npm start is lc-form-validation we will create a sample including this lib to validate the login form (required fields) -> As an excercise add a react toaster to notify when the login fails. \ No newline at end of file +> As an excercise add a react toaster to notify when the login fails. + +> One more excercise port this layout to flexbox and new CSS standards. \ No newline at end of file From 16fd3f4a4312fc8bf6d0bac33eab663adf99749c Mon Sep 17 00:00:00 2001 From: Irene Date: Fri, 20 Apr 2018 08:39:15 +0200 Subject: [PATCH 019/180] add readme_es in 04 Callback --- 04 Callback/readme_es.md | 134 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 04 Callback/readme_es.md diff --git a/04 Callback/readme_es.md b/04 Callback/readme_es.md new file mode 100644 index 0000000..26b35b8 --- /dev/null +++ b/04 Callback/readme_es.md @@ -0,0 +1,134 @@ +# 04 Callback + State + +En este ejemplo vamos a refactorizar el ejemplo **03 state**. + +Actualizaremos la propiedad del nombre solo cuando el usuario haga click en el botón _cambiar_ , simplificaremos el evento también. + +Obviamente, partiremos del ejemplo **03 State** como punto de partida. + +Pasos resumidos: + +- Añadir un botón al componente _EditName_ y un controlador para éste. +- Enviar solo el nombre cuando el usuario haga click en el botón. +- Actualizar el componente _app_ para manejar el nuevo evento simplificado. + +## Prerrequisitos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0) si no lo tenemos ya instalado. +> Verificar que estÔs corriendo al menos con la versión de node 6.x.x con `node -v` y `npm -v` en la terminal/consola. Versiones mÔs antgiguas pueden producir errores. + +## Pasos para construirlo + +- Copiar el contenido de la carpeta `03 State` en una carpeta vacía de ejemplo y hacer ésta tu carpeta actual. +- Instalar los paquetes npm descritos en el `package.json` y verificar que funciona_ + +```bash +npm install +``` + +- Como vamos a utilizar un controlador interno, vamos a transformar `NameEditComponent` de un componente sin estado a un componente de clase, luego añadiremos algún refactor en el nombre. + +El fichero `nameEdit.tsx` debería parecerse a: + +_nameEdit.tsx_ + +```diff +import * as React from 'react'; +import {Fragment} from 'react'; + + +interface Props { +- userName : string; +- onChange : (event) => void; ++ initialUserName: string; ++ onNameUpdated: (newName: string) => any; +} + ++ interface State { ++ editingName: string; ++ } + + +- export const NameEditComponent = (props : Props) => { +- return ( +- +- +- +- +- ); +-} + ++ export class NameEditComponent extends React.Component { ++ ++ constructor(props: Props) { ++ super(props); ++ // Watch out what would happen if we get this user name via an AJAX callback ++ // you will find a different implementatin on 05 sample ++ this.state = {editingName: this.props.initialUserName}; ++ } ++ ++ onChange = (event) => { ++ this.setState({editingName: event.target.value} as State); ++ } ++ ++ onNameSubmit = (event: any): any => { ++ this.props.onNameUpdated(this.state.editingName); ++ } ++ ++ public render() { ++ return ( ++
    ++ ++ ++ ++
    ++ ); ++ } ++ } +``` + +- Vamos a conectar esto en el fichero app.tsx + +_./src/app.tsx_ + +```diff +export class App extends React.Component { + constructor(props : Props) { + super(props); + + this.state = {userName: 'defaultUserName'}; + } + +- setUsernameState = (event) => { +- this.setState({userName : event.target.value}) +- } + ++ setUsernameState = (newName: string) => { ++ this.setState({userName: newName}); ++ } + + public render() { + return ( + + +- ++ + + ); + } +} +``` + +Ahora vamos a limpiar el evento, fuertemente tipado y simplificado. + +- Vamos a intentarlo: + + ```bash + npm start + ``` + +- Luegom cargamos http://localhost:8080/ en el navegador y veremos la salida. + +Ahora, el saludo solo cambia cuando el usuario hace click en el botón de cambiar. + +> Qué ocurre si simulamos una llamada AJAX, vamos a colocar en la aplicación en componentWillMount un timeout y establecemos el valor del nombre. From 13204a2cb515449be9890dca6bbeb78102683f82 Mon Sep 17 00:00:00 2001 From: Irene Date: Fri, 20 Apr 2018 08:40:41 +0200 Subject: [PATCH 020/180] add readme_es in 04 Callback --- 04 Callback/readme_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/04 Callback/readme_es.md b/04 Callback/readme_es.md index 26b35b8..c54af41 100644 --- a/04 Callback/readme_es.md +++ b/04 Callback/readme_es.md @@ -127,7 +127,7 @@ Ahora vamos a limpiar el evento, fuertemente tipado y simplificado. npm start ``` -- Luegom cargamos http://localhost:8080/ en el navegador y veremos la salida. +- Luego cargamos http://localhost:8080/ en el navegador y veremos la salida. Ahora, el saludo solo cambia cuando el usuario hace click en el botón de cambiar. From 0b1b65507f7da594c850cfb36243dad72d3d6448 Mon Sep 17 00:00:00 2001 From: Braulio Date: Fri, 20 Apr 2018 12:53:01 +0200 Subject: [PATCH 021/180] sample working pending form validation --- 16 Validation/.babelrc | 10 + 16 Validation/package.json | 37 +++ 16 Validation/readme.md | 272 ++++++++++++++++++ 16 Validation/src/api/login.ts | 6 + .../common/content-center/content-center.tsx | 10 + .../src/common/forms/input/input.tsx | 45 +++ 16 Validation/src/common/index.ts | 2 + .../src/common/panel/components/body.tsx | 9 + .../src/common/panel/components/header.tsx | 10 + 16 Validation/src/common/panel/index.ts | 1 + 16 Validation/src/common/panel/panel.tsx | 17 ++ 16 Validation/src/index.html | 11 + 16 Validation/src/main.tsx | 15 + 16 Validation/src/model/login.ts | 9 + 16 Validation/src/pages/b/index.ts | 1 + 16 Validation/src/pages/b/pageB.tsx | 12 + .../src/pages/login/components/form.tsx | 42 +++ 16 Validation/src/pages/login/index.ts | 1 + 16 Validation/src/pages/login/loginPage.tsx | 26 ++ .../src/pages/login/loginPageContainer.tsx | 61 ++++ 16 Validation/src/pages/login/validation.ts | 16 ++ 16 Validation/src/pages/login/viewmodel.ts | 11 + 16 Validation/src/utils/error-boundaries.tsx | 40 +++ 16 Validation/tsconfig.json | 17 ++ 16 Validation/webpack.config.js | 86 ++++++ 25 files changed, 767 insertions(+) create mode 100644 16 Validation/.babelrc create mode 100644 16 Validation/package.json create mode 100644 16 Validation/readme.md create mode 100644 16 Validation/src/api/login.ts create mode 100644 16 Validation/src/common/content-center/content-center.tsx create mode 100644 16 Validation/src/common/forms/input/input.tsx create mode 100644 16 Validation/src/common/index.ts create mode 100644 16 Validation/src/common/panel/components/body.tsx create mode 100644 16 Validation/src/common/panel/components/header.tsx create mode 100644 16 Validation/src/common/panel/index.ts create mode 100644 16 Validation/src/common/panel/panel.tsx create mode 100644 16 Validation/src/index.html create mode 100644 16 Validation/src/main.tsx create mode 100644 16 Validation/src/model/login.ts create mode 100644 16 Validation/src/pages/b/index.ts create mode 100644 16 Validation/src/pages/b/pageB.tsx create mode 100644 16 Validation/src/pages/login/components/form.tsx create mode 100644 16 Validation/src/pages/login/index.ts create mode 100644 16 Validation/src/pages/login/loginPage.tsx create mode 100644 16 Validation/src/pages/login/loginPageContainer.tsx create mode 100644 16 Validation/src/pages/login/validation.ts create mode 100644 16 Validation/src/pages/login/viewmodel.ts create mode 100644 16 Validation/src/utils/error-boundaries.tsx create mode 100644 16 Validation/tsconfig.json create mode 100644 16 Validation/webpack.config.js diff --git a/16 Validation/.babelrc b/16 Validation/.babelrc new file mode 100644 index 0000000..911d8c1 --- /dev/null +++ b/16 Validation/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "env", + { + "modules": false + } + ] + ] +} \ No newline at end of file diff --git a/16 Validation/package.json b/16 Validation/package.json new file mode 100644 index 0000000..ef82934 --- /dev/null +++ b/16 Validation/package.json @@ -0,0 +1,37 @@ +{ + "name": "helloworld-react", + "version": "1.0.0", + "description": "State basics.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server", + "build": "webpack" + }, + "author": "Lemoncode and Front End Master Students", + "license": "MIT", + "dependencies": { + "bootstrap": "~4.0.0", + "lc-form-validation": "^1.0.0", + "react": "~16.2.0", + "react-dom": "~16.2.0", + "react-router-dom": "^4.2.2" + }, + "devDependencies": { + "@types/history": "^4.6.2", + "@types/react-router-dom": "^4.2.3", + "@types/react": "~16.0.36", + "@types/react-dom": "~16.0.3", + "babel-core": "^6.26.0", + "babel-preset-env": "^1.6.1", + "awesome-typescript-loader": "^3.4.1", + "css-loader": "~0.28.9", + "extract-text-webpack-plugin": "^3.0.2", + "file-loader": "~1.1.6", + "html-webpack-plugin": "~2.30.1", + "style-loader": "~0.20.1", + "typescript": "~2.7.1", + "url-loader": "~0.6.2", + "webpack": "~3.10.0", + "webpack-dev-server": "^2.11.1" + } +} diff --git a/16 Validation/readme.md b/16 Validation/readme.md new file mode 100644 index 0000000..ec7abab --- /dev/null +++ b/16 Validation/readme.md @@ -0,0 +1,272 @@ +# 15 Login form Validation + +Let's add validation support to this form. + +For this we will use lc-form-validation library + +Summary steps: + +- Install lc-form-validation library. +- Refactor input component to a common component and include error validation info. +- Let's define the validation for the form. +- Let's hook it. + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Copy the content from _15 React Form_ and execute _npm install_. + +```bash +npm install +``` + +- Let's install the library (it includes already the typings). + +```bash +npm install lc-form-validation --save-dev +``` + +- To avoid having too much repeated code let's move to common an input component, including it's +label plus validation text. + +_./common/forms/input/input.tsx_ + +```tsx +import * as React from "react"; + +interface Props { + name: string; + label: string; + onChange: any; + onBlur?: any; + placeholder?: string; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text' +} + +const buildWrapperClass = (error : string) : string => + "form-group" + ( + (props.error && props.error.length > 0) ? + "has-error" : + "" + ); + + +export const Input : React.StatelessComponent = (props) => +
    + +
    + +
    {props.error}
    +
    +
    + +Input.defaultProps = defaultProps; +``` + +- Now let's define a basic validation for the form, we want to ensure both fields are informed. + +_./src/pages/login/components_ + +```typescript +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const dataValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const dataValidation = createFormValidation(dataValidationConstraints); +``` + +- Let's create now a class to hold the dataFormErrors. + +_./src/login/viewmodel.ts_ + +```typescript +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} +``` + +- Now let's go for the component side. + +- First let's add the dataFormErrors to the state of the component. + +_./src/pages/login/loginPageContainer.tsx_ + +```diff +import { isValidLogin } from '../../api/login'; ++ import {LoginFormErrors} from './viewmodel.ts'; + +interface State { + loginInfo: LoginEntity, ++ loginFormErrors : LoginFormErrors; +} +``` + +- Now let's update the onUpdate callback to include the validation. + +_./src/pages/login/loginPageContainer.tsx_ + +// Adding imports +```diff +import { isValidLogin } from '../../api/login'; ++ import {dataValidation} from './validation' +``` + +_./src/pages/login/loginPageContainer.tsx_ + +```diff ++ // This could be simplified and made in one go + updateLoginField = (name, value) => { ++ dataValidation.validateField(this.state.loginInfo, name, value) ++ .then((fieldValidationResult) => { + ++ this.setState({loginFormErrors: { ++ ...this.state.loginFormErrors, ++ [name]: fieldValidationResult, ++ }); + + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + }); ++ }); + } +``` + +- We need to pass down dataFormErrors + +_./src/loginPageContainer.tsx_ + +```diff + public render() { + return ( + + ) + } +``` + +- Now we need to define the property in the loginPage component. + +_./src/loginPage.tsx_ + +```diff +import { LoginEntity } from "../../model/login"; ++ import {LoginFormErrors} from './viewmodel'; + +interface Props { + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; ++ loginFormErrors : LoginFormErrors; +} +``` + +- And let's define it in the form component. + +```diff +import { LoginEntity } from "../../../model/login"; ++ import {LoginFormErrors} from '../viewmodel'; ++ import { Input } from '../../../common/forms/input/input'; + +interface Props { + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; ++ loginFormErrors : LoginFormErrors; +} +``` + +- Now let's update our components to match the new input component. + +```diff +export const Form = (props : Props) => + +
    +-
    +- ++ +-
    +-
    +- ++ +
    + +
    + +``` + +- Let's give a try. + +```bash +npm start +``` + +- And let's add an alert (Excercise and a notification) when the user clicks and +the form has not fields required. + +> Excercise add property styling using CSS Modules nad proper react alert. \ No newline at end of file diff --git a/16 Validation/src/api/login.ts b/16 Validation/src/api/login.ts new file mode 100644 index 0000000..d542d9e --- /dev/null +++ b/16 Validation/src/api/login.ts @@ -0,0 +1,6 @@ +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); + diff --git a/16 Validation/src/common/content-center/content-center.tsx b/16 Validation/src/common/content-center/content-center.tsx new file mode 100644 index 0000000..f615709 --- /dev/null +++ b/16 Validation/src/common/content-center/content-center.tsx @@ -0,0 +1,10 @@ +import * as React from "react" + +export const ContentCenter : React.StatelessComponent = (props) => +
    +
    +
    + {props.children} +
    +
    +
    \ No newline at end of file diff --git a/16 Validation/src/common/forms/input/input.tsx b/16 Validation/src/common/forms/input/input.tsx new file mode 100644 index 0000000..4497963 --- /dev/null +++ b/16 Validation/src/common/forms/input/input.tsx @@ -0,0 +1,45 @@ +import * as React from "react"; +import {ErrorBoundary} from '../../../utils/error-boundaries' +interface Props { + name: string; + label: string; + onChange: any; + onBlur?: any; + placeholder?: string; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text' +} + +const buildWrapperClass = (error : string) : string => + "form-group" + ( + (error && error.length > 0) ? + "has-error" : + "" + ); + + +export const Input : React.StatelessComponent = (props) => + +
    + +
    + +
    {props.error}
    +
    +
    +
    + +Input.defaultProps = defaultProps; + diff --git a/16 Validation/src/common/index.ts b/16 Validation/src/common/index.ts new file mode 100644 index 0000000..bf0cd1f --- /dev/null +++ b/16 Validation/src/common/index.ts @@ -0,0 +1,2 @@ +export {Panel} from './panel'; +export {ContentCenter} from './content-center/content-center'; \ No newline at end of file diff --git a/16 Validation/src/common/panel/components/body.tsx b/16 Validation/src/common/panel/components/body.tsx new file mode 100644 index 0000000..edb9d76 --- /dev/null +++ b/16 Validation/src/common/panel/components/body.tsx @@ -0,0 +1,9 @@ +import * as React from "react" + +interface Props { +} + +export const Body : React.StatelessComponent = (props) => +
  • + {props.children} +
  • \ No newline at end of file diff --git a/16 Validation/src/common/panel/components/header.tsx b/16 Validation/src/common/panel/components/header.tsx new file mode 100644 index 0000000..d53ea05 --- /dev/null +++ b/16 Validation/src/common/panel/components/header.tsx @@ -0,0 +1,10 @@ +import * as React from "react" + +interface Props { + title : string; +} + +export const Header = (props : Props) => +
    +

    {props.title}

    +
    \ No newline at end of file diff --git a/16 Validation/src/common/panel/index.ts b/16 Validation/src/common/panel/index.ts new file mode 100644 index 0000000..5e5c164 --- /dev/null +++ b/16 Validation/src/common/panel/index.ts @@ -0,0 +1 @@ +export {Panel} from './panel'; \ No newline at end of file diff --git a/16 Validation/src/common/panel/panel.tsx b/16 Validation/src/common/panel/panel.tsx new file mode 100644 index 0000000..c126f25 --- /dev/null +++ b/16 Validation/src/common/panel/panel.tsx @@ -0,0 +1,17 @@ +import * as React from "react" +import { Body } from './components/body'; +import { Header } from './components/header'; + +interface Props { + title: string; +} + +export const Panel: React.StatelessComponent = (props) => +
    +
    +
      + + {props.children} + +
    +
    \ No newline at end of file diff --git a/16 Validation/src/index.html b/16 Validation/src/index.html new file mode 100644 index 0000000..d972c34 --- /dev/null +++ b/16 Validation/src/index.html @@ -0,0 +1,11 @@ + + + + + Sample 14: ReactRouter + + +

    Sample 14: ReactRouter

    +
    + + diff --git a/16 Validation/src/main.tsx b/16 Validation/src/main.tsx new file mode 100644 index 0000000..e2f118f --- /dev/null +++ b/16 Validation/src/main.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPageContainer } from './pages/login'; +import { PageB } from './pages/b'; + +ReactDOM.render( + + + + + + + , document.getElementById('root') +); diff --git a/16 Validation/src/model/login.ts b/16 Validation/src/model/login.ts new file mode 100644 index 0000000..a9da6e2 --- /dev/null +++ b/16 Validation/src/model/login.ts @@ -0,0 +1,9 @@ +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); \ No newline at end of file diff --git a/16 Validation/src/pages/b/index.ts b/16 Validation/src/pages/b/index.ts new file mode 100644 index 0000000..8c7e132 --- /dev/null +++ b/16 Validation/src/pages/b/index.ts @@ -0,0 +1 @@ +export {PageB} from './pageB' diff --git a/16 Validation/src/pages/b/pageB.tsx b/16 Validation/src/pages/b/pageB.tsx new file mode 100644 index 0000000..259758c --- /dev/null +++ b/16 Validation/src/pages/b/pageB.tsx @@ -0,0 +1,12 @@ +import * as React from "react" +import { Link } from 'react-router-dom'; + +export const PageB = () => { + return ( +
    +

    Hello from page B

    +
    + Navigate to Login +
    + ) +} diff --git a/16 Validation/src/pages/login/components/form.tsx b/16 Validation/src/pages/login/components/form.tsx new file mode 100644 index 0000000..8ff15b9 --- /dev/null +++ b/16 Validation/src/pages/login/components/form.tsx @@ -0,0 +1,42 @@ +import * as React from "react" +import { withRouter } from 'react-router-dom'; +import { RouteComponentProps } from 'react-router' +import { LoginEntity } from "../../../model/login"; +import { LoginFormErrors } from '../viewmodel'; +import { Input } from '../../../common/forms/input/input'; + +interface Props { + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; + loginFormErrors : LoginFormErrors; +} + +const onChange = (props : Props) => (e: React.ChangeEvent) => { + props.updateField(e.target.name, e.target.value); +} + +export const Form = (props : Props) => +
    +
    + + + + +
    +
    diff --git a/16 Validation/src/pages/login/index.ts b/16 Validation/src/pages/login/index.ts new file mode 100644 index 0000000..52680f1 --- /dev/null +++ b/16 Validation/src/pages/login/index.ts @@ -0,0 +1 @@ +export {LoginPageContainer} from './loginPageContainer'; \ No newline at end of file diff --git a/16 Validation/src/pages/login/loginPage.tsx b/16 Validation/src/pages/login/loginPage.tsx new file mode 100644 index 0000000..3a30e46 --- /dev/null +++ b/16 Validation/src/pages/login/loginPage.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import { Panel, ContentCenter } from '../../common'; +import { Form } from './components/form'; +import { LoginEntity } from "../../model/login"; +import {ErrorBoundary} from '../utils/error-boundaries'; +import {LoginFormErrors} from './viewmodel'; + +interface Props { + loginInfo: LoginEntity; + updateField: (string, any) => void; + doLogin: () => void; + loginFormErrors : LoginFormErrors; +} + + +export const LoginPage = (props : Props) => + + + +
    + + + + diff --git a/16 Validation/src/pages/login/loginPageContainer.tsx b/16 Validation/src/pages/login/loginPageContainer.tsx new file mode 100644 index 0000000..70c6d00 --- /dev/null +++ b/16 Validation/src/pages/login/loginPageContainer.tsx @@ -0,0 +1,61 @@ +import * as React from "react" +import { LoginPage } from './loginPage'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { withRouter } from 'react-router-dom'; +import { isValidLogin } from '../../api/login'; +import { dataValidation } from './validation' +import {LoginFormErrors, createEmptyDataFormErrors} from './viewmodel'; + +interface State { + loginInfo: LoginEntity; + loginFormErrors : LoginFormErrors; +} + +interface Props { + history; +} + +export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { + constructor(props) { + super(props); + + this.state = { loginInfo: createEmptyLogin(), loginFormErrors: createEmptyDataFormErrors()} + } + + performLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } + + updateLoginField = (name, value) => { + dataValidation.validateField(this.state.loginInfo, name, value) + .then((fieldValidationResult) => { + + this.setState({ + loginFormErrors: { + ...this.state.loginFormErrors, + [name]: fieldValidationResult, + } + }); + + this.setState({ + loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + }); + } + + public render() { + return ( + + ) + } +}) diff --git a/16 Validation/src/pages/login/validation.ts b/16 Validation/src/pages/login/validation.ts new file mode 100644 index 0000000..49424fd --- /dev/null +++ b/16 Validation/src/pages/login/validation.ts @@ -0,0 +1,16 @@ +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const dataValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const dataValidation = createFormValidation(dataValidationConstraints); \ No newline at end of file diff --git a/16 Validation/src/pages/login/viewmodel.ts b/16 Validation/src/pages/login/viewmodel.ts new file mode 100644 index 0000000..ed9fbbe --- /dev/null +++ b/16 Validation/src/pages/login/viewmodel.ts @@ -0,0 +1,11 @@ +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} + +export const createEmptyDataFormErrors = (): LoginFormErrors => ({ + login: new FieldValidationResult(), + password: new FieldValidationResult(), +}); diff --git a/16 Validation/src/utils/error-boundaries.tsx b/16 Validation/src/utils/error-boundaries.tsx new file mode 100644 index 0000000..d34e11e --- /dev/null +++ b/16 Validation/src/utils/error-boundaries.tsx @@ -0,0 +1,40 @@ +import * as React from "react" + +interface State { + error : any, + errorInfo : any; +} + +export class ErrorBoundary extends React.Component<{}, State> { + constructor(props) { + super(props); + this.state = { error: null, errorInfo: null }; + } + + componentDidCatch(error, errorInfo) { + // Catch errors in any components below and re-render with error message + this.setState({ + error: error, + errorInfo: errorInfo + }) + // You can also log error messages to an error reporting service here + } + + render() { + if (this.state.errorInfo) { + // Error path + return ( +
    +

    Something went wrong.

    +
    + {this.state.error && this.state.error.toString()} +
    + {this.state.errorInfo.componentStack} +
    +
    + ); + } + // Normally, just render children + return this.props.children; + } +} \ No newline at end of file diff --git a/16 Validation/tsconfig.json b/16 Validation/tsconfig.json new file mode 100644 index 0000000..ba8b3b7 --- /dev/null +++ b/16 Validation/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/16 Validation/webpack.config.js b/16 Validation/webpack.config.js new file mode 100644 index 0000000..9b58da0 --- /dev/null +++ b/16 Validation/webpack.config.js @@ -0,0 +1,86 @@ +var path = require('path'); +var webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + + entry: [ + './main.tsx', + '../node_modules/bootstrap/dist/css/bootstrap.css' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + + devtool: 'source-map', + + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + }, + }, + { + test: /\.css$/, + include: /node_modules/, + loader: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: { + loader: 'css-loader', + }, + }), + }, + // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack + // Using here url-loader and file-loader + { + test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url-loader?limit=10000&mimetype=application/font-woff' + }, + { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + }, + { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url-loader?limit=10000&mimetype=image/svg+xml' + }, + { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + loader: 'file-loader' + }, + ] + }, + plugins: [ + // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', // Name of file in ./dist/ + template: 'index.html', // Name of template in ./src + hash: true + }), + new ExtractTextPlugin({ + filename: '[chunkhash].[name].css', + disable: false, + allChunks: true, + }), + ] +} From c791203625a9c8f5fa1dc2860c3127b9e6235d79 Mon Sep 17 00:00:00 2001 From: Braulio Date: Fri, 20 Apr 2018 13:03:14 +0200 Subject: [PATCH 022/180] form validation completed --- 16 Validation/readme.md | 19 ++++++++++++++++++- .../src/pages/login/loginPageContainer.tsx | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/16 Validation/readme.md b/16 Validation/readme.md index ec7abab..e0015ec 100644 --- a/16 Validation/readme.md +++ b/16 Validation/readme.md @@ -267,6 +267,23 @@ npm start ``` - And let's add an alert (Excercise and a notification) when the user clicks and -the form has not fields required. +the form all the fields are valid. + +_./src/pages/login/loginPageContainer.tsx_ + +```diff + performLogin = () => { ++ dataValidation.validateForm(this.state.loginInfo) ++ .then((FormValidationResult) => { ++ if(FormValidationResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } ++ } else { ++ alert('error, review the fields'); ++ } ++ }) + } +``` > Excercise add property styling using CSS Modules nad proper react alert. \ No newline at end of file diff --git a/16 Validation/src/pages/login/loginPageContainer.tsx b/16 Validation/src/pages/login/loginPageContainer.tsx index 70c6d00..837f53c 100644 --- a/16 Validation/src/pages/login/loginPageContainer.tsx +++ b/16 Validation/src/pages/login/loginPageContainer.tsx @@ -5,6 +5,7 @@ import { withRouter } from 'react-router-dom'; import { isValidLogin } from '../../api/login'; import { dataValidation } from './validation' import {LoginFormErrors, createEmptyDataFormErrors} from './viewmodel'; +import { FormValidationResult } from "lc-form-validation"; interface State { loginInfo: LoginEntity; @@ -22,10 +23,18 @@ export const LoginPageContainer = withRouter(class LoginPageContainerInner exten this.state = { loginInfo: createEmptyLogin(), loginFormErrors: createEmptyDataFormErrors()} } + // We could apply clean code here break into two functions performLogin = () => { - if (isValidLogin(this.state.loginInfo)) { - this.props.history.push('/pageB'); - } + dataValidation.validateForm(this.state.loginInfo) + .then((FormValidationResult) => { + if(FormValidationResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } else { + alert('error, review the fields'); + } + }) } updateLoginField = (name, value) => { From a80cb697cc8f68e083d6a9f4f9ec7c354f2d6963 Mon Sep 17 00:00:00 2001 From: Braulio Date: Fri, 20 Apr 2018 13:10:18 +0200 Subject: [PATCH 023/180] updates --- 15 LoginForm/readme.md | 2 +- 15 LoginForm/src/pages/login/components/form.tsx | 2 +- 16 Validation/readme.md | 2 +- 16 Validation/src/pages/login/components/form.tsx | 2 +- 16 Validation/src/pages/login/loginPage.tsx | 5 +---- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/15 LoginForm/readme.md b/15 LoginForm/readme.md index 0a08762..cf24f19 100644 --- a/15 LoginForm/readme.md +++ b/15 LoginForm/readme.md @@ -322,7 +322,7 @@ import * as React from "react"
    - -+ ++
    diff --git a/15 LoginForm/src/pages/login/components/form.tsx b/15 LoginForm/src/pages/login/components/form.tsx index 2516ba6..909a158 100644 --- a/15 LoginForm/src/pages/login/components/form.tsx +++ b/15 LoginForm/src/pages/login/components/form.tsx @@ -36,6 +36,6 @@ export const Form = (props : Props) => value={props.loginInfo.password} />
    - + diff --git a/16 Validation/readme.md b/16 Validation/readme.md index e0015ec..5f74ae7 100644 --- a/16 Validation/readme.md +++ b/16 Validation/readme.md @@ -255,7 +255,7 @@ export const Form = (props : Props) => + type="password" + />
    - + ``` diff --git a/16 Validation/src/pages/login/components/form.tsx b/16 Validation/src/pages/login/components/form.tsx index 8ff15b9..13569c4 100644 --- a/16 Validation/src/pages/login/components/form.tsx +++ b/16 Validation/src/pages/login/components/form.tsx @@ -37,6 +37,6 @@ export const Form = (props : Props) => type="password" /> - + diff --git a/16 Validation/src/pages/login/loginPage.tsx b/16 Validation/src/pages/login/loginPage.tsx index 3a30e46..7cd1767 100644 --- a/16 Validation/src/pages/login/loginPage.tsx +++ b/16 Validation/src/pages/login/loginPage.tsx @@ -2,7 +2,6 @@ import * as React from "react" import { Panel, ContentCenter } from '../../common'; import { Form } from './components/form'; import { LoginEntity } from "../../model/login"; -import {ErrorBoundary} from '../utils/error-boundaries'; import {LoginFormErrors} from './viewmodel'; interface Props { @@ -16,11 +15,9 @@ interface Props { export const LoginPage = (props : Props) => -
    - + /> From 12373442fd39a276e430baec8a5be6f658610170 Mon Sep 17 00:00:00 2001 From: "LAPTOP-LADPESTJ\\usuario" Date: Fri, 20 Apr 2018 23:09:25 +0200 Subject: [PATCH 024/180] #86 reviewed and extended sample 07 Enable --- 07 Enable/readme.md | 80 ++++++++++++++++------ 07 Enable/readme_es.md | 136 +++++++++++++++++++++++++++++++++++++ 07 Enable/src/app.tsx | 1 + 07 Enable/src/nameEdit.tsx | 12 ++-- 4 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 07 Enable/readme_es.md diff --git a/07 Enable/readme.md b/07 Enable/readme.md index 78ff7c9..5fc5f26 100644 --- a/07 Enable/readme.md +++ b/07 Enable/readme.md @@ -1,6 +1,5 @@ # 07 Enable - Let's continue with the update name sample, this time we want to disable the "update" button when the input is empty or when the value hasn't changed. @@ -10,7 +9,6 @@ Summary steps: - Add a condition to disable - ## Prerequisites Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are not already installed on your computer. @@ -19,12 +17,11 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ## Steps to build it -- Copy the content from _06 MoveBacktoStateless_. - -- Let's start by adding a condition to disable the field whenever is empty. Replace only the input tag in _src/nameEdit.tsx_ with the following code: +- Copy the content from _[./../06 MoveBacktoStateless/](./../06 MoveBacktoStateless/)_. -_./src/nameEditComponent.tsx_ +- Let's start by adding a condition to disable the field whenever is empty. Replace only the input tag in _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ with the following code: +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ ```diff
    @@ -40,13 +37,11 @@ _./src/nameEditComponent.tsx_
    ``` - - Now comes the tricky part, detect when the name hasn't changed.
    -First we will add a new property called _userName_ with type `string` in _src/nameEdit.tsx_. This one will hold the last accepted userName. - -_./src/nameEdit.tsx_ +First we will add a new property called _userName_ with type `string` in _[./src/nameEdit.tsx](./src/nameEdit.tsx)_. This one will hold the last accepted userName. - ```diff +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ +```diff interface Props { + userName : string; editingUserName : string; @@ -56,8 +51,9 @@ _./src/nameEdit.tsx_ ``` - We will add to the enable condition one more test, checking if name has changed. -Replace again only the input tag in _src/nameEdit.tsx_ with the following code: +Replace again only the input tag in _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ with the following code: +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ ```diff ``` -- Now we have to feed this property from the parent control (Add `userName={this.state.userName}` to the NameEditComponent in _src/app.tsx_). The `NameEditComponent` should be like: - -_./src/app.tsx_ +- Now we have to feed this property from the parent control (Add `userName={this.state.userName}` to the NameEditComponent in _[./src/app.tsx](./src/app.tsx)_). The `NameEditComponent` should be like: +_[./src/app.tsx](./src/app.tsx)_ ```diff public render() { return ( @@ -88,9 +83,54 @@ _./src/app.tsx_ - Let's give a try - ``` +``` npm start - ``` +``` + +> As an excercise, what if we want to do this more generic? we could have a generic property called enable that could be true or false. - > As an excercise, what if we want to do this more generic? we could have a generic property - called enable that could be true or false. +To do this, we will modify [./src/app.tsx](./src/app.tsx) adding the variable `disable` to the `` component. This variable is **Boolean**, so you need conditions to evaluate it. + +_[./src/app.tsx](./src/app.tsx)_ +```diff + public render() { + return ( + <> + + + + ); + } +``` + +Within the component we define the **props** **disable** as Boolean, together with its conditions that will evaluate it. + +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ +```diff +interface Props { +++ disable: boolean; + userName : string; + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => void; + onNameUpdateRequest : () => void; +} + +export const NameEditComponent = (props : Props) => +
    + + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + + +
    +``` \ No newline at end of file diff --git a/07 Enable/readme_es.md b/07 Enable/readme_es.md new file mode 100644 index 0000000..ee7c7f6 --- /dev/null +++ b/07 Enable/readme_es.md @@ -0,0 +1,136 @@ +# 07 Habilitar + +Continuemos con el ejemplo de nombre de actualización, esta vez queremos desactivar el +botón "actualizar" cuando la entrada estÔ vacía o cuando el valor no ha cambiado. + +Tomaremos una muestra del punto de inicio _06 MoveBacktOStateless_. + +Pasos resumidos: + +- Agregar una condición para deshabilitar + +## Prerrequisitos + +Instale [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si aún no estÔn instalados en su computadora. + +> Verifique que esté ejecutando al menos los nodos v6.x.x y npm 3.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copie el contenido de _[./../06 MoveBacktoStateless/](./../06 MoveBacktoStateless /)_. + +- Comencemos agregando una condición para deshabilitar el campo siempre que esté vacío. Reemplace solo la etiqueta de entrada en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ con el siguiente código: + +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ +```diff +
    + + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + +- ++ +
    +``` + +- Ahora viene la parte difĆ­cil, detectar cuando el nombre no ha cambiado.
    +Primero agregaremos una nueva propiedad llamada `userName` con el tipo `string` en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_. Este tendrÔ el último nombre de usuario aceptado. + +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ +```diff + interface Props { ++ userName : string; + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => any; + onNameUpdateRequest : () => void; + } + ``` + +- Añadiremos a la condición de habilitación una prueba mÔs, verificando si el nombre ha cambiado. +Reemplace nuevamente solo la etiqueta de entrada en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ con el siguiente código: + +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ +```diff + +``` + +- Ahora tenemos que alimentar esta propiedad desde el control principal (Agregar `userName = {this.state.userName}` al `NameEditComponent` en _[./src/app.tsx](./src/app.tsx)_). El `NameEditComponent` debería ser como: + +_[./src/app.tsx](./src/app.tsx)_ +```diff + public render() { + return ( + + + + + ); + } +``` + +- Démosle una oportunidad + +``` +npm start +``` + +> Como ejercicio, ¿y si queremos hacer esto mÔs genérico? podríamos tener una propiedad genérica llamada enable que podría ser verdadera o falsa. + +Para hacer esto, modificaremos [./src/app.tsx](./src/app.tsx) agregando la variable `disable` al componente` `. Esta variable es **booleana**, por lo que necesita condiciones para evaluarla. + +_[./src/app.tsx](./src/app.tsx)_ +```diff + public render() { + return ( + <> + + + + ); + } +``` + +Dentro del componente definimos **props** **disable** como Boolean, junto con sus condiciones que lo evaluarÔn. + +_[./src/nameEdit.tsx](./src/nameEdit.tsx)_ +```diff +interface Props { +++ disable: boolean; + userName : string; + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => void; + onNameUpdateRequest : () => void; +} + +export const NameEditComponent = (props : Props) => +
    + + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + + +
    +``` \ No newline at end of file diff --git a/07 Enable/src/app.tsx b/07 Enable/src/app.tsx index 335bb09..6d10633 100644 --- a/07 Enable/src/app.tsx +++ b/07 Enable/src/app.tsx @@ -32,6 +32,7 @@ export class App extends React.Component { <> void; onNameUpdateRequest : () => void; @@ -15,8 +16,9 @@ export const NameEditComponent = (props : Props) => props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - +
    From 815348c9f0b3e6ff7260f2492bdc1927d8271e41 Mon Sep 17 00:00:00 2001 From: "LAPTOP-LADPESTJ\\usuario" Date: Fri, 20 Apr 2018 23:24:52 +0200 Subject: [PATCH 025/180] #87 updated and extended --- 07 Enable/readme.md | 4 ++-- 07 Enable/readme_es.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/07 Enable/readme.md b/07 Enable/readme.md index 5fc5f26..2e5fe50 100644 --- a/07 Enable/readme.md +++ b/07 Enable/readme.md @@ -3,7 +3,7 @@ Let's continue with the update name sample, this time we want to disable the "update" button when the input is empty or when the value hasn't changed. -We will take a startup point sample _06 MoveBacktOStateless_. +We will take a startup point sample _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. Summary steps: @@ -17,7 +17,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ## Steps to build it -- Copy the content from _[./../06 MoveBacktoStateless/](./../06 MoveBacktoStateless/)_. +- Copy the content from _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. - Let's start by adding a condition to disable the field whenever is empty. Replace only the input tag in _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ with the following code: diff --git a/07 Enable/readme_es.md b/07 Enable/readme_es.md index ee7c7f6..f068b7a 100644 --- a/07 Enable/readme_es.md +++ b/07 Enable/readme_es.md @@ -3,7 +3,7 @@ Continuemos con el ejemplo de nombre de actualización, esta vez queremos desactivar el botón "actualizar" cuando la entrada estÔ vacía o cuando el valor no ha cambiado. -Tomaremos una muestra del punto de inicio _06 MoveBacktOStateless_. +Tomaremos una muestra del punto de inicio _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. Pasos resumidos: @@ -13,11 +13,11 @@ Pasos resumidos: Instale [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si aún no estÔn instalados en su computadora. -> Verifique que esté ejecutando al menos los nodos v6.x.x y npm 3.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. +> Verifique que esté ejecutando al menos los nodos v6.x.x y npm 3.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal/ consola. Las versiones anteriores pueden producir errores. ## Pasos para construirlo -- Copie el contenido de _[./../06 MoveBacktoStateless/](./../06 MoveBacktoStateless /)_. +- Copie el contenido de _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. - Comencemos agregando una condición para deshabilitar el campo siempre que esté vacío. Reemplace solo la etiqueta de entrada en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ con el siguiente código: From 2cfdd691a3cbe1f808deae7ed58dbfa92c89461d Mon Sep 17 00:00:00 2001 From: "LAPTOP-LADPESTJ\\usuario" Date: Fri, 20 Apr 2018 23:26:37 +0200 Subject: [PATCH 026/180] #87 updated and extended --- 07 Enable/readme.md | 4 ++-- 07 Enable/readme_es.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/07 Enable/readme.md b/07 Enable/readme.md index 2e5fe50..746a8b1 100644 --- a/07 Enable/readme.md +++ b/07 Enable/readme.md @@ -3,7 +3,7 @@ Let's continue with the update name sample, this time we want to disable the "update" button when the input is empty or when the value hasn't changed. -We will take a startup point sample _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. +We will take a startup point sample _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. Summary steps: @@ -17,7 +17,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ## Steps to build it -- Copy the content from _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. +- Copy the content from _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. - Let's start by adding a condition to disable the field whenever is empty. Replace only the input tag in _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ with the following code: diff --git a/07 Enable/readme_es.md b/07 Enable/readme_es.md index f068b7a..ffd87cd 100644 --- a/07 Enable/readme_es.md +++ b/07 Enable/readme_es.md @@ -3,7 +3,7 @@ Continuemos con el ejemplo de nombre de actualización, esta vez queremos desactivar el botón "actualizar" cuando la entrada estÔ vacía o cuando el valor no ha cambiado. -Tomaremos una muestra del punto de inicio _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. +Tomaremos una muestra del punto de inicio _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. Pasos resumidos: @@ -17,7 +17,7 @@ Instale [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si aún ## Pasos para construirlo -- Copie el contenido de _[./../06%20MoveBackToStateless/](./../06%20MoveBackToStateless/)_. +- Copie el contenido de _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. - Comencemos agregando una condición para deshabilitar el campo siempre que esté vacío. Reemplace solo la etiqueta de entrada en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ con el siguiente código: From c1c0c18c06d2e13020c374fcdef46707700ea4fb Mon Sep 17 00:00:00 2001 From: Igor Alvarez Date: Sun, 22 Apr 2018 19:51:11 +0200 Subject: [PATCH 027/180] #92 Review 05 Refactor and create Readme_es.md --- 05 Refactor/readme.md | 4 +- 05 Refactor/readme_es.md | 178 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 05 Refactor/readme_es.md diff --git a/05 Refactor/readme.md b/05 Refactor/readme.md index bc2519d..49492f1 100644 --- a/05 Refactor/readme.md +++ b/05 Refactor/readme.md @@ -33,7 +33,7 @@ Constructor update: constructor(props: Props) { super(props); // Watch out what would happen if we get this user name via an AJAX callback - // you will find a different implementatin on 05 sample + // you will find a different implementation on 05 sample - this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; + this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; @@ -54,7 +54,7 @@ Inside the class component - The second idea is to setup two properties, the parent control will hold _userName_ and _editingUsername__, whenever the user clicks on the button to replace the name it will notify the parent control and it will replace the -content of _userName_" with the content from _editingUsername_. If _userName_ gets updated by any other third party (e.g. ajax callback) it will update as well +content of _userName_ with the content from _editingUsername_. If _userName_ gets updated by any other third party (e.g. AJAX callback) it will update as well _editingUsername_. We will take as a starting point sample _04 Callback_: diff --git a/05 Refactor/readme_es.md b/05 Refactor/readme_es.md new file mode 100644 index 0000000..9c7cfc2 --- /dev/null +++ b/05 Refactor/readme_es.md @@ -0,0 +1,178 @@ +# 05 Refactorizar + +En el ejemplo anterior estabamos estableciendo un valor username inicial, ¿que ocurriría si esperasemos que este valor viniera, por ejemplo, de una petición AJAX o si pudiera variar en el tiempo? Lo que ocurriría es que la aproximación actual no funcionaría. + +Podríamos pensar en dos posibles soluciones: + +- La primera idea que podría venirnos a la mentes sería implementar una mezcla: recibimos el actual valor name via props, entonces mantenemos un estado con el valor editable actual... ¿Que desventajas nos encontraríamos? Tendríamos que escuchar el getDerivedStateFromProps (componentWillRecieveProps estÔ obsoleto) para cualquier cambio en el control de nombre de usuario del padre y sustituir nuestro estado, acabaríamos con un control compartido. + +> MÔs información sobre getDerivedStateFromProps: https://medium.com/@baphemot/whats-new-in-react-16-3-d2c9b7b6193b + +Veamos como quedaría (usando el nuevo método estÔtico getDerivedStateFromProps): + +Props e interface: + +```diff +interface Props { + initialUserName: string; + onNameUpdated: (newName: string) => any; +} + +interface State { ++ initialUserName : string, + editingName: string; +} + +Actualización del constructor: + +```diff + constructor(props: Props) { + super(props); + // Comprueba que pasaría si obtenemos este nombre de usuario a través de un callback AJAX + // encontrarÔs una implementación diferente en el ejemplo 05 +- this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; + ++ this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; + } + +Dentro del componente de clase + +```javascript + static getDerivedStateFromProps(nextProps : Props, prevState : State) : Partial { + if(nextProps.initialUserName && + nextProps.initialUserName != prevState.initialUserName) { + return {editingName: nextProps.initialUserName} + } else { + return null; + } + } +``` + +- La segunda idea es preparar dos propiedades, el control padre contendrÔ _userName_ y _editingUsername__, cuando el usuario hace click en el botón para sustituir el nombre se notifica al control padre y reemplazarÔ el contenido de _userName_ con el contenido de _editingUsername_. Si _userName_ es actualizado por cualquier otra tercera parte (por ejemplo, un callback AJAX) también se actualizarÔ _editingUsername_. + +Tomaremos como punto de partida el ejemplo _04 Callback_: + +Pasos resumidos: + +- Actualizar _nameEdit.tsx_ para que solicite el nuevo _editingUsername_, y eliminarlo del estado. + +- Actualizar _app.tsx_ para contener la nueva propiedad de edición en el estado, pasarla al hijo, controlar y realizar la actualización apropiada en el evento callback del control hijo. + +## Prerequisitos + +Instalar [Node.js and npm](https://nodejs.org/es/) si no lo tenemos ya instalado. + +> Verificar que estÔs ejecutando al menos con la versión 6.x.x de node y la versión 3.x.x de npm ejecutando `node -v` y `npm -v` en la ventana de terminal/consola. Versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copiar el contenido de _04 Callback_ y ejecutar `npm install`. + +- Actualizar _nameEdit.tsx_ para que solicite el nuevo _editingUsername_, y eliminarlo del estado. + +_nameEdit.tsx_ + +```diff +import * as React from 'react'; +import {Fragment} from 'react'; + + +interface Props { +- initialUserName: string; +- onNameUpdated: (newName: string) => any; ++ editingUserName : string; ++ onEditingNameUpdated : (newEditingName : string) => void; ++ onNameUpdateRequest : () => void; +} + +-interface State { +- editingName: string; +-} + +-export class NameEditComponent extends React.Component { ++ export class NameEditComponent extends React.Component { + constructor(props: Props) { + super(props); + +- this.state = {editingName: this.props.initialUserName} + } + +- onChange = (event) => { +- this.setState({editingName: event.target.value} as State); +- } + +- onNameSubmit = (event) => { +- this.props.onNameUpdated(this.state.editingName); +- } + ++ onChange = (e: React.ChangeEvent) => { ++ this.props.onEditingNameUpdated((e.target as HTMLInputElement).value); ++ } + + + public render() { + return ( +
    + +- +- ++ ++ +
    + ) + } +} +``` + +- Actualizar _app.tsx_ para contener la nueva propiedad de edición en el estado, pasarla al hijo, controlar y realizar la actualización apropiada en el evento callback del control hijo. + +```diff +import * as React from 'react'; +import {HelloComponent} from './hello'; +import {NameEditComponent} from './nameEdit' + +interface Props { + +} + +interface State { + userName : string; ++ editingUserName : string; +} + +export class App extends React.Component { + constructor(props : Props) { + super(props); + +- this.state = {userName: 'defaultUserName'}; ++ const defaultUserName = 'defaultUserName'; ++ this.state = {userName: defaultUserName, editingUserName: defaultUserName}; + } + +- setUsernameState = (newName: string) => { ++ setUsernameState = () => { +- this.setState({userName: newName}); ++ this.setState({userName: this.state.editingUserName} as State); + } + ++ updateEditingName = (editingName : string) : void => { ++ this.setState({editingUserName: editingName} as State); ++ } + + public render() { + return ( + <> + +- ++ + + ); + } +} +``` + +Finalmente podemos comprobar que el ejemplo funciona como el _04 Callback_ ejecutando un `npm start` desde la consola de comandos y abriendo [http://localhost:8080](http://localhost:8080). From 30fd45d46f73945c19b877746aa6bd45f5c0299d Mon Sep 17 00:00:00 2001 From: Irene Date: Tue, 24 Apr 2018 13:39:22 +0200 Subject: [PATCH 028/180] add + in line 95 96 and 97 --- 12 TableHttp/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/12 TableHttp/readme.md b/12 TableHttp/readme.md index f2d2c8c..5fb3c5a 100644 --- a/12 TableHttp/readme.md +++ b/12 TableHttp/readme.md @@ -92,9 +92,9 @@ _./src/memberTable.tsx_ // https://facebook.github.io/react/docs/component-specs.html public componentDidMount() { - this.setState({members: memberAPI.getAllMembers()}) - memberAPI.getAllMembers().then((members) => - this.setState({members: members}) - ); ++ memberAPI.getAllMembers().then((members) => ++ this.setState({members: members}) ++ ); } ``` From 8a9a3d8ad09944e2f43f05d8d01733a4bbf3755d Mon Sep 17 00:00:00 2001 From: Irene Date: Tue, 24 Apr 2018 13:59:05 +0200 Subject: [PATCH 029/180] review and create README_ES.md --- 12 TableHttp/readme_es.md | 104 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 12 TableHttp/readme_es.md diff --git a/12 TableHttp/readme_es.md b/12 TableHttp/readme_es.md new file mode 100644 index 0000000..2db26a9 --- /dev/null +++ b/12 TableHttp/readme_es.md @@ -0,0 +1,104 @@ +# 12 Table Http + +Sigamos con nuestro ejemplo de la tabla, vamos a cambiar datos falsos por unos reales. + +Cogeremos como punto inicial el ejemplo _11 TableMock_: + +Pasos resumidos: + +- Configurar transpilación y añadir una transpilación extra babel >> es5. +- Actualizar la API para trabajar con promesas y obtener datos de la API de Github. +- Actualizar _tableComponent_ para mostrar los datos. + + +## Prerrequisitos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no estÔn ya instalados. + +> Verificar que tienes al menos corriendo la versión de node v6.x.x y npm 3.x.x ejecutando `node -v` y `npm -v` en la terminal de Windows. Versiones mÔs antiguas pueden producir errores. + +## Pasos + +- Copiar el contenido de _11 TableMock_ y ejecutar: + + ``` + npm install + ``` + +- Vamos a eliminar el fichero _mermberMockData.ts_ en el directorio _src/api_ . + +- Vamos a reemplazar _memberAPI_ cargando los miembros con promesas : + +_./src/api/memberAPI.ts_ + +```javascript +import {MemberEntity} from '../model/member'; +import {} from 'core-js'; +import {} from 'whatwg-fetch'; + +// Sync mock data API, inspired from: +// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 +class MemberAPI { + + // Just return a copy of the mock data + getAllMembers() : Promise { + const gitHubMembersUrl : string = 'https://api.github.com/orgs/lemoncode/members'; + + return fetch(gitHubMembersUrl) + .then((response) => this.checkStatus(response)) + .then((response) => this.parseJSON(response)) + .then((data) => this.resolveMembers(data)); + } + + private checkStatus(response : Response) : Promise { + if (response.status >= 200 && response.status < 300) { + return Promise.resolve(response); + } else { + let error = new Error(response.statusText); + throw error; + } + } + + private parseJSON(response : Response) : any { + return response.json(); + } + + private resolveMembers (data : any) : Promise { + const members = data.map((gitHubMember) => { + var member : MemberEntity = { + id: gitHubMember.id, + login: gitHubMember.login, + avatar_url: gitHubMember.avatar_url, + }; + + return member; + }); + + return Promise.resolve(members); + } +} + +export const memberAPI = new MemberAPI(); +``` + +- Ahora vamos a actualizar nuestro componente _membersTable_ .
    + Vamos a consumir el nuevo método de promesas para recuperar a los usuarios: + +_./src/memberTable.tsx_ + +```diff +// Standard react lifecycle function: +// https://facebook.github.io/react/docs/component-specs.html +public componentDidMount() { +- this.setState({members: memberAPI.getAllMembers()}) ++ memberAPI.getAllMembers().then((members) => ++ this.setState({members: members}) ++ ); +} +``` + +- Vamos a probar y verificar los resultados + +``` +npm start +``` From db2d79790bb3e59aa1420eb9fd6f7eea0899affc Mon Sep 17 00:00:00 2001 From: Irene Date: Tue, 24 Apr 2018 14:00:37 +0200 Subject: [PATCH 030/180] review and create README_ES.md --- 12 TableHttp/readme_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/12 TableHttp/readme_es.md b/12 TableHttp/readme_es.md index 2db26a9..5a8aad2 100644 --- a/12 TableHttp/readme_es.md +++ b/12 TableHttp/readme_es.md @@ -25,7 +25,7 @@ Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no e npm install ``` -- Vamos a eliminar el fichero _mermberMockData.ts_ en el directorio _src/api_ . +- Vamos a eliminar el fichero _mermberMockData.ts_ del directorio _src/api_ . - Vamos a reemplazar _memberAPI_ cargando los miembros con promesas : From ee09ad834371e6c5b4153da2e968ae2cfbb85c43 Mon Sep 17 00:00:00 2001 From: "Juan A. Carvajal" Date: Wed, 25 Apr 2018 01:26:26 +0200 Subject: [PATCH 031/180] Update links to nameEdit.tsx and add readme_es --- 06 MoveBackToStateless/readme.md | 11 +++---- 06 MoveBackToStateless/readme_es.md | 51 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 06 MoveBackToStateless/readme_es.md diff --git a/06 MoveBackToStateless/readme.md b/06 MoveBackToStateless/readme.md index ff3fba5..322f7c1 100644 --- a/06 MoveBackToStateless/readme.md +++ b/06 MoveBackToStateless/readme.md @@ -2,14 +2,13 @@ In example 05 we learned how to remove state from a child control just to have clear governance of state. -It's time to make some cleanup, let's simplify _nameEdit_ component and move it as a stateless component. +It's time to make some cleanup, let's simplify _[nameEdit.tsx](./src/nameEdit.tsx)_ component and move it as a stateless component. -We will take a startup point sample _05 MoveBacktOStateless_. +We will take a startup point sample _[05 Refactor](./../05%20Refactor)_. Summary steps: -- Update _nameEdit.tsx_, port it to stateless component and add the methods inline. - +- Update _[nameEdit.tsx](./src/nameEdit.tsx)_, port it to stateless component and add the methods inline. ## Prerequisites @@ -19,9 +18,9 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ## Steps to build it -- Copy the content from _05 Refactor_ and execute `npm install`. +- Copy the content from _[05 Refactor](./../05%20Refactor)_ and execute `npm install`. -- Update _nameEdit.tsx_, port it to stateless component and add the methods inline. It should look like: +- Update _[nameEdit.tsx](./src/nameEdit.tsx)_, port it to stateless component and add the methods inline. It should look like: ```jsx import * as React from 'react'; diff --git a/06 MoveBackToStateless/readme_es.md b/06 MoveBackToStateless/readme_es.md new file mode 100644 index 0000000..a82c36b --- /dev/null +++ b/06 MoveBackToStateless/readme_es.md @@ -0,0 +1,51 @@ +# 06 MoveBackToStateless + +En el ejemplo 05 aprendimos como eliminar el estado de un control secundario para tener una gestión limpia del estado. + +Es hora de hacer limpieza, simplificando el componente _[nameEdit.tsx](./src/nameEdit.tsx)_ convirtiéndolo en un componente sin estado. + +Tomaremos como punto de partida el ejemplo _[05 Refactor](./../05%20Refactor)_. + +Pasos resumidos: + +- Actualizar _[nameEdit.tsx](./src/nameEdit.tsx)_, convirtiéndolo en un componente sin estado y añadir los métodos inline. + +## Prerequisitos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o superior) si aún no los tienes instalados en tu equipo. + +> Verifica que estÔs usando al menos node v6.x.x and npm 3.x.x usando los comandos `node -v` y `npm -v` en un terminal/consola. Versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copia el contenido de _[05 Refactor](./../05%20Refactor)_ y ejecuta `npm install`. + +- Actualiza _[nameEdit.tsx](./src/nameEdit.tsx)_, convirtiendo en un componente sin estados y añadiendo los métodos inline. Debería verse como: + + ```jsx +import * as React from 'react'; +import {Fragment} from 'react'; + + +interface Props { + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => void; + onNameUpdateRequest : () => void; +} + + +export const NameEditComponent = (props : Props) => +
    + + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + + +
    + ``` + +- Ahora podemos arrancar el ejemplo y obtendremos los mismos resultados. + +```bash +npm start +``` \ No newline at end of file From 0b94347cca5cbd1051ced3c3a4c53726dc0baeea Mon Sep 17 00:00:00 2001 From: "Juan A. Carvajal" Date: Wed, 25 Apr 2018 01:33:30 +0200 Subject: [PATCH 032/180] Corrected grammatical fault --- 06 MoveBackToStateless/readme_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/06 MoveBackToStateless/readme_es.md b/06 MoveBackToStateless/readme_es.md index a82c36b..c84c730 100644 --- a/06 MoveBackToStateless/readme_es.md +++ b/06 MoveBackToStateless/readme_es.md @@ -10,7 +10,7 @@ Pasos resumidos: - Actualizar _[nameEdit.tsx](./src/nameEdit.tsx)_, convirtiéndolo en un componente sin estado y añadir los métodos inline. -## Prerequisitos +## Prerrequisitos Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o superior) si aún no los tienes instalados en tu equipo. From 0121dd4a606ed338d88a8feb491ca951cbadf492 Mon Sep 17 00:00:00 2001 From: Irene Date: Wed, 25 Apr 2018 11:32:06 +0200 Subject: [PATCH 033/180] add a new component thead to improve the table --- 12 TableHttp/readme_es.md | 29 +++++++++++++++++++++++++++-- 12 TableHttp/src/memberHead.tsx | 17 +++++++++++++++++ 12 TableHttp/src/membersTable.tsx | 13 ++----------- 3 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 12 TableHttp/src/memberHead.tsx diff --git a/12 TableHttp/readme_es.md b/12 TableHttp/readme_es.md index 5a8aad2..746ed58 100644 --- a/12 TableHttp/readme_es.md +++ b/12 TableHttp/readme_es.md @@ -80,12 +80,37 @@ class MemberAPI { export const memberAPI = new MemberAPI(); ``` +- Añadimos un nuevo componente _memberHead_ para crear la cabecera de la tabla : -- Ahora vamos a actualizar nuestro componente _membersTable_ .
    - Vamos a consumir el nuevo mƩtodo de promesas para recuperar a los usuarios: +```javascript +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + Avatar + + + Id + + + Name + + +``` +- Ahora vamos a actualizar nuestro componente _membersTable_ .
    + _./src/memberTable.tsx_ +- Importamos el nuevo componente : +```diff ++ import {MemberHead} from './memberHead'; +``` + +- Vamos a consumir el nuevo mƩtodo de promesas para recuperar a los usuarios: + ```diff // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html diff --git a/12 TableHttp/src/memberHead.tsx b/12 TableHttp/src/memberHead.tsx new file mode 100644 index 0000000..aa262b0 --- /dev/null +++ b/12 TableHttp/src/memberHead.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + + Avatar + + + Id + + + Name + + + \ No newline at end of file diff --git a/12 TableHttp/src/membersTable.tsx b/12 TableHttp/src/membersTable.tsx index 9f1d1b9..422201e 100644 --- a/12 TableHttp/src/membersTable.tsx +++ b/12 TableHttp/src/membersTable.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { MemberEntity } from './model/member'; import { memberAPI } from './api/memberAPI'; import { MemberRow } from './memberRow'; +import {MemberHead} from './memberHead'; interface Props { } @@ -37,17 +38,7 @@ export class MembersTableComponent extends React.Component {

    Members Page

    - - - - - + { From 89c6add301262f83114c7dd00ba8a72c4d96eff3 Mon Sep 17 00:00:00 2001 From: Irene Date: Wed, 25 Apr 2018 11:39:45 +0200 Subject: [PATCH 034/180] modify readme_es --- 12 TableHttp/readme_es.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/12 TableHttp/readme_es.md b/12 TableHttp/readme_es.md index 746ed58..73b3656 100644 --- a/12 TableHttp/readme_es.md +++ b/12 TableHttp/readme_es.md @@ -108,6 +108,25 @@ _./src/memberTable.tsx_ ```diff + import {MemberHead} from './memberHead'; ``` +- Modificamos la funcion render : +```diff +- +- +- +- +- +- +- ++ ++ ++ +``` - Vamos a consumir el nuevo mƩtodo de promesas para recuperar a los usuarios: From 33f74fa31ccbe78ce2fe1511be5a5704381b644c Mon Sep 17 00:00:00 2001 From: Irene Date: Wed, 25 Apr 2018 11:50:16 +0200 Subject: [PATCH 035/180] modify readme --- 12 TableHttp/readme.md | 54 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/12 TableHttp/readme.md b/12 TableHttp/readme.md index 5fb3c5a..1dd2f75 100644 --- a/12 TableHttp/readme.md +++ b/12 TableHttp/readme.md @@ -80,13 +80,65 @@ class MemberAPI { } export const memberAPI = new MemberAPI(); + +``` +- Add a new component _memberHead_ to create the table's header: + +```javascript +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + + + +``` + ``` - Now it's time to update our _membersTable_ component.
    - Let's consume the new promise base method to retrieve the users: _./src/memberTable.tsx_ +- Import the new component : + +```diff + ++ import {MemberHead} from './memberHead'; + +``` + +- Modify the render function : + +```diff +- +- +- +- +- +- +- ++ ++ ++ +``` + +- Let's consume the new promise base method to retrieve the users: + ```diff // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html From 46a8c00eda035c55bcb35f6b7e452ff5f0975623 Mon Sep 17 00:00:00 2001 From: Irene Date: Wed, 25 Apr 2018 11:51:23 +0200 Subject: [PATCH 036/180] modify readme --- 12 TableHttp/readme.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/12 TableHttp/readme.md b/12 TableHttp/readme.md index 1dd2f75..2a4ee02 100644 --- a/12 TableHttp/readme.md +++ b/12 TableHttp/readme.md @@ -102,8 +102,6 @@ export const MemberHead = () => ``` -``` - - Now it's time to update our _membersTable_ component.
    _./src/memberTable.tsx_ From 970ea85e56a9b955850d6a2c6fc9a2dc8445b5dc Mon Sep 17 00:00:00 2001 From: dailymp Date: Sat, 28 Apr 2018 09:34:32 +0200 Subject: [PATCH 037/180] # 95 # 96 Both samples reviewed and created both readmes_es --- 08 Colorpicker/readme_es.md | 310 +++++++++++++++++++++++++++++++++ 09 ColorpRefactor/readme_es.md | 219 +++++++++++++++++++++++ 2 files changed, 529 insertions(+) create mode 100644 08 Colorpicker/readme_es.md create mode 100644 09 ColorpRefactor/readme_es.md diff --git a/08 Colorpicker/readme_es.md b/08 Colorpicker/readme_es.md new file mode 100644 index 0000000..0d99c36 --- /dev/null +++ b/08 Colorpicker/readme_es.md @@ -0,0 +1,310 @@ +# 08 Colorpicker + +Tomaremos como punto de partida el ejemplo _01 HelloReact_: + +>Este ejemplo estÔ basado en el siguiente: [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), pero añade algunas variaciones + +Resumen de pasos: + +- Renombrar el archivo _hello.tsx_ a _colorpicker.tsx_. +- Definir las propiedades y el estado. +- Crear la UI. + + +## Prerrequisitos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 ó + nueva) si aún no estÔn instaladas. + +> Verificar que tienes node al menos v6.x.x y npm 3.x.x ejecutando `node -v` y `npm -v` en un terminal ó console de windows. Versiones viejas pueden provocar errores. + +## Pasos a seguir. + +- Copiar el contenido de _01 HelloReact_ y ejecutar `npm install`. + +- Vamos a definir una estructura apropiada (crea un archivo _color.tsx_). + +_./src/color.tsx_ + +```javascript +export interface Color { + red : number; + green : number; + blue : number; +} +``` + +- Renombramos _hello.tsx_ a _colorpicker.tsx_. + +- Renombramos también el nombre del componente. + +```jsx +import * as React from 'react'; + +export const ColorPicker = () => { + return ( +

    Hello component !

    + ); +} +``` + +- Creamos un archivo intermedio _app.tsx_ como hicimos en ejemplos previos: + +_./src/app.tsx_ + +```jsx +import * as React from 'react'; +import {Color} from './color'; +import {ColorPicker} from './colorpicker'; + +interface State { + color : Color; +} + +export class App extends React.Component<{}, State> { + constructor(props) { + super(props); + + this.state = {color: {red: 90, green: 50, blue: 70}}; + } + + setColorState = (newColor : Color) => { + this.setState({color: newColor}); + } + + public render() { + return ( +
    + +
    + ); + } +} +``` + +- Necesitamos actualizar _main.tsx_ para indicar los cambios + +_./src/main.tsx_ + +```diff + import * as React from 'react'; + import * as ReactDOM from 'react-dom'; +- import { HelloComponent } from './hello'; ++ import {App} from './app'; + + ReactDOM.render( +- ++ , + document.getElementById('root')); +``` + +- Vamos a cambiar tambiƩn el contenido del fichero, definimos un color y un + callback como propiedad para establecer el color (_colorpicker.tsx_). + +_./src/colorpicker.tsx_ + +```diff +import * as React from 'react'; ++ import {Color} from './color' + ++ interface Props { ++ color : Color; ++ onColorUpdated : (color : Color) => void; ++ } + +export const ColorPicker = () => { + return ( +

    Hello component !

    + ); +} +``` + +- Vamos a comenzar solo definiendo un control para el componente rojo de un color dado. (_colorpicker.tsx_). + +_./src/colorpicker.tsx_ + +```diff +- export const ColorPicker = () => { ++ export const ColorPicker = (props : Props) => { + return ( +-

    Hello component !

    ++
    ++ props.onColorUpdated( ++ {red: event.target.value, green: props.color.green, blue: props.color.blue} ++ )} ++ /> ++ {props.color.red} ++
    + ); + } +``` + +- Ahora actualizamos el archivo _app.tsx_ para interactuar con las propiedades del componente. + +_./src/app.tsx_ + +```diff + import * as React from 'react'; + import {Color} from './color'; + import {ColorPicker} from './colorpicker'; + + interface State { + color : Color; + } + + export class App extends React.Component<{}, State> { + constructor(props) { + super(props); + + this.state = {color: {red: 90, green: 50, blue: 70}}; + } + + setColorState(newColor : Color) { + this.setState({color: newColor}); + } + + public render() { + return ( +
    ++ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] +- ++ +
    + ); + } + } + +``` + +- Ahora probamos que tenemos el funcionamiento bÔsico correcto. + +``` + npm start +``` + +- Completamos el componente añadiendo sliders para las opciones de color verde y azul: + +> Nota: esto parecerÔ un poco feo, en el siguiente ejemplo vamos a refactorizar y lo haremos una solución mÔs limpia. + +_./src/colopicker.tsx_ + +```diff + export const ColorPicker = (props : Props) => { + return ( +
    + props.onColorUpdated( + { + red: event.target.value, + green: props.color.green, + blue: props.color.blue + } + )} + /> + {props.color.red} ++
    ++ props.onColorUpdated( ++ { ++ red: props.color.red, ++ green: event.target.value, ++ blue: props.color.blue ++ } ++ )} ++ /> ++ {props.color.green} ++
    ++ props.onColorUpdated( ++ { ++ red: props.color.red, ++ green: props.color.green, ++ blue: event.target.value ++ } ++ )} ++ /> ++ {props.color.blue} ++
    +
    + ); + } +``` + +- Haremos esto un poco mƔs atractivo visualmente, serƭa una buena idea mostrar un rectƔngulo relleno con el color selccionado. Crearemos un componente ColorDisplayer (_colordisplayer.tsx_). + +_./src/colordisplayer.tsx_ + +```jsx + import * as React from 'react'; + import {Color} from './color' + + interface Props { + color : Color; + } + + export const ColorDisplayer = (props : Props) => { + const divStyle = { + width: '11rem', + height: '7rem', + backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` + }; + + return ( +
    +
    + ); + } +``` + +- Ahora vamos a usarlo dentro de nuestro componente App (_app.tsx_). + +```diff +import * as React from 'react'; +import {Color} from './color'; +import {ColorPicker} from './colorpicker'; ++ import {ColorDisplayer} from './colordisplayer'; + +interface State { + color : Color; +} + +export class App extends React.Component<{}, State> { + constructor(props) { + super(props); + + this.state = {color: {red: 90, green: 50, blue: 70}}; + } + + setColorState(newColor : Color) { + this.setState({color: newColor}); + } + + public render() { + return ( +
    ++ ++ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + +
    + ); + } +} +``` + +- Ahora a probarlo y ver los resultados!!! + + ``` +npm start +``` diff --git a/09 ColorpRefactor/readme_es.md b/09 ColorpRefactor/readme_es.md new file mode 100644 index 0000000..2d09921 --- /dev/null +++ b/09 ColorpRefactor/readme_es.md @@ -0,0 +1,219 @@ +# 09 Refactorización del Colorpicker +En este ejemplo revisaremos el componente colorpicker que creamos previamente, y lo simplificaremos. +Ahora tenemos 3 controles sliders con muchos detalles que hacen nuestro HTML difícil de leer. +Haremos un escenario mÔs orientado al componente. + +Tomaremos como punto de partida el ejemplo _08 Colorpicker_: + +Resumen de pasos: + +- Crear un componente deslizable para un color simple. +- Remplazar los slides inputs actuales con el nuevo componente creado. +- Revisar el resultado. + + +## Prerrequisitos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o posterior) si no estÔn ya instalados. + +> Verificar que estÔs ejecutando al menos la v6.x.x de node y npm 3.x.x ejecutando el siguiente comando `node -v` y `npm -v` en una terminal/console de window. Versiones viejas pueden producir errores. + +## Pasos a seguir: + +- Copiar el contenido desde _08 ColorPicker_ y ejecutar `npm install`. + +- Definiremos un componente ColorSliderComponent (_colorslider.tsx_). + +_./src/colorslider.tsx_ + +```jsx +import * as React from 'react'; +import {Color} from './color'; + +interface Props { + value : number; + onValueUpdated : (newValue : number) => void; +} + +export const ColorSliderComponent = (props : Props) => { + + return ( +
    + props.onValueUpdated(event.target.value)} + /> + {props.value} +
    + ); +} +``` + +- Refactorizamos nuestro _colorpicker.tsx_. + +```diff +import * as React from 'react'; +import {Color} from './color'; ++ import {ColorSliderComponent} from './colorslider'; + +interface Props { + color : Color; + onColorUpdated : (color : Color) => void; +} + +export const ColorPicker = (props : Props) => { + return ( +
    +- props.onColorUpdated( +- { +- red: props.color.red, +- green: event.target.value, +- blue: props.color.blue +- } +- )} +- /> +- {props.color.green} ++ props.onColorUpdated( ++ { ++ red: value, ++ green: props.color.green, ++ blue: props.color.blue ++ }) ++ } ++ /> +
    +- props.onColorUpdated( +- { +- red: props.color.red, +- green: event.target.value, +- blue: props.color.blue +- } +- )} +- /> +- {props.color.green} ++ props.onColorUpdated( ++ { ++ red: props.color.red, ++ green: value, ++ blue: props.color.blue ++ }) ++ } ++ /> +
    +- props.onColorUpdated( +- { +- red: props.color.red, +- green: props.color.green, +- blue: event.target.value +- } +- )} +- /> +- {props.color.blue} ++ props.onColorUpdated( ++ { ++ red: props.color.red, ++ green: props.color.green, ++ blue: value ++ }) ++ } ++ /> +
    +
    + ); +} +``` + +- Probemos y verifiquemos que todo funciona segĆŗn lo esperado. + + ``` + npm start + ``` + +- TodavĆ­a tenemos mejoras que hacer, porque no tener un solo manejador para todos los colores? Si currificamos el colorupdated handler, podemos! + +```diff +import * as React from 'react'; +import { Color } from './color' +import {ColorSliderComponent} from './colorslider'; + +interface Props { + color: Color; + onColorUpdated: (color: Color) => void; +} + ++ const updateColor = (props : Props, colorId : keyof Color) => (value) => { ++ props.onColorUpdated({ ++ ...props.color, ++ [colorId]: value ++ }); ++ }; + +export const ColorPicker = (props: Props) => { + return ( +
    + props.onColorUpdated( +- { +- red: value, +- green: props.color.green, +- blue: props.color.blue +- }) +- } + /> +
    + props.onColorUpdated( +- { +- red: props.color.red, +- green: value, +- blue: props.color.blue +- }) +- } + /> +
    + props.onColorUpdated( +- { +- red: props.color.red, +- green: props.color.green, +- blue: value +- }) +- } + /> +
    +
    + ); +} +``` + +- DƩmosle una oprtunidad el ejemplo: + +``` +npm start +``` From 47db18e4140de43391fc73bbe35f3ba26f223b1b Mon Sep 17 00:00:00 2001 From: Igor Alvarez Date: Tue, 1 May 2018 14:16:43 +0200 Subject: [PATCH 038/180] #105 Review 10 Sidebar and create README_ES.md --- 10 Sidebar/readme.md | 59 +++----- 10 Sidebar/readme_es.md | 296 +++++++++++++++++++++++++++++++++++++ 10 Sidebar/src/sidebar.tsx | 5 +- 3 files changed, 321 insertions(+), 39 deletions(-) create mode 100644 10 Sidebar/readme_es.md diff --git a/10 Sidebar/readme.md b/10 Sidebar/readme.md index e55c159..16f4f69 100644 --- a/10 Sidebar/readme.md +++ b/10 Sidebar/readme.md @@ -23,7 +23,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Copy the content from _03 State_ and execute `npm install`. -- Create a file called src/sidebar.css and add the following styles (http://www.w3schools.com/howto/howto_js_sidenav.asp): +- Create a file called _src/sidebar.css_ and add the following styles (http://www.w3schools.com/howto/howto_js_sidenav.asp): _./src/sidebar.css_ @@ -84,13 +84,8 @@ _./webpack.config.js_ { test: /\.css$/, + include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), - }, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + // Use CSS modules for custom stylesheets + { + test: /\.css$/, @@ -122,16 +117,13 @@ import * as React from 'react'; const classNames = require('./sidebar.css'); -export const SidebarComponent = () => { - return ( +export const SidebarComponent = () =>
    Basic side bar, first steps
    - ); -} ``` -- We are going to add a known id to to body section of _src/index.html_ page +- We are going to add a known id to the body section of _src/index.html_ page _./src/index.html_ @@ -140,7 +132,7 @@ _./src/index.html_ + ``` -- Let's place the component adding into the app.tsx: +- Let's place the component adding it into the `app.tsx`: ```jsx import {SidebarComponent} from './sidebar'; @@ -156,7 +148,7 @@ _./src/index.html_ ); ``` -- Now is time to run the app, just to check we haven't broken anything (but you will see no results). +- Now it is time to run the app, just to check we haven't broken anything (but you will see no results). ``` npm start @@ -176,17 +168,14 @@ const classNames = require('./sidebar.css'); + isVisible: boolean; + } -- export const SidebarComponent = () => { -+ export const SidebarComponent = (props: Props) => { - return ( +- export const SidebarComponent = () => ++ export const SidebarComponent = (props: Props) =>
    - Basic side bar, first steps + Basic sidebar, first steps
    - ); -} ``` -- Now let's add some logic to show / display the sidebar in case the flag gets +- Now let's add some logic to show / hide the sidebar in case the flag gets updated ```diff @@ -202,14 +191,11 @@ interface Props { + width: (props.isVisible) ? '23rem' : '0rem' + }); -export const SidebarComponent = (props: Props) => { - return ( +export const SidebarComponent = (props: Props) => -
    +
    - Basic side bar, first steps + Basic sidebar, first steps
    - ); -} ``` - Now at app level (in file _app.tsx_) we can add a new member to the state (a boolean flag) and a button to turn it @@ -228,7 +214,7 @@ export class App extends React.Component<{}, State> { super(props); - this.state = {userName: 'defaultUserName'}; -+ this.state = {userName: "defaultUserName", isSidebarVisible: false}; ++ this.state = {userName: "defaultUserName", isSidebarVisible: false}; } setUsernameState(event) { @@ -263,9 +249,15 @@ export class App extends React.Component<{}, State> { } ``` +- At this point we will need to stop the app and start it again to see the changes working: + +```cmd +npm start +``` + - If we run our sample, we can see how the sidebar is shown / hidden. -- So far so good, but what happens if we want to make this sidebar a reusable component, we could +- So far so good, but what happens if we want to make this sidebar a reusable component? We could just show the frame but the content should be dynamic. - Let's start by adding some content when instantiating the sidebar (_app.tsx_). @@ -291,19 +283,16 @@ const divStyle = (props: React.CSSProperties) => ({ width: (props.isVisible) ? '250px' : '0px' }); -- export const SidebarComponent = (props: Props) => { -+ export const SidebarComponent : React.StatelessComponent = (props) => { +- export const SidebarComponent = (props: Props) => ++ export const SidebarComponent : React.StatelessComponent = (props) => - return (
    - Basic side bar, first steps + {props.children}
    - ); -} ``` -> This code can be enhanced, excercise: try to extra the width calculation to a separate function (isolated form the SidebarComponent). +> This code can be enhanced, exercise: try to extract the width calculation to a separate function (isolated from the SidebarComponent). - Let's try the sample diff --git a/10 Sidebar/readme_es.md b/10 Sidebar/readme_es.md new file mode 100644 index 0000000..ca37c27 --- /dev/null +++ b/10 Sidebar/readme_es.md @@ -0,0 +1,296 @@ +# 10 Sidebar + +En este ejemplo vamos a implementar una única barra lateral. + +Tomaremos como punto de partida el ejemplo _03 State_: + +Resumen de pasos: + +- Añadir algunos estilos. +- Incluir el nuevo css en webpack. +- Crear un componente de barra lateral. +- Añadamos algo de contenido a la barra lateral. +- Añadir un botón para abrir / cerrar la barra lateral. + + +## Prerequisitos + +Instalar [Node.js and npm](https://nodejs.org/es/) si no lo tenemos ya instalado. + +> Verificar que estÔs ejecutando al menos con la versión 6.x.x de node y la versión 3.x.x de npm ejecutando `node -v` y `npm -v` en la ventana de terminal/consola. Versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copiar el contenido de _03 State_ y ejecutar `npm install`. + +- Crear un fichero llamado _src/sidebar.css_ y añadir los siguientes estilos (http://www.w3schools.com/howto/howto_js_sidenav.asp): + +_./src/sidebar.css_ + +```css + /* The side navigation menu */ + .sidenav { + height: 100%; /* 100% Full-height */ + width: 0; /* 0 width - change this with JavaScript */ + position: fixed; /* Stay in place */ + z-index: 1; /* Stay on top */ + top: 0; + left: 0; + background-color: #808080; /* Gray*/ + overflow-x: hidden; /* Disable horizontal scroll */ + padding-top: 60px; /* Place content 60px from the top */ + transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */ + } + + + /* Position and style the close button (top right corner) */ + .sidenav .closebtn { + position: absolute; + top: 0; + right: 25px; + font-size: 36px; + margin-left: 50px; + } + + /* Style page content - use this if you want to push the page content to the right when you open the side navigation */ + #main { + transition: margin-left .5s; + padding: 20px; + } + + /* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */ + @media screen and (max-height: 450px) { + .sidenav {padding-top: 15px;} + .sidenav a {font-size: 18px;} + } +``` + +- Vamos a utilizar CSS Modules, así que configurémoslo. + +_./webpack.config.js_ + +```diff + module.exports = { + context: path.join(basePath, "src"), + resolve: { +- extensions: ['.js', '.ts', '.tsx'] ++ extensions: ['.js', '.ts', '.tsx', '.css'] + }, +``` + +- Solo utilizaremos CSS Modules para la hoja de estilos de la app. No utilizaremos CSS Modules para otros ficheros CSS, como Bootstrap (carpeta node_modules). + +```diff + { + test: /\.css$/, ++ include: /node_modules/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, ++ // Use CSS modules for custom stylesheets ++ { ++ test: /\.css$/, ++ exclude: /node_modules/, ++ use: [ ++ MiniCssExtractPlugin.loader, ++ { ++ loader: 'css-loader', ++ options: { ++ modules: true, ++ localIdentName: '[name]__[local]___[hash:base64:5]', ++ camelCase: true, ++ }, ++ }, ++ ] ++ }, ++ // Do not use CSS modules in node_modules folder + +``` + + +- Vamos a crear un nuevo componente sidebar, _src/sidebar.tsx_. En este punto crearemos solamente un rectangulo e interactuaremos con la animación. + +_./src/sidebar.tsx_ + +```jsx +import * as React from 'react'; + +const classNames = require('./sidebar.css'); + +export const SidebarComponent = () => +
    + Basic side bar, first steps +
    +``` + +- Vamos a añadir un id conocido a la sección body de la pÔgina _src/index.html_ + +_./src/index.html_ + +```diff +- ++ +``` + +- Situemos el componente añadiendolo dentro de `app.tsx`: + +```jsx + import {SidebarComponent} from './sidebar'; +``` + +```diff + return ( + <> ++ + + + + ); +``` + +- Ahora es el momento de ejecutar la aplicación, simplemente para comprobar que no se ha roto nada (pero no verÔs ningún resultado). + +``` + npm start +``` + +- Comencemos con la parte interesante de la implementación, añadamos un flag para mostrar/ocultar la barra lateral _sidebar.tsx_. + +_./src/sidebar.tsx_ + +```diff +import * as React from 'react'; + +const classNames = require('./sidebar.css'); + ++ interface Props { ++ isVisible: boolean; ++ } + +- export const SidebarComponent = () => ++ export const SidebarComponent = (props: Props) => +
    + Basic sidebar, first steps +
    +``` + +- Añadamos algo de lógica para mostrar/ocultar la barra lateral en caso de que se actualice el valor del flag + +```diff +import * as React from 'react'; + +const classNames = require('./sidebar.css'); + +interface Props { + isVisible: boolean; +}; + ++ const divStyle = (props: Props): React.CSSProperties => ({ ++ width: (props.isVisible) ? '23rem' : '0rem' ++ }); + +export const SidebarComponent = (props: Props) => +-
    ++
    + Basic sidebar, first steps +
    +``` + +- Ahora a nivel de app (en el fichero _app.tsx_) podemos añadir un nuevo miembro al estado (un flag booleano) y un botón para activarlo o desactivarlo. + +```diff + interface State { + userName : string; ++ isSidebarVisible : boolean; + } +``` + +```diff +export class App extends React.Component<{}, State> { + constructor(props) { + super(props); + +- this.state = {userName: 'defaultUserName'}; ++ this.state = {userName: "defaultUserName", isSidebarVisible: false}; + } + + setUsernameState(event) { + // If the state gets more complex we should use object.assign + this.setState({userName: event.target.value}); + } + ++ toggleSidebarVisibility = () => { ++ const newVisibleState = !this.state.isSidebarVisible; ++ ++ this.setState({isSidebarVisible: newVisibleState} as State); ++ } + + + public render() { + return ( + <> +- ++ + + ++
    ++ ++
    + + ); + } +} +``` + +- Llegados a este punto necesitaremos parar y volver a arrancar la aplicación para ver los cambios funcionando: + +```cmd +npm start +``` + +- Si ejecutamos nuestro ejemplo, podemos ver como nuestra barra lateral se muestra/oculta. + +- Hasta aqui todo bien, pero ¿que pasa si queremos hacer que nuestra barra lateral sea un componente reusable? Podriamos simplemente mostrar nuestra caja pero el contenido debería ser dinÔmico. + +- Comencemos añadiendo algo de contenido cuando nuestra barra lateral es instanciada. + +```diff + ++

    Test content

    +
    +``` + +- Ahora vamos a volcar algo de contenido en _sidebar.tsx_ usando {this.props.children} + +```diff +import * as React from 'react'; + +const classNames = require('./sidebar.css'); + +interface Props { + isVisible: boolean; +}; + +const divStyle = (props: React.CSSProperties) => ({ + width: (props.isVisible) ? '250px' : '0px' +}); + +- export const SidebarComponent = (props: Props) => ++ export const SidebarComponent : React.StatelessComponent = (props) => + +
    +- Basic side bar, first steps ++ {props.children} +
    +``` + +> Este código puede mejorarse, ejercicio: intenta extraer el cÔlculo de width a una función separada (aislada de SidebarComponent). + +- Probemos el ejemplo + +```cmd +npm start +``` diff --git a/10 Sidebar/src/sidebar.tsx b/10 Sidebar/src/sidebar.tsx index 3689ab9..9c14230 100644 --- a/10 Sidebar/src/sidebar.tsx +++ b/10 Sidebar/src/sidebar.tsx @@ -11,10 +11,7 @@ const divStyle = (props: Props): React.CSSProperties => ({ }); -export const SidebarComponent : React.StatelessComponent = (props) => { - return ( +export const SidebarComponent : React.StatelessComponent = (props) =>
    {props.children}
    - ); -} From 9b006782ab70ec4c225fe7cac457f1d189148994 Mon Sep 17 00:00:00 2001 From: Igor Alvarez Date: Tue, 1 May 2018 14:29:05 +0200 Subject: [PATCH 039/180] #122 05 Refactor Markup errors in README files --- 05 Refactor/readme.md | 4 ++-- 05 Refactor/readme_es.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/05 Refactor/readme.md b/05 Refactor/readme.md index 49492f1..e2a478f 100644 --- a/05 Refactor/readme.md +++ b/05 Refactor/readme.md @@ -26,7 +26,7 @@ interface State { + initialUserName : string, editingName: string; } - +``` Constructor update: ```diff @@ -38,7 +38,7 @@ Constructor update: + this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; } - +``` Inside the class component ```javascript diff --git a/05 Refactor/readme_es.md b/05 Refactor/readme_es.md index 9c7cfc2..d9d174e 100644 --- a/05 Refactor/readme_es.md +++ b/05 Refactor/readme_es.md @@ -22,7 +22,7 @@ interface State { + initialUserName : string, editingName: string; } - +``` Actualización del constructor: ```diff @@ -34,7 +34,7 @@ Actualización del constructor: + this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; } - +``` Dentro del componente de clase ```javascript From 53145aa220dad7941454bbc05a77ca10a8650d55 Mon Sep 17 00:00:00 2001 From: "Juan A. Carvajal" Date: Tue, 15 May 2018 00:31:34 +0200 Subject: [PATCH 040/180] Update readme and add readme_es --- 14 ReactRouter/readme.md | 41 ++++------- 14 ReactRouter/readme_es.md | 133 ++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 14 ReactRouter/readme_es.md diff --git a/14 ReactRouter/readme.md b/14 ReactRouter/readme.md index 9a7be6a..ab1c7c7 100644 --- a/14 ReactRouter/readme.md +++ b/14 ReactRouter/readme.md @@ -6,16 +6,16 @@ In this case we will provide a default `userName` but let the user update it. -We will take a startup point sample _03 State_: +We will take a startup point sample _[03 State](./../03%20State)_: Summary steps: - Let's make first some cleanup: remove _hello.tsx_ and _nameEdit.tsx_ -- Let's create two components _PageA_ and _PageB_ +- Let's create two components _[PageA.tsx](./src/pageA.tsx)_ and _[PageB.tsx](./src/pageB.tsx)_ - Let's install the dependencies to _react-router-dom_ and typescript definitions for this. - Let's define the routing. -- Let's define a navigation from _PageA_ to _PageB_. -- Let's define a navigation from _PageB_ to _PageA_. +- Let's define a navigation from _[PageA.tsx](./src/pageA.tsx)_ to _[PageB.tsx](./src/pageB.tsx)_. +- Let's define a navigation from _[PageB.tsx](./src/pageB.tsx)_ to _[PageA.tsx](./src/pageA.tsx)_. ## Prerequisites @@ -25,7 +25,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ## Steps to build it -- Copy the content from _03 State_ and execute: +- Copy the content from _[03 State](./../03%20State)_ and execute: ``` npm install @@ -40,29 +40,22 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ```jsx import * as React from "react" -export const PageA = () => { - return ( +export const PageA = () =>

    Hello from page A

    - ) -} ``` +- Let's create a component called _PageB_ as _src/pageB.tsx_: ### ./src/pageB.tsx -- Let's create a component called _PageB_ as _src/pageB.tsx_: - ```jsx import * as React from "react" -export const PageB = () => { - return ( +export const PageB = () =>

    Hello from page B

    - ) -} ``` - Let's install the dependencies [`react-router-dom`](https://github.com/ReactTraining/react-router) and typescript definitions for this. @@ -103,7 +96,7 @@ ReactDOM.render( npm start ``` -- Let's define a navigation from PageA to PageB (_src/pageA.tsx_). +- Let's define a navigation from _[PageA.tsx](./src/pageA.tsx)_ to _[PageB.tsx](./src/pageB.tsx)_. ### ./src/pageA.tsx @@ -111,34 +104,28 @@ npm start import * as React from "react" + import { Link } from 'react-router-dom'; -export const PageA = () => { - return ( +export const PageA = () =>

    Hello from page A

    +
    + Navigate to Page B
    - ) -} - ``` -- Let's define a navigation from PageB to PageA (_pageA.tsx_) +- Let's define a navigation from _[PageB.tsx](./src/pageB.tsx)_ to _[PageA.tsx](./src/pageA.tsx)_ + +### ./src/pageB.tsx ```diff import * as React from "react" + import { Link } from 'react-router-dom'; -export const PageB = () => { - return ( +export const PageB = () =>

    Hello from page B

    +
    + Navigate to Page A
    - ) -} - ``` diff --git a/14 ReactRouter/readme_es.md b/14 ReactRouter/readme_es.md new file mode 100644 index 0000000..c18f91a --- /dev/null +++ b/14 ReactRouter/readme_es.md @@ -0,0 +1,133 @@ +# 14 ReactRouter + +En este ejemplo comenzaremos a usar React-Router (navegación SPA). + +En este caso proporcionaremos un `userName` por defecto pero dejaremos actualizarlo al usuario. + +Tomaremos como punto de partida el ejemplo _[03 State](./../03%20State)_ + +Resumen de pasos: + +- Primero vamos a hacer un poco de limpieza: eliminamos _hello.tsx_ y _nameEdit.tsx_ +- Vamos a crear dos componentes: _[PageA.tsx](./src/pageA.tsx)_ y _[PageB.tsx](./src/pageB.tsx)_ +- Vamos a instalar las dependencias a _react-router-dom_ y sus definiciones para typescript. +- Vamos a definir el enrutado. +- Vamos a definir la navegación de _[PageA.tsx](./src/pageA.tsx)_ a _[PageB.tsx](./src/pageB.tsx)_ +- Vamos a definir la navegación de _[PageB.tsx](./src/pageB.tsx)_ a _[PageA.tsx](./src/pageA.tsx)_ + +## Prerrequisitos + +Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estÔs usando al menos node v6.x.x and npm 3.x.x usando los comandos `node -v` y `npm -v` en un terminal/consola. Versiones anteriores pueden producir errores. + +## Pasos para construirlo + +Copia el contenido de _[03 State](./../03%20State)_ y ejecuta: + + ``` + npm install + ``` + +- Vamos a hacer algo de limpieza (eliminar los archivos _src/hello.tsx_ y _src/nameEdit.tsx_). + +- Vamos a crear un componente llamado _PageA_ como _src/pageA.tsx_: + +### ./src/pageA.tsx + +```jsx +import * as React from "react" + +export const PageA = () => +
    +

    Hello from page A

    +
    +``` + +- Vamos a crear un componente llamado _PageB_ como _src/pageB.tsx_: + +### ./src/pageB.tsx + +```jsx +import * as React from "react" + +export const PageB = () => +
    +

    Hello from page B

    +
    +``` + +- Vamos a instalar las dependencias [`react-router-dom`](https://github.com/ReactTraining/react-router) y sus definiciones para typescript. + +```bash +npm install react-router-dom --save +npm install @types/react-router-dom --save-dev +``` + +- Vamos a definir el enrutado en _[main.tsx](./src/main.tsx)_ : + +### ./src/main.tsx + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +- import {App} from './app'; ++ import { HashRouter, Switch, Route } from 'react-router-dom'; ++ import {PageA} from './pageA'; ++ import {PageB} from './pageB'; + +ReactDOM.render( +- ++ ++ ++ ++ ++ ++ + , document.getElementById('root') +); +``` + +- Es hora de verificar que estamos siguiendo el camino correcto: + +```bash +npm start +``` + +- Vamos a definir la navegación de _[PageA.tsx](./src/pageA.tsx)_ a _[PageB.tsx](./src/pageB.tsx)_. + +### ./src/pageA.tsx + +```diff +import * as React from "react" ++ import { Link } from 'react-router-dom'; + +export const PageA = () => +
    +

    Hello from page A

    ++
    ++ Navigate to Page B +
    +``` + +- Vamos a definir la navegación de _[PageB.tsx](./src/pageB.tsx)_ a _[PageA.tsx](./src/pageA.tsx)_ + +### ./src/pageB.tsx + +```diff +import * as React from "react" ++ import { Link } from 'react-router-dom'; + +export const PageB = () => +
    +

    Hello from page B

    ++
    ++ Navigate to Page A +
    +``` + +- Ejecutamos la aplicación y comprobamos que la navegación funciona correctamente. + +```bash +npm start +``` \ No newline at end of file From 64a0de74486eb07c7f0a901920c17bc429cf93e5 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 14:55:24 +0200 Subject: [PATCH 041/180] Update README.md Small corrections to ease readability. --- 00 Boilerplate/readme.md | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/00 Boilerplate/readme.md b/00 Boilerplate/readme.md index 84b8268..eb030d9 100644 --- a/00 Boilerplate/readme.md +++ b/00 Boilerplate/readme.md @@ -1,12 +1,11 @@ # 00 Boilerplate -In this sample we are going to setup the basic plumbing to "build" our project and launch it in a dev server. +In this sample we setup the basic plumbing to "build" our project and launch it in a dev server. -We won't install anything related about React, just some basic plumbing. In sample 01 we will start by importing -React and ReactDOM. +We won't install anything related to React, but some basic plumbing. React and ReactDOM are imported in sample 01. -We will setup an initial npm project, give support to TypeScript, and install React.
    -Then we will create a **helloworld.ts** sample. +We setup an initial npm project, give support to TypeScript, and install React.
    +Then we create a **helloworld.ts** sample. Summary steps: @@ -18,7 +17,7 @@ Summary steps: - Babel. - Bootstrap. - Setup **[./webpack.config.js](./webpack.config.js)** -- Create a test js file. +- Create a test JS file. - Create a simple HTML file. # Prerequisites @@ -31,8 +30,8 @@ Install [Node.js and npm](https://nodejs.org/en/) (v8.9.1) if they are not alrea - Create and navigate to the folder where you are going to create the empty project. -- Execute `npm init`, you will be prompted to answer some information request about the project (e.g. set name to _samplereact_ and description to _Sample working with React,TypeScript and Webpack_). -Once you have successfully fullfilled them a **[./package.json](./package.json)** file we will generated. +- Execute `npm init`. You are prompted to answer some questions about the project (e.g. set name to _samplereact_ and description to _Sample working with React,TypeScript and Webpack_). +Once you have successfully answered them, a **[./package.json](./package.json)** file is generated. ```bash npm init @@ -49,13 +48,13 @@ Once you have successfully fullfilled them a **[./package.json](./package.json)* npm install webpack-dev-server --save-dev ``` -- Let's install a list of plugins and loaders that will add powers to our webpack configuration (handling CSS, TypeScript...). +- Let's install a list of plugins and loaders to add capabilities to our webpack configuration (handling CSS, TypeScript...). ```bash npm install css-loader style-loader file-loader url-loader html-webpack-plugin awesome-typescript-loader mini-css-extract-plugin --save-dev ``` -- Let's add two commands to our **[./package.json](./package.json)** to build and start. +- Let's add two commands to our **[./package.json](./package.json)**: build and start. _[./package.json](./package.json)_ ```diff @@ -65,7 +64,7 @@ _[./package.json](./package.json)_ }, ``` -- Let's install locally TypeScript: +- Let's install TypeScript locally: ```bash npm install typescript --save-dev @@ -100,7 +99,7 @@ _[./tsconfig.json](./tsconfig.json)_ npm install babel-core babel-preset-env --save-dev ``` - - Babel needs to be configured for works. We will create one file **[./.babelrc](./.babelrc)** in root and later we will see how to put it in **[./webpack.config.js](./webpack.config.js)**. In this example, we will use this .babelrc: + - Babel needs to be configured for it to work. We create **[./.babelrc](./.babelrc)** in the root folder. Later we will see how to put it in **[./webpack.config.js](./webpack.config.js)**. In this example, we use this .babelrc: _[./.babelrc](./.babelrc)_ ```json @@ -122,7 +121,7 @@ _[./.babelrc](./.babelrc)_ npm install bootstrap --save ``` -- Now, our **[./package.json](./package.json)** file should looks something like: +- Now, our **[./package.json](./package.json)** file should look something like: _[./package.json](./package.json)_ ```json @@ -190,11 +189,10 @@ _[./src/index.html](./src/index.html)_ ``` -- Now it's time to create a basic **[./webpack.config.js](./webpack.config.js)** file, this configuration will - include plumbing for: +- Now it's time to create a basic **[./webpack.config.js](./webpack.config.js)** file. This configuration includes plumbing for: - Launching a web dev server. - Transpiling from TypeScript to JavaScript. - - Setup Twitter Bootstrap (including fonts, etc...). + - Setting up Twitter Bootstrap (including fonts, etc...). - Generating the build under a **dist** folder. _[./webpack.config.js](./webpack.config.js)_ @@ -265,7 +263,7 @@ module.exports = { }; ``` -- Run webpack with: +- Run webpack: ```bash npm start From fe9a1153797c37a500f9c159da92a77f625847df Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 15:12:43 +0200 Subject: [PATCH 042/180] Update README.md Small corrections to ease the readability. --- 01 HelloReact/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/01 HelloReact/readme.md b/01 HelloReact/readme.md index 9d3381a..348dc2a 100644 --- a/01 HelloReact/readme.md +++ b/01 HelloReact/readme.md @@ -1,8 +1,8 @@ # 01 Hello React -In this sample we will create our first react component and connect it with the DOM via react-dom. +In this example we create our first react component and connect it with the DOM via react-dom. -We will take a startup point sample _00 Boilerplate_. +We take a startup point sample _00 Boilerplate_. Summary steps: @@ -20,7 +20,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v8.6.0 or newer) if they are ## Steps to build it -- Copy the content of the `00 Boilerplate` folder to an empty folder for the sample. +- Copy the content of the `00 Boilerplate` folder to an empty folder for this example. - Install the npm packages described in the [./package.json](./package.json) and verify that it works: From f3f38d1d5ba8eee3a858a955750e55d47bd48454 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 15:22:53 +0200 Subject: [PATCH 043/180] Update README.md ES6 variant for hello.tsx --- 01 HelloReact/readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/01 HelloReact/readme.md b/01 HelloReact/readme.md index 9d3381a..ca3e1bd 100644 --- a/01 HelloReact/readme.md +++ b/01 HelloReact/readme.md @@ -69,6 +69,16 @@ export const HelloComponent = () => { ); } ``` +Side note: in ES6, this can written as + +```jsx +import * as React from 'react'; + +export const HelloComponent = () => ( +

    Hello component !

    +); + + ``` - Wire up this component by using `react-dom` under [./src/main.tsx](./src/main.tsx) (we have to rename this file extension from `ts` to `tsx` and replace the content). From 7551becd1367781fe169179fbf91beedb4ceef19 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 15:38:53 +0200 Subject: [PATCH 044/180] Update README.md Small corrections to ease the readability. Alternative ES6 implementation of hello.tsx. --- 02 Properties/readme.md | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/02 Properties/readme.md b/02 Properties/readme.md index 5240c24..0949f05 100644 --- a/02 Properties/readme.md +++ b/02 Properties/readme.md @@ -1,16 +1,15 @@ # 02 Properties -In this sample we will introduce a basic React concept, handling properties. +In this example we introduce a basic React concept: handling properties. -We will add a _username_ property and display it in the _hello_ component. +We add a _username_ property and display it in the _hello_ component. -We will take a startup point sample **01 Hello React**: +We take as startup point the example **01 Hello React**: ### Summary steps: -- _hello_ stateless component: create a property that will hold the _username_ value. - -- Let's inform it from our parent control. +- _hello_ stateless component: create a property to hold the _username_ value. +- Let's provide a value from our parent control. ## Prerequisites @@ -26,7 +25,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not alrea npm install ``` -- Let's update _hello.tsx_ in order to reflect the new property added (_userName_) and display it using interpolation (_{userName}_): +- Let's update _hello.tsx_ to add a new property (_userName_) and display it using interpolation (_{userName}_): _hello.tsx_ @@ -42,7 +41,27 @@ import * as React from 'react'; } ``` -- Let's update _main.tsx_ and inform the _userName_ property value: +Side note: using interfaces and ES6, the change looks like this: + +```diff +import * as React from 'react'; + ++ interface Props ++ { ++ username: string; ++ } + +- export const HelloComponent = () => { ++ export const HelloComponent = (props: Props) => ( +- return ( +-

    Hello component !

    ++

    Hello user: {props.userName} !

    + ); +-} +``` + + +- Let's update _main.tsx_ and provide a value to the _userName_ property: ```diff import * as React from 'react'; From 84aa4e27de521754618a3817b63c69ac73eb8b4c Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 16:35:30 +0200 Subject: [PATCH 045/180] Update README.md Small corrections to ease the readability. Additional implementations in ES6. Additional side notes. --- 03 State/readme.md | 57 ++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/03 State/readme.md b/03 State/readme.md index 7a750b6..6fd9809 100644 --- a/03 State/readme.md +++ b/03 State/readme.md @@ -1,23 +1,22 @@ # 03 State -In this sample we will introduce a basic React concept, handling State. +In this example we introduce a basic React concept: handling State. -In this scenario we will provide a default username but let the user update -it. +In this scenario we provide a default username and let the user update it. -We will take as a starting point sample _02 Properties_: +We take as a starting point the example _02 Properties_: ## Summary steps: -- Create an _App_ component that will hold the state, this state will contain the current -username (by default assigned to "defaultUserName" value). -This _App_ component will render the _Hello_ component. At first we will create a simple stateless +- Create an _App_ component that holds the state. This state will contain the current +username (with default value "defaultUserName"). +This _App_ component renders the _Hello_ component. At first we create a simple stateless _App_ component. - Update _main.tsx_ file to include our _App_ component. -- Change _App_ component to a statful class component to hold the _userName_ state. -- Create a _NameEdit_ component to let the user change the username. This will change the _App_ state +- Change _App_ component to a stateful class component to hold the _userName_ state. +- Create a _NameEdit_ component to let the user change the value of username. This changes the _App_ state using a function from _App_. -- Check everything is working properly. +- Check everything works properly. ## Prerequisites @@ -44,7 +43,7 @@ export const App = () => { } ``` -- Let's update _main.tsx_ just to use the _App_ component that we have recently created. +- Let's update _main.tsx_ just to use the _App_ component that we have just created. _./src/main.tsx_ @@ -60,15 +59,13 @@ _./src/main.tsx_ ); ``` -- Now we can check that things are still working as expected (nothing broken so far). +- Now we can check that things are still working as expected. ``` npm start ``` -- It's time to revisit _app.tsx_, since we want to store the name of the user and let the -user updated it, let's move this component to a class stateful component and define -a state, including _userName_ and pass this value to the _Hello_ component. +- It's time to revisit _app.tsx_. We want to store the user's name and let the user updated it. Let's move this component to a class stateful component and define a state including _userName_, and pass this value to the _Hello_ component. _./src/app.tsx_ @@ -98,15 +95,13 @@ export class App extends React.Component { } ``` -- Again, we can do a quick check to test that everything is working as expected. +- Again, we can do a quick check to test that everything works as expected. ``` npm start ``` -- Now it's time to create an _NameEdit_ component, this component will let the user -update his username and will notify with a callback to the parent control whenever -the _userName_ gets updated. +- Now it's time to create an _NameEdit_ component. This component lets the user update his username and notifies with a callback to the parent control whenever the value of _userName_ gets updated. _./src/nameEdit.tsx_ @@ -118,20 +113,26 @@ interface Props { onChange : (event) => void; } -export const NameEditComponent = (props : Props) => { - return ( +export const NameEditComponent = (props : Props) => <> <> - ); -} ``` -> What is this Fragment or <> stuff? A way to create component that have multiple root elements (not a single parent) +Side note: What is this Fragment or <> stuff? A way to create component that has multiple root elements (not a single parent). Available from React 16.2. As an alternative you can type: +```jsx + ... + export const NameEditComponent = (props : Props) => + + + + +} +``` -- In the _app.tsx_ file let's add a function to set the changed _userName_ in the state. +- In the _app.tsx_ file, let's add a function to replace the state value of _userName_ with the new one. ```diff import * as React from 'react'; @@ -167,9 +168,11 @@ export const NameEditComponent = (props : Props) => { } ``` -> Note down the fat arrow class method, this will avoid loosing the _this_ context on the callback +Side note: mind the use of the fat arrow function. This avoids losing the context for _this_ in the callback. + +Side note 2: this.setState() will change the value of the state at some point in the future. Do not consider it is a synchronous change - it isn't. Writing logic that depends on the username new value being in the state right after calling this.setState() is wrong and might fail. -- Finally let's test the final sample. +- Finally let's test everything works once more. ``` npm start From 1f779ef1f626ed0dd59772933d8eb238abaf9d56 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 17:10:27 +0200 Subject: [PATCH 046/180] Update README.md Small corrections to ease the readability. --- 04 Callback/readme.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/04 Callback/readme.md b/04 Callback/readme.md index 24be2ea..f46c4cc 100644 --- a/04 Callback/readme.md +++ b/04 Callback/readme.md @@ -1,16 +1,15 @@ # 04 Callback + State -In this sample we are going to refactor the previous sample **03 State**. +In this example we refactor the previous **03 State** example. -We'll update the name property only when the user clicks on -a _change_ button, we will simplify the event itself as well. +We update the name property only when the user clicks a _change_ button, and we simplify the event itself as well. -Obviously, we will take the sample **03 State** as a starting point. +Obviously, we take the example **03 State** as a starting point. Summary steps: - Add a button to the `EditName` component and a handler function for this. -- Submit the name only when the user clicks on the button. +- Submit the name only when the user clicks that button. - Update the `app` component to handle the new simplified event. ## Prerequisites @@ -21,8 +20,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not alrea ## Steps to build it -- Copy the content of the `03 State` folder to an empty folder for the sample -and make this your current folder. +- Copy the content of the `03 State` folder to an empty folder for this example and make this your current folder. - Install the npm packages described in the `package.json` and verify that it works: @@ -30,10 +28,9 @@ and make this your current folder. npm install ``` -- Since we are going to use an internal handler, we'll transform the `NameEditComponent` -from a stateless component into a class component, then we will add some refactor on the naming. +- Since we are going to use an internal handler, we'll transform the `NameEditComponent` from a stateless component into a class component, then we will add some refactor on the naming. - The `nameEdit.tsx` file should looks like this: + The `nameEdit.tsx` file should look like this: _nameEdit.tsx_ @@ -125,7 +122,7 @@ export class App extends React.Component { ``` - Now we've got a clear event, strongly typed and simplified (straight forward). + Now we've got a clear event, strongly typed and simplified (as it is more straightforward). - Let's give it a try: @@ -135,6 +132,6 @@ export class App extends React.Component { - Then, load http://localhost:8080/ in a browser to see the output. - Now, the greeting only change when the user clicks on the change button. + Now, the greetings message only changes when the user clicks the change button. -> What happens if we simulate an AJAX call, let's place in the app on componentWillMount a timeout and set the name value. \ No newline at end of file +> What happens if we simulate an AJAX call? Let's place in the app's componentWillMount a timeout and set the name value in the timeout's callback. From 26a336dad74b847a6236143a9fff196193f225fa Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 17:55:43 +0200 Subject: [PATCH 047/180] Update README.md To add this.setState() callback side note. --- 03 State/readme.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/03 State/readme.md b/03 State/readme.md index 6fd9809..2773271 100644 --- a/03 State/readme.md +++ b/03 State/readme.md @@ -170,7 +170,17 @@ Side note: What is this Fragment or <> stuff? A way to create component that has Side note: mind the use of the fat arrow function. This avoids losing the context for _this_ in the callback. -Side note 2: this.setState() will change the value of the state at some point in the future. Do not consider it is a synchronous change - it isn't. Writing logic that depends on the username new value being in the state right after calling this.setState() is wrong and might fail. +Side note 2: this.setState() will change the value of the state at some point in the future. Do not consider it is a synchronous change - it isn't. Writing logic that depends on the username new value being in the state right after calling this.setState() is wrong and might fail. If you need to write code dependent on the new value being in the state, use a callback as second parameter of this.setState(). See this example + +``` + setUserNameState = (newName: string) => { + this.setState({userName: newName{, this.nameChanged); + } + + nameChanged() { + /* logic here gets invoked after the new name value in the state is set. */ + } +``` - Finally let's test everything works once more. From c812e0a7282dbffd90cfc413b1687a47ee665683 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 17:56:44 +0200 Subject: [PATCH 048/180] Update README.md Typo. --- 03 State/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/03 State/readme.md b/03 State/readme.md index 2773271..ae91be3 100644 --- a/03 State/readme.md +++ b/03 State/readme.md @@ -174,7 +174,7 @@ Side note 2: this.setState() will change the value of the state at some point in ``` setUserNameState = (newName: string) => { - this.setState({userName: newName{, this.nameChanged); + this.setState({userName: newName}, this.nameChanged); } nameChanged() { From a8c1eceb66684b5e998bf168cc0eaa53d0a9fad6 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Thu, 31 May 2018 18:33:01 +0200 Subject: [PATCH 049/180] Update README.md Small corrections to ease the readability. --- 05 Refactor/readme.md | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/05 Refactor/readme.md b/05 Refactor/readme.md index e2a478f..683a3cb 100644 --- a/05 Refactor/readme.md +++ b/05 Refactor/readme.md @@ -1,18 +1,14 @@ # 05 Refactor -In the previous sample we were setting an initial username value, what would -happen if we expect this value to come from e.g. an AJAX request or if it could -change in time? The current approach won't work. +In the previous example we set an initial username value. What happens if we expect this value to come from e.g. an AJAX request or if it could change in time? The current approach doesn't work. We can think about two possible solutions: -- The first idea that could come into our mind is to implement a mix: we receive via props the current name value, then we hold an state with the current editing -value... what drawbacks could we encounter? We have to listen on the getDerivedStateFromProps (componentWillRecieveProps has been deprecated) for any change on the parent user name control and replace our state, we end up with a mixed governance. +- The first idea that could come into our mind is to implement a mix: we receive via props the current name value, then we hold an state with the current editingvalue... what drawbacks could we encounter? We have to listen on the getDerivedStateFromProps (componentWillRecieveProps has been deprecated) for any change on the parent user name control and replace our state. With this approach We end up with a mixed governance. > More info about getDerivedStateFromProps: https://medium.com/@baphemot/whats-new-in-react-16-3-d2c9b7b6193b -And update of how it would look like (using the new static method -getDerivedStateFromProps): +With this solution, the code looks like this (using the new static method getDerivedStateFromProps): Props and interface: @@ -52,20 +48,14 @@ Inside the class component } ``` -- The second idea is to setup two properties, the parent control will hold _userName_ and _editingUsername__, whenever the user clicks on the button to -replace the name it will notify the parent control and it will replace the -content of _userName_ with the content from _editingUsername_. If _userName_ gets updated by any other third party (e.g. AJAX callback) it will update as well -_editingUsername_. +- The second idea is to setup two properties, the parent control will hold _userName_ and _editingUsername__. Whenever the user clicks the button to replace the name, it will notify the parent control and it will replace the content of _userName_ with the content from _editingUsername_. If _userName_ gets updated by any other third party (e.g. AJAX callback) it will update as well _editingUsername_. -We will take as a starting point sample _04 Callback_: +We take as a starting point sample _04 Callback_: Summary steps: - Update _nameEdit.tsx_ in order to request the new _editingUsername_, and remove it from the state. - -- Update _app.tsx_ to hold the new editing property in the state, pass it to the -children, control and perform the proper update on the callback event from the -child control. +- Update _app.tsx_ to hold the new editing property in the state, pass it to the children, control and perform the proper update on the callback event from the child control. ## Prerequisites @@ -77,8 +67,7 @@ Install [Node.js and npm](https://nodejs.org/en/) if they are not already instal - Copy the content from _04 Callback_ and execute `npm install`. -- Update _nameEdit.tsx_ in order to request the new _editingUsername_, and remove it -from the state. +- Update _nameEdit.tsx_ in order to request the new _editingUsername_, and remove it from the state. _nameEdit.tsx_ @@ -135,9 +124,7 @@ interface Props { } ``` -- Update _app.tsx_ to hold the new editing property in the state, pass it to the -children control and perform the proper update on the callback event from the -child control. +- Update _app.tsx_ to hold the new editing property in the state, pass it to the children controls and perform the proper update on the callback event from the child control. ```diff import * as React from 'react'; @@ -187,5 +174,4 @@ export class App extends React.Component { } ``` -Finally we can check the sample is working as _04 Callback_ executing from the command line -`npm start` and opening [http://localhost:8080](http://localhost:8080). +Finally we can check the example is working as in _04 Callback_ by executing from the command line `npm start` and opening [http://localhost:8080](http://localhost:8080). From 6d4eba809c2f9d2292fd3618dc55db7dd8df0823 Mon Sep 17 00:00:00 2001 From: Francis Tocino Date: Fri, 1 Jun 2018 10:20:35 +0200 Subject: [PATCH 050/180] 16Validadrion. In the file input.tsx, change the sign of the aributo type of "text" to {props.type} --- 16 Validation/src/common/forms/input/input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/16 Validation/src/common/forms/input/input.tsx b/16 Validation/src/common/forms/input/input.tsx index 4497963..c2d0568 100644 --- a/16 Validation/src/common/forms/input/input.tsx +++ b/16 Validation/src/common/forms/input/input.tsx @@ -28,7 +28,7 @@ export const Input : React.StatelessComponent = (props) =>
    - Date: Fri, 1 Jun 2018 14:34:57 +0200 Subject: [PATCH 051/180] Update README.md "as State" is not needed as seen in the IV edition video. --- 05 Refactor/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/05 Refactor/readme.md b/05 Refactor/readme.md index e2a478f..ebcd915 100644 --- a/05 Refactor/readme.md +++ b/05 Refactor/readme.md @@ -108,7 +108,7 @@ interface Props { } - onChange = (event) => { -- this.setState({editingName: event.target.value} as State); +- this.setState({editingName: event.target.value}); - } - onNameSubmit = (event) => { @@ -165,11 +165,11 @@ export class App extends React.Component { - setUsernameState = (newName: string) => { + setUsernameState = () => { - this.setState({userName: newName}); -+ this.setState({userName: this.state.editingUserName} as State); ++ this.setState({userName: this.state.editingUserName}); } + updateEditingName = (editingName : string) : void => { -+ this.setState({editingUserName: editingName} as State); ++ this.setState({editingUserName: editingName}); + } public render() { From 8ad68a742c4d233f435786365504a0ac5f38c028 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Fri, 1 Jun 2018 14:46:51 +0200 Subject: [PATCH 052/180] Update README.md Small corrections to ease the readability. Additional side notes. --- 06 MoveBackToStateless/readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/06 MoveBackToStateless/readme.md b/06 MoveBackToStateless/readme.md index 322f7c1..9e47e66 100644 --- a/06 MoveBackToStateless/readme.md +++ b/06 MoveBackToStateless/readme.md @@ -4,11 +4,11 @@ In example 05 we learned how to remove state from a child control just to have c It's time to make some cleanup, let's simplify _[nameEdit.tsx](./src/nameEdit.tsx)_ component and move it as a stateless component. -We will take a startup point sample _[05 Refactor](./../05%20Refactor)_. +Let's take example _[05 Refactor](./../05%20Refactor)_ as reference. Summary steps: -- Update _[nameEdit.tsx](./src/nameEdit.tsx)_, port it to stateless component and add the methods inline. +- Update _[nameEdit.tsx](./src/nameEdit.tsx)_, transform it to a stateless component and inline some methods. ## Prerequisites @@ -20,7 +20,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Copy the content from _[05 Refactor](./../05%20Refactor)_ and execute `npm install`. -- Update _[nameEdit.tsx](./src/nameEdit.tsx)_, port it to stateless component and add the methods inline. It should look like: +- Update _[nameEdit.tsx](./src/nameEdit.tsx)_, transform it to stateless component and inline some methods. It should look like this: ```jsx import * as React from 'react'; @@ -33,7 +33,6 @@ interface Props { onNameUpdateRequest : () => void; } - export const NameEditComponent = (props : Props) =>
    @@ -43,9 +42,10 @@ export const NameEditComponent = (props : Props) =>
    ``` +Side note: when refactoring this code, we have replaced ```this.props``` by ```props```. This is because ```NameEditComponent``` is now a function, not a class. If you keep ```this.props```, it fails in runtime because ```this``` is now undefined. -- Now we can run the sample and we will get same results +- Now we can run the example, and we get the same results. ```bash npm start -``` \ No newline at end of file +``` From 0254ea33fdd20580f2b9f672b3c0562d5c94fa92 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Fri, 1 Jun 2018 15:58:24 +0200 Subject: [PATCH 053/180] Update README.md Small corrections to ease the readability. Additional side notes. --- 08 Colorpicker/readme.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/08 Colorpicker/readme.md b/08 Colorpicker/readme.md index 16e4f2d..ff7603e 100644 --- a/08 Colorpicker/readme.md +++ b/08 Colorpicker/readme.md @@ -1,15 +1,13 @@ # 08 Colorpicker +We take _01 HelloReact_ as reference. - -We will take a startup point sample _01 HelloReact_: - ->This sample is based on the following [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), but adding some variations. +>This example is based on the following [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), but adding some variations. Summary steps: - Rename _hello.tsx_ file to _colorpicker.tsx_. -- Define the properties and state. +- Define properties and state. - Create the UI. @@ -49,7 +47,7 @@ export const ColorPicker = () => { } ``` -- Let's create an indermediate _app.tsx_ file like we did in some previous samples: +- Let's create an indermediate _app.tsx_ file as we did in some of the previous examples: _./src/app.tsx_ @@ -83,7 +81,7 @@ export class App extends React.Component<{}, State> { } ``` -- We need to update _main.tsx_ to indicate the change +- We need to update _main.tsx_ to adjust it to the change: _./src/main.tsx_ @@ -99,8 +97,7 @@ _./src/main.tsx_ document.getElementById('root')); ``` -- We are going to change as well the content of the file let's define a color and callback -as a property to setup the color (_colorpicker.tsx_). +- We are going to change as well the content of the file. Let's define a color and a callback (as a property) to set the color (_colorpicker.tsx_). _./src/colorpicker.tsx_ @@ -144,7 +141,7 @@ _./src/colorpicker.tsx_ } ``` -- Now it's time to update _app.tsx_ to interact with the components props. +- Now it's time to update _app.tsx_ to interact with the component's props. _./src/app.tsx_ @@ -181,7 +178,7 @@ _./src/app.tsx_ ``` -- Let's give a try and check that we got the basics working +- Let's give a try and check that we got the basics working. ``` npm start @@ -189,7 +186,7 @@ _./src/app.tsx_ - Let's complete the component by adding sliders for the green and blue options: -> Note: this will look a bit ugly, in the next sample we will refactor this to a cleaner solution +> Note: this will look a bit ugly, in the next example we will refactor this to a cleaner solution. _./src/colopicker.tsx_ @@ -244,8 +241,7 @@ _./src/colopicker.tsx_ } ``` -- Let's make this a bit more visual, it would be a good idea to display a rectangle -filled with the selected color. Let's create a ColorDisplayer component (_colordisplayer.tsx_). +- Let's make this a bit more visual. It would be a good idea to display a rectangle filled with the selected color. Let's create a ColorDisplayer component (_colordisplayer.tsx_). _./src/colordisplayer.tsx_ @@ -258,7 +254,7 @@ _./src/colordisplayer.tsx_ } export const ColorDisplayer = (props : Props) => { - const divStyle = { + const divStyle = React.CSSProperties { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. width: '11rem', height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` @@ -306,7 +302,7 @@ export class App extends React.Component<{}, State> { } ``` -- Let's give a try and check the results +- Let's give a try and check the results. ``` npm start From c8b206c9a0247ce992d0f6307e2e0cd1e8e53d07 Mon Sep 17 00:00:00 2001 From: AurelioGomezRosales <39548788+AurelioGomezRosales@users.noreply.github.com> Date: Fri, 1 Jun 2018 16:13:02 +0200 Subject: [PATCH 054/180] Update README.md Small corrections to ease the readability. Additional side notes. --- 09 ColorpRefactor/readme.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/09 ColorpRefactor/readme.md b/09 ColorpRefactor/readme.md index 42440e0..0ca071a 100644 --- a/09 ColorpRefactor/readme.md +++ b/09 ColorpRefactor/readme.md @@ -1,17 +1,14 @@ # 09 Colorpicker Refactor -In this sample we are going to review the colorpicker component we have created -and simplify it, right now we have three slider controls with many details -that make our HTML hard to read, let's componentize this scenario. +In this example we are going to review the colorpicker component we have created and simplify it. Right now we have three slider controls with many details that make our HTML hard to read. Let's componentize this scenario. -We will take a startup point sample _08 Colorpicker_: +We take _08 Colorpicker_ as reference. Summary steps: -- Create a simple color slide component. +- Create a simple color slider component. - Replace the color slider inputs with the new slider. -- Check result. - +- Check the result. ## Prerequisites @@ -144,13 +141,13 @@ export const ColorPicker = (props : Props) => { } ``` -- Let's give a try and check that everything is still working as expected. +- Let's give it a try and check it works as expected. ``` npm start ``` -- We have still room for improvement, why not having a single handler for all colors? if we currified the colorupdated handler we can ! +- We have still room for improvement. What about using a single handler for all colors? If we currify the colorupdated handler, then we can! ```diff import * as React from 'react'; @@ -162,10 +159,10 @@ interface Props { onColorUpdated: (color: Color) => void; } -+ const updateColor = (props : Props, colorId : keyof Color) => (value) => { ++ const updateColor = (props : Props, colorId : keyof Color) => (value) => { // keyof Color ensures only 'red', 'blue' or 'green' can be passed in. + props.onColorUpdated({ -+ ...props.color, -+ [colorId]: value ++ ...props.color, // this creates a clone of the current props.color object... ++ [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value. + }); + }; @@ -213,7 +210,7 @@ export const ColorPicker = (props: Props) => { } ``` -- Let's give a try to the sample +- Let's give it a try: ``` npm start From eea8a327469837b1620ef65743d743064172bd2e Mon Sep 17 00:00:00 2001 From: Braulio Date: Mon, 4 Jun 2018 22:35:26 +0200 Subject: [PATCH 055/180] 15 login Form updated --- 15 LoginForm/package.json | 41 +++++++++++++++++----------------- 15 LoginForm/webpack.config.js | 19 +++++----------- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/15 LoginForm/package.json b/15 LoginForm/package.json index 0614095..f3c3b40 100644 --- a/15 LoginForm/package.json +++ b/15 LoginForm/package.json @@ -4,33 +4,34 @@ "description": "State basics.", "main": "index.js", "scripts": { - "start": "webpack-dev-server", - "build": "webpack" + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development" }, "author": "Lemoncode and Front End Master Students", "license": "MIT", "dependencies": { - "bootstrap": "~4.0.0", - "react": "~16.2.0", - "react-dom": "~16.2.0", + "bootstrap": "~4.1.1", + "react": "~16.4.0", + "react-dom": "~16.4.0", "react-router-dom": "^4.2.2" }, "devDependencies": { "@types/history": "^4.6.2", - "@types/react-router-dom": "^4.2.3", - "@types/react": "~16.0.36", - "@types/react-dom": "~16.0.3", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "awesome-typescript-loader": "^3.4.1", - "css-loader": "~0.28.9", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "~1.1.6", - "html-webpack-plugin": "~2.30.1", - "style-loader": "~0.20.1", - "typescript": "~2.7.1", - "url-loader": "~0.6.2", - "webpack": "~3.10.0", - "webpack-dev-server": "^2.11.1" + "@types/react": "~16.3.16", + "@types/react-dom": "~16.0.5", + "@types/react-router-dom": "^4.2.7", + "awesome-typescript-loader": "^5.0.0", + "babel-core": "^6.26.3", + "babel-preset-env": "^1.7.0", + "css-loader": "~0.28.11", + "file-loader": "~1.1.11", + "html-webpack-plugin": "~3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "~0.21.0", + "typescript": "~2.9.1", + "url-loader": "~1.0.1", + "webpack": "~4.10.2", + "webpack-cli": "^3.0.2", + "webpack-dev-server": "^3.1.4" } } diff --git a/15 LoginForm/webpack.config.js b/15 LoginForm/webpack.config.js index 9b58da0..5601d52 100644 --- a/15 LoginForm/webpack.config.js +++ b/15 LoginForm/webpack.config.js @@ -1,7 +1,7 @@ var path = require('path'); var webpack = require('webpack'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); var basePath = __dirname; @@ -42,13 +42,7 @@ module.exports = { }, { test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + use: [MiniCssExtractPlugin.loader, "css-loader"] }, // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack // Using here url-loader and file-loader @@ -77,10 +71,9 @@ module.exports = { template: 'index.html', // Name of template in ./src hash: true }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, - }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), ] } From 3d2bbe47fb88993d4f120b0e20312ccf006dc726 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Wed, 6 Jun 2018 11:51:38 +0200 Subject: [PATCH 056/180] Update readme.md --- 16 Validation/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/16 Validation/readme.md b/16 Validation/readme.md index 5f74ae7..1219a79 100644 --- a/16 Validation/readme.md +++ b/16 Validation/readme.md @@ -56,7 +56,7 @@ const defaultProps : Partial = { const buildWrapperClass = (error : string) : string => "form-group" + ( - (props.error && props.error.length > 0) ? + (error && error.length > 0) ? "has-error" : "" ); @@ -286,4 +286,4 @@ _./src/pages/login/loginPageContainer.tsx_ } ``` -> Excercise add property styling using CSS Modules nad proper react alert. \ No newline at end of file +> Excercise add property styling using CSS Modules nad proper react alert. From a5e3c17e00271e8b166c4ebb2820ee3617832a4a Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Wed, 6 Jun 2018 12:47:01 +0200 Subject: [PATCH 057/180] Update readme.md --- 16 Validation/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/16 Validation/readme.md b/16 Validation/readme.md index 1219a79..4d3d3ce 100644 --- a/16 Validation/readme.md +++ b/16 Validation/readme.md @@ -70,7 +70,6 @@ export const Input : React.StatelessComponent = (props) => name={props.name} className="form-control" placeholder={props.placeholder} - ref={props.name} value={props.value} onChange={props.onChange} onBlur={props.onBlur} From 6a5fd00dea5f5312b22dd09759447b21a42f0505 Mon Sep 17 00:00:00 2001 From: Sreekumar Menon Date: Sun, 1 Jul 2018 22:55:45 -0700 Subject: [PATCH 058/180] update readme app.tsx does not have this.state.defaultUserName, should be this.state.editingUserName ? --- 07 Enable/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/07 Enable/readme.md b/07 Enable/readme.md index 746a8b1..217f61a 100644 --- a/07 Enable/readme.md +++ b/07 Enable/readme.md @@ -98,7 +98,7 @@ _[./src/app.tsx](./src/app.tsx)_ <> ++ disabled={props.disable} >Change
    -``` \ No newline at end of file +``` From b9a21204bedc82cdc145e50dabb520b1785705bc Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 9 Oct 2018 11:50:22 +0200 Subject: [PATCH 059/180] updated to babel 7 plus readme updated pending ES --- 00 Boilerplate/.babelrc | 10 - 00 Boilerplate/package-lock.json | 10547 ---------------- 00 Boilerplate/package.json | 31 - 00_Boilerplate/.babelrc | 10 + 00_Boilerplate/package.json | 31 + {00 Boilerplate => 00_Boilerplate}/readme.md | 85 +- .../readme_es.md | 0 .../src/index.html | 2 +- .../src/main.ts | 0 .../tsconfig.json | 2 +- .../webpack.config.js | 28 +- 11 files changed, 100 insertions(+), 10646 deletions(-) delete mode 100644 00 Boilerplate/.babelrc delete mode 100644 00 Boilerplate/package-lock.json delete mode 100644 00 Boilerplate/package.json create mode 100644 00_Boilerplate/.babelrc create mode 100644 00_Boilerplate/package.json rename {00 Boilerplate => 00_Boilerplate}/readme.md (81%) rename {00 Boilerplate => 00_Boilerplate}/readme_es.md (100%) rename {00 Boilerplate => 00_Boilerplate}/src/index.html (95%) rename {00 Boilerplate => 00_Boilerplate}/src/main.ts (100%) rename {00 Boilerplate => 00_Boilerplate}/tsconfig.json (99%) rename {00 Boilerplate => 00_Boilerplate}/webpack.config.js (76%) diff --git a/00 Boilerplate/.babelrc b/00 Boilerplate/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/00 Boilerplate/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/00 Boilerplate/package-lock.json b/00 Boilerplate/package-lock.json deleted file mode 100644 index 9aba4b1..0000000 --- a/00 Boilerplate/package-lock.json +++ /dev/null @@ -1,10547 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@sindresorhus/is": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "dev": true, - "requires": { - "mime-types": "2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "dev": true, - "requires": { - "acorn": "5.5.3" - } - }, - "ajv": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", - "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", - "dev": true, - "requires": { - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1", - "uri-js": "3.0.2" - } - }, - "ajv-keywords": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz", - "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=", - "dev": true - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "1.9.1" - } - }, - "any-observable": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.2.0.tgz", - "integrity": "sha1-xnhwBYADV5AJCD9UrAq6+1wz0kI=", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", - "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", - "dev": true - }, - "array-includes": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", - "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.11.0" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.3.tgz", - "integrity": "sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA==", - "dev": true - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "atob": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", - "integrity": "sha512-SuiKH8vbsOyCALjA/+EINmt/Kdl+TQPrtFgW7XZZcwtryFu9e5kQoX3bjCW6mIvGH1fbeAZZuvwGR5IlBRznGw==", - "dev": true - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000830", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "awesome-typescript-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.0.0.tgz", - "integrity": "sha512-/80vlBnWp5IlERQ0sxRDQfz5voqht02MRNgUdbn90rKHrope6eh0PYr0qepD2TpYYnCSvq0DzdWc8udHM0KefA==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "enhanced-resolve": "4.0.0", - "loader-utils": "1.1.0", - "lodash": "4.17.5", - "micromatch": "3.1.10", - "mkdirp": "0.5.1", - "source-map-support": "0.5.4" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.1", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.1", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.5", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.8", - "slash": "1.0.0", - "source-map": "0.5.7" - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.5", - "source-map": "0.5.7", - "trim-right": "1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - } - } - }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", - "dev": true, - "requires": { - "babel-helper-bindify-decorators": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", - "dev": true - }, - "babel-plugin-syntax-class-constructor-call": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", - "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-export-extensions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", - "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", - "dev": true - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-generators": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "6.24.1", - "babel-plugin-syntax-async-functions": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-class-constructor-call": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", - "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", - "dev": true, - "requires": { - "babel-plugin-syntax-class-constructor-call": "6.18.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-plugin-syntax-class-properties": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", - "dev": true, - "requires": { - "babel-helper-explode-class": "6.24.1", - "babel-plugin-syntax-decorators": "6.13.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.5" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" - }, - "dependencies": { - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - } - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", - "babel-plugin-syntax-exponentiation-operator": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-export-extensions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", - "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", - "dev": true, - "requires": { - "babel-plugin-syntax-export-extensions": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", - "dev": true, - "requires": { - "babel-plugin-syntax-flow": "6.18.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", - "dev": true, - "requires": { - "babel-plugin-syntax-object-rest-spread": "6.13.0", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "0.10.1" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-preset-env": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0", - "browserslist": "2.11.3", - "invariant": "2.2.4", - "semver": "5.5.0" - }, - "dependencies": { - "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", - "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", - "dev": true, - "requires": { - "caniuse-lite": "1.0.30000830", - "electron-to-chromium": "1.3.42" - } - } - } - }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0" - } - }, - "babel-preset-stage-1": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", - "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", - "dev": true, - "requires": { - "babel-plugin-transform-class-constructor-call": "6.24.1", - "babel-plugin-transform-export-extensions": "6.22.0", - "babel-preset-stage-2": "6.24.1" - } - }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", - "dev": true, - "requires": { - "babel-plugin-syntax-dynamic-import": "6.18.0", - "babel-plugin-transform-class-properties": "6.24.1", - "babel-plugin-transform-decorators": "6.24.1", - "babel-preset-stage-3": "6.24.1" - } - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", - "dev": true, - "requires": { - "babel-plugin-syntax-trailing-function-commas": "6.22.0", - "babel-plugin-transform-async-generator-functions": "6.24.1", - "babel-plugin-transform-async-to-generator": "6.24.1", - "babel-plugin-transform-exponentiation-operator": "6.24.1", - "babel-plugin-transform-object-rest-spread": "6.26.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.5", - "home-or-tmp": "2.0.0", - "lodash": "4.17.5", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" - }, - "dependencies": { - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - } - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.5", - "regenerator-runtime": "0.11.1" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.5" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.5" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.5", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "base64-js": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", - "integrity": "sha512-MsAhsUW1GxCdgYSO6tAfZrNapmUKk7mWx/k5mFY/A1gBtkaCaNapTg+FExCw1r9yeaZhqx/xPg43xgTFH6KL5w==", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true - }, - "binaryextensions": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.1.1.tgz", - "integrity": "sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==", - "dev": true - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.16" - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "2.1.1", - "deep-equal": "1.0.1", - "dns-equal": "1.0.0", - "dns-txt": "2.0.2", - "multicast-dns": "6.2.3", - "multicast-dns-service-types": "1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "bootstrap": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.0.tgz", - "integrity": "sha512-kCo82nE8qYVfOa/Z3hL98CPgPIEkh6iPdiJrUJMQ9n9r0+6PEET7cmhLlV0XVYmEj5QtKIOaSGMLxy5jSFhKog==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "dev": true, - "requires": { - "browserify-aes": "1.2.0", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.3" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "1.0.6" - } - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "1.0.30000830", - "electron-to-chromium": "1.3.42" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.2.3", - "ieee754": "1.1.11", - "isarray": "1.0.0" - } - }, - "buffer-from": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.2", - "mississippi": "2.0.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.3.0", - "unique-filename": "1.1.0", - "y18n": "4.0.0" - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" - } - }, - "cacheable-request": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", - "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", - "dev": true, - "requires": { - "clone-response": "1.0.2", - "get-stream": "3.0.0", - "http-cache-semantics": "3.8.1", - "keyv": "3.0.0", - "lowercase-keys": "1.0.0", - "normalize-url": "2.0.1", - "responselike": "1.0.2" - }, - "dependencies": { - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", - "dev": true - }, - "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "dev": true, - "requires": { - "prepend-http": "2.0.0", - "query-string": "5.1.1", - "sort-keys": "2.0.0" - } - }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, - "query-string": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, - "requires": { - "decode-uri-component": "0.2.0", - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", - "dev": true, - "requires": { - "is-plain-obj": "1.1.0" - } - } - } - }, - "camel-case": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", - "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", - "dev": true, - "requires": { - "no-case": "2.3.2", - "upper-case": "1.1.3" - } - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" - } - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000830", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" - } - }, - "caniuse-db": { - "version": "1.0.30000830", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000830.tgz", - "integrity": "sha1-bkUlWzRWSf0V/1kHLaHhK7PeLxM=", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30000830", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000830.tgz", - "integrity": "sha512-yMqGkujkoOIZfvOYiWdqPALgY/PVGiqCHUJb6yNq7xhI/pR+gQO0U2K6lRDqAiJv4+CIU3CtTLblNGw0QGnr6g==", - "dev": true - }, - "chalk": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", - "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.3.0" - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "chokidar": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", - "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", - "dev": true, - "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.2", - "fsevents": "1.1.3", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.0.4" - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true - }, - "chrome-trace-event": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-0.1.2.tgz", - "integrity": "sha1-kPNohdU0WlBiEzLwcXtZWIPV2YI=", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "1.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "clean-css": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz", - "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "2.0.0" - } - }, - "cli-spinners": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", - "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", - "dev": true - }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "requires": { - "colors": "1.0.3" - }, - "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - } - } - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "dev": true, - "requires": { - "slice-ansi": "0.0.4", - "string-width": "1.0.2" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.0.0.tgz", - "integrity": "sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw==", - "dev": true, - "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", - "dev": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "1.0.0" - } - }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", - "dev": true - }, - "cloneable-readable": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz", - "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "process-nextick-args": "2.0.0", - "readable-stream": "2.3.6" - } - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "1.5.1" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" - } - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "1.0.4", - "color-convert": "1.9.1", - "color-string": "0.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "0.11.4", - "css-color-names": "0.0.4", - "has": "1.0.1" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "compressible": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", - "integrity": "sha1-DRAgq5JLL9tNYnmHXH1tq6a6p6k=", - "dev": true, - "requires": { - "mime-db": "1.33.0" - } - }, - "compression": { - "version": "1.7.2", - "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz", - "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=", - "dev": true, - "requires": { - "accepts": "1.3.5", - "bytes": "3.0.0", - "compressible": "2.0.13", - "debug": "2.6.9", - "on-headers": "1.0.1", - "safe-buffer": "5.1.1", - "vary": "1.1.2" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "1.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" - } - }, - "connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-js": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", - "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.11" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.2", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", - "randombytes": "2.0.6", - "randomfill": "1.0.4" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-loader": { - "version": "0.28.11", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", - "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "css-selector-tokenizer": "0.7.0", - "cssnano": "3.10.0", - "icss-utils": "2.1.0", - "loader-utils": "1.1.0", - "lodash.camelcase": "4.3.0", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-modules-extract-imports": "1.2.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0", - "postcss-value-parser": "3.3.0", - "source-list-map": "2.0.0" - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", - "domutils": "1.5.1", - "nth-check": "1.0.1" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" - } - }, - "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", - "dev": true - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.1", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "1.0.2" - } - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "dargs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", - "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=", - "dev": true - }, - "date-fns": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", - "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==", - "dev": true - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "1.0.0" - } - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "dev": true, - "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "6.1.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "p-map": "1.2.0", - "pify": "3.0.0", - "rimraf": "2.6.2" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-conflict": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.1.tgz", - "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "detect-node": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", - "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "1.1.5", - "safe-buffer": "5.1.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "1.1.1" - } - }, - "dom-converter": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz", - "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", - "dev": true, - "requires": { - "utila": "0.3.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", - "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "duplexify": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.4.tgz", - "integrity": "sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "stream-shift": "1.0.0" - } - }, - "editions": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", - "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "ejs": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.8.tgz", - "integrity": "sha512-QIDZL54fyV8MDcAsO91BMH1ft2qGGaHIJsJIA/+t+7uvXol1dm413fPcUgUb4k8F/9457rx4/KFE4XfDifrQxQ==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.42", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.42.tgz", - "integrity": "sha1-lcM78B0MxAVVauyJn+Yf1NduoPk=", - "dev": true - }, - "elegant-spinner": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", - "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", - "dev": true - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz", - "integrity": "sha512-jox/62b2GofV1qTUQTMPEJSDIGycS43evqYzD/KVtEb9OCoki9cnacUPxCrZa7JfPzZSYOCZhu9O9luaMxAX8g==", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "tapable": "1.0.0" - } - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - }, - "envinfo": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-4.4.2.tgz", - "integrity": "sha512-5rfRs+m+6pwoKRCFqpsA5+qsLngFms1aWPrxfKbrObCzQaPc3M3yPloZx+BL9UE3dK58cxw36XVQbFRSCCfGSQ==", - "dev": true - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "1.0.1" - } - }, - "error": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz", - "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", - "dev": true, - "requires": { - "string-template": "0.2.1", - "xtend": "4.0.1" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es-abstract": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", - "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", - "dev": true, - "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.1", - "is-callable": "1.1.3", - "is-regex": "1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "1.1.3", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.2.0" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "4.2.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", - "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", - "dev": true - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", - "dev": true, - "requires": { - "original": "1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - }, - "dependencies": { - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "1.0.1" - } - }, - "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", - "dev": true, - "requires": { - "accepts": "1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.3", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", - "utils-merge": "1.0.1", - "vary": "1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "0.4.2", - "iconv-lite": "0.4.19", - "tmp": "0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5" - } - }, - "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.5" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "make-dir": "1.2.0", - "pkg-dir": "2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "first-chunk-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", - "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", - "dev": true, - "requires": { - "readable-stream": "2.3.6" - } - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, - "flow-parser": { - "version": "0.69.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.69.0.tgz", - "integrity": "sha1-N4tRKNbQtVSosvFqTKPhq5ZJ8A4=", - "dev": true - }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.6" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "1.0.2", - "hawk": "3.1.3", - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "gh-got": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-6.0.0.tgz", - "integrity": "sha512-F/mS+fsWQMo1zfgG9MD8KWvTWPPzzhuVwY++fhQ5Ggd+0P+CAMHtzMZhNxG+TqGfHDChJKsbh6otfMGqO2AKBw==", - "dev": true, - "requires": { - "got": "7.1.0", - "is-plain-obj": "1.1.0" - }, - "dependencies": { - "got": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", - "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", - "dev": true, - "requires": { - "decompress-response": "3.3.0", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "is-plain-obj": "1.1.0", - "is-retry-allowed": "1.1.0", - "is-stream": "1.1.0", - "isurl": "1.0.0", - "lowercase-keys": "1.0.1", - "p-cancelable": "0.3.0", - "p-timeout": "1.2.1", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "url-parse-lax": "1.0.0", - "url-to-options": "1.0.1" - } - }, - "p-cancelable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", - "dev": true - }, - "p-timeout": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", - "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", - "dev": true, - "requires": { - "p-finally": "1.0.0" - } - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "1.0.4" - } - } - } - }, - "github-username": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/github-username/-/github-username-4.1.0.tgz", - "integrity": "sha1-y+KABBiDIG2kISrp5LXxacML9Bc=", - "dev": true, - "requires": { - "gh-got": "6.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-all": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", - "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", - "dev": true, - "requires": { - "glob": "7.1.2", - "yargs": "1.2.6" - }, - "dependencies": { - "minimist": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", - "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=", - "dev": true - }, - "yargs": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", - "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", - "dev": true, - "requires": { - "minimist": "0.1.0" - } - } - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - }, - "dependencies": { - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - } - } - }, - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "1.0.2", - "is-windows": "1.0.2", - "resolve-dir": "1.0.1" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "2.0.2", - "homedir-polyfill": "1.0.1", - "ini": "1.3.5", - "is-windows": "1.0.2", - "which": "1.3.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "got": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/got/-/got-8.3.0.tgz", - "integrity": "sha512-kBNy/S2CGwrYgDSec5KTWGKUvupwkkTVAjIsVFF2shXO13xpZdFP4d4kxa//CLX2tN/rV0aYwK8vY6UKWGn2vQ==", - "dev": true, - "requires": { - "@sindresorhus/is": "0.7.0", - "cacheable-request": "2.1.4", - "decompress-response": "3.3.0", - "duplexer3": "0.1.4", - "get-stream": "3.0.0", - "into-stream": "3.1.0", - "is-retry-allowed": "1.1.0", - "isurl": "1.0.0", - "lowercase-keys": "1.0.1", - "mimic-response": "1.0.0", - "p-cancelable": "0.4.1", - "p-timeout": "2.0.1", - "pify": "3.0.0", - "safe-buffer": "5.1.1", - "timed-out": "4.0.1", - "url-parse-lax": "3.0.0", - "url-to-options": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "grouped-queue": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.3.tgz", - "integrity": "sha1-wWfSpTGcWg4JZO9qJbfC34mWyFw=", - "dev": true, - "requires": { - "lodash": "4.17.5" - } - }, - "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", - "dev": true - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbol-support-x": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true - }, - "has-to-string-tag-x": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", - "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "dev": true, - "requires": { - "has-symbol-support-x": "1.4.2" - } - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", - "dev": true, - "requires": { - "parse-passwd": "1.0.0" - } - }, - "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", - "dev": true - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "obuf": "1.1.2", - "readable-stream": "2.3.6", - "wbuf": "1.7.3" - } - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, - "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", - "dev": true - }, - "html-minifier": { - "version": "3.5.15", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.15.tgz", - "integrity": "sha512-OZa4rfb6tZOZ3Z8Xf0jKxXkiDcFWldQePGYFDcgKqES2sXeWaEv9y6QQvWUtX3ySI3feApQi5uCsHLINQ6NoAw==", - "dev": true, - "requires": { - "camel-case": "3.0.0", - "clean-css": "4.1.11", - "commander": "2.15.1", - "he": "1.1.1", - "param-case": "2.1.1", - "relateurl": "0.2.7", - "uglify-js": "3.3.21" - }, - "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-js": { - "version": "3.3.21", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.21.tgz", - "integrity": "sha512-uy82472lH8tshK3jS3c5IFb5MmNKd/5qyBd0ih8sM42L3jWvxnE339U9gZU1zufnLVs98Stib9twq8dLm2XYCA==", - "dev": true, - "requires": { - "commander": "2.15.1", - "source-map": "0.6.1" - } - } - } - }, - "html-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", - "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", - "dev": true, - "requires": { - "html-minifier": "3.5.15", - "loader-utils": "0.2.17", - "lodash": "4.17.5", - "pretty-error": "2.1.1", - "tapable": "1.0.0", - "toposort": "1.0.6", - "util.promisify": "1.0.0" - }, - "dependencies": { - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - } - } - }, - "htmlparser2": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", - "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", - "dev": true, - "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.1.0", - "domutils": "1.1.6", - "readable-stream": "1.0.34" - }, - "dependencies": { - "domutils": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", - "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": "1.4.0" - } - }, - "http-parser-js": { - "version": "0.4.11", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.11.tgz", - "integrity": "sha512-QCR5O2AjjMW8Mo4HyI1ctFcv+O99j/0g367V3YoVnrNw5hkDvAWZD0lWGcc+F4yN3V55USPCVix4efb75HxFfA==", - "dev": true - }, - "http-proxy": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", - "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", - "dev": true, - "requires": { - "eventemitter3": "1.2.0", - "requires-port": "1.0.0" - } - }, - "http-proxy-middleware": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz", - "integrity": "sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM=", - "dev": true, - "requires": { - "http-proxy": "1.16.2", - "is-glob": "3.1.0", - "lodash": "4.17.5", - "micromatch": "2.3.11" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - } - } - }, - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - } - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true, - "requires": { - "postcss": "6.0.21" - }, - "dependencies": { - "postcss": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz", - "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "ieee754": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", - "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", - "dev": true, - "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "3.1.0", - "chalk": "2.3.2", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.2.0", - "figures": "2.0.0", - "lodash": "4.17.5", - "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rxjs": "5.5.9", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "internal-ip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", - "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", - "dev": true, - "requires": { - "meow": "3.7.0" - } - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "into-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", - "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", - "dev": true, - "requires": { - "from2": "2.3.0", - "p-is-promise": "1.1.0" - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ipaddr.js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", - "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.11.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true - }, - "is-observable": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-0.2.0.tgz", - "integrity": "sha1-s2ExHYPG5dcmyr9eJQsCNxBvWuI=", - "dev": true, - "requires": { - "symbol-observable": "0.2.4" - }, - "dependencies": { - "symbol-observable": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-0.2.4.tgz", - "integrity": "sha1-lag9smGG1q9+ehjb2XYKL4bQj0A=", - "dev": true - } - } - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "dev": true, - "requires": { - "is-number": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "1.0.1" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "3.0.1" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true - }, - "is-scoped": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", - "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", - "dev": true, - "requires": { - "scoped-regex": "1.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "1.1.1" - } - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "istextorbinary": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", - "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", - "dev": true, - "requires": { - "binaryextensions": "2.1.1", - "editions": "1.3.4", - "textextensions": "2.2.0" - } - }, - "isurl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", - "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "dev": true, - "requires": { - "has-to-string-tag-x": "1.4.1", - "is-object": "1.0.1" - } - }, - "js-base64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "1.0.10", - "esprima": "2.7.3" - } - }, - "jscodeshift": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.5.0.tgz", - "integrity": "sha512-JAcQINNMFpdzzpKJN8k5xXjF3XDuckB1/48uScSzcnNyK199iWEc9AxKL9OoX5144M2w5zEx9Qs4/E/eBZZUlw==", - "dev": true, - "requires": { - "babel-plugin-transform-flow-strip-types": "6.22.0", - "babel-preset-es2015": "6.24.1", - "babel-preset-stage-1": "6.24.1", - "babel-register": "6.26.0", - "babylon": "7.0.0-beta.44", - "colors": "1.1.2", - "flow-parser": "0.69.0", - "lodash": "4.17.5", - "micromatch": "2.3.11", - "neo-async": "2.5.1", - "node-dir": "0.1.8", - "nomnom": "1.8.1", - "recast": "0.14.7", - "temp": "0.8.3", - "write-file-atomic": "1.3.4" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "babylon": { - "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", - "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - } - } - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "keyv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "killable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", - "integrity": "sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "listr": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.13.0.tgz", - "integrity": "sha1-ILsLowuuZg7oTMBQPfS+PVYjiH0=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "figures": "1.7.0", - "indent-string": "2.1.0", - "is-observable": "0.2.0", - "is-promise": "2.1.0", - "is-stream": "1.1.0", - "listr-silent-renderer": "1.1.1", - "listr-update-renderer": "0.4.0", - "listr-verbose-renderer": "0.4.1", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "ora": "0.2.3", - "p-map": "1.2.0", - "rxjs": "5.5.9", - "stream-to-observable": "0.2.0", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "1.1.3" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", - "dev": true - }, - "listr-update-renderer": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz", - "integrity": "sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "elegant-spinner": "1.0.1", - "figures": "1.7.0", - "indent-string": "3.2.0", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", - "dev": true - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "1.1.3" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "date-fns": "1.29.0", - "figures": "1.7.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "2.3.2" - } - }, - "log-update": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", - "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "cli-cursor": "1.0.2" - }, - "dependencies": { - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - } - } - }, - "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", - "dev": true - }, - "loglevelnext": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.4.tgz", - "integrity": "sha512-V3N6LAJAiGwa/zjtvmgs2tyeiCJ23bGNhxXN8R+v7k6TNlSlTz40mIyZYdmO762eBnEFymn0Mhha+WuAhnwMBg==", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", - "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", - "dev": true - }, - "make-dir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", - "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "1.0.1" - } - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "1.2.0" - } - }, - "mem-fs": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.3.tgz", - "integrity": "sha1-uK6NLj/Lb10/kWXBLUVRoGXZicw=", - "dev": true, - "requires": { - "through2": "2.0.3", - "vinyl": "1.2.0", - "vinyl-file": "2.0.0" - } - }, - "mem-fs-editor": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-3.0.2.tgz", - "integrity": "sha1-3Qpuryu4prN3QAZ6pUnrUwEFr58=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "deep-extend": "0.4.2", - "ejs": "2.5.8", - "glob": "7.1.2", - "globby": "6.1.0", - "mkdirp": "0.5.1", - "multimatch": "2.1.0", - "rimraf": "2.6.2", - "through2": "2.0.3", - "vinyl": "2.1.0" - }, - "dependencies": { - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "vinyl": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.1.0.tgz", - "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", - "dev": true, - "requires": { - "clone": "2.1.2", - "clone-buffer": "1.0.0", - "clone-stats": "1.0.0", - "cloneable-readable": "1.1.2", - "remove-trailing-separator": "1.1.0", - "replace-ext": "1.0.0" - } - } - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "0.1.7", - "readable-stream": "2.3.6" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", - "dev": true - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "requires": { - "mime-db": "1.33.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mimic-response": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz", - "integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4=", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.0.tgz", - "integrity": "sha512-2Zik6PhUZ/MbiboG6SDS9UTPL4XXy4qnyGjSdCIWRrr8xb6PwLtHE+AYOjkXJWdF0OG8vo/yrJ8CgS5WbMpzIg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "webpack-sources": "1.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.11" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "duplexify": "3.5.4", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.0.3", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "2.0.1", - "pumpify": "1.4.0", - "stream-each": "1.2.2", - "through2": "2.0.3" - } - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "1.3.1", - "thunky": "1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "multimatch": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", - "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", - "dev": true, - "requires": { - "array-differ": "1.0.0", - "array-union": "1.0.2", - "arrify": "1.0.1", - "minimatch": "3.0.4" - } - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "dev": true, - "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" - } - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", - "dev": true - }, - "neo-async": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", - "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", - "dev": true - }, - "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", - "dev": true - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "dev": true, - "requires": { - "lower-case": "1.1.4" - } - }, - "node-dir": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz", - "integrity": "sha1-VfuN62mQcHB/tn+RpGDwRIKUx30=", - "dev": true - }, - "node-forge": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", - "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", - "dev": true - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", - "dev": true, - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.6", - "stream-browserify": "2.0.1", - "stream-http": "2.8.1", - "string_decoder": "1.1.1", - "timers-browserify": "2.0.6", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "nomnom": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", - "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", - "dev": true, - "requires": { - "chalk": "0.4.0", - "underscore": "1.6.0" - }, - "dependencies": { - "ansi-styles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", - "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", - "dev": true - }, - "chalk": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", - "dev": true, - "requires": { - "ansi-styles": "1.0.0", - "has-color": "0.1.7", - "strip-ansi": "0.1.1" - } - }, - "strip-ansi": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", - "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", - "dev": true - } - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "2.6.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.3" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "2.0.1" - } - }, - "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", - "dev": true, - "requires": { - "boolbase": "1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "3.0.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.11.0" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "3.0.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "1.2.0" - } - }, - "opn": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", - "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", - "dev": true, - "requires": { - "is-wsl": "1.1.0" - } - }, - "ora": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", - "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-spinners": "0.1.2", - "object-assign": "4.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "original": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.0.tgz", - "integrity": "sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs=", - "dev": true, - "requires": { - "url-parse": "1.0.5" - }, - "dependencies": { - "url-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.0.5.tgz", - "integrity": "sha1-CFSGBCKv3P7+tsllxmLUgAFpkns=", - "dev": true, - "requires": { - "querystringify": "0.0.4", - "requires-port": "1.0.0" - } - } - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-cancelable": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", - "dev": true - }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", - "dev": true - }, - "p-lazy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-lazy/-/p-lazy-1.0.0.tgz", - "integrity": "sha1-7FPIAvLuOsKPFmzILQsrAt4nqDU=", - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, - "requires": { - "p-try": "1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.2.0" - } - }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, - "p-timeout": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", - "dev": true, - "requires": { - "p-finally": "1.0.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "param-case": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", - "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", - "dev": true, - "requires": { - "no-case": "2.3.2" - } - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "dev": true, - "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.2.0", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.14" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - }, - "dependencies": { - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - } - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", - "dev": true, - "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.11" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "2.1.0" - } - }, - "portfinder": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", - "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", - "dev": true, - "requires": { - "async": "1.5.2", - "debug": "2.6.9", - "mkdirp": "0.5.1" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.4.3", - "source-map": "0.5.7", - "supports-color": "3.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "1.1.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.1" - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3" - } - }, - "postcss-modules-extract-imports": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", - "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", - "dev": true, - "requires": { - "postcss": "6.0.21" - }, - "dependencies": { - "postcss": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz", - "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.21" - }, - "dependencies": { - "postcss": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz", - "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.21" - }, - "dependencies": { - "postcss": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz", - "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.21" - }, - "dependencies": { - "postcss": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz", - "integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "source-map": "0.6.1", - "supports-color": "5.3.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, - "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "prettier": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.11.1.tgz", - "integrity": "sha512-T/KD65Ot0PB97xTrG8afQ46x3oiVhnfGjGESSI9NWYcG92+OUPZKkwHqGWXH2t9jK1crnQjubECW0FuOth+hxw==", - "dev": true - }, - "pretty-bytes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", - "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", - "dev": true - }, - "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", - "dev": true, - "requires": { - "renderkid": "2.0.1", - "utila": "0.4.0" - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "proxy-addr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", - "dev": true, - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.6.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.6" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" - } - }, - "pumpify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.4.0.tgz", - "integrity": "sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA==", - "dev": true, - "requires": { - "duplexify": "3.5.4", - "inherits": "2.0.3", - "pump": "2.0.1" - } - }, - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-0.0.4.tgz", - "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.1" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "dev": true - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.4.0" - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - } - } - }, - "read-chunk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz", - "integrity": "sha1-agTAkoAF7Z1C4aasVgDhnLx/9lU=", - "dev": true, - "requires": { - "pify": "3.0.0", - "safe-buffer": "5.1.1" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.6", - "set-immediate-shim": "1.0.1" - } - }, - "recast": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.14.7.tgz", - "integrity": "sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A==", - "dev": true, - "requires": { - "ast-types": "0.11.3", - "esprima": "4.0.0", - "private": "0.1.8", - "source-map": "0.6.1" - }, - "dependencies": { - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "1.7.0" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" - } - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.8" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" - } - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "renderkid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.1.tgz", - "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", - "dev": true, - "requires": { - "css-select": "1.2.0", - "dom-converter": "0.1.4", - "htmlparser2": "3.3.0", - "strip-ansi": "3.0.1", - "utila": "0.3.3" - }, - "dependencies": { - "utila": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz", - "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY=", - "dev": true - } - } - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz", - "integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "2.0.2", - "global-modules": "1.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "1.0.1" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "2.0.1", - "signal-exit": "3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", - "dev": true, - "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "2.1.0" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "1.2.0" - } - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "requires": { - "rx-lite": "4.0.8" - } - }, - "rxjs": { - "version": "5.5.9", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.9.tgz", - "integrity": "sha512-DHG9AHmCmgaFWgjBcXp6NxFDmh3MvIA62GqTWmLnTzr/3oZ6h5hLD8NA+9j+GF0jEwklNIpI4KuuyLG8UWMEvQ==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "0.1.15" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "6.4.0", - "ajv-keywords": "3.1.0" - } - }, - "scoped-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", - "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=", - "dev": true - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selfsigned": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.2.tgz", - "integrity": "sha1-tESVgNmZKbZbEKSDiTAaZZIIh1g=", - "dev": true, - "requires": { - "node-forge": "0.7.1" - } - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.3", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" - } - }, - "serialize-javascript": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.4.0.tgz", - "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=", - "dev": true - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "1.3.5", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "1.0.3", - "http-errors": "1.6.3", - "mime-types": "2.1.18", - "parseurl": "1.3.2" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "dev": true, - "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.2" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shelljs": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.1.tgz", - "integrity": "sha512-YA/iYtZpzFe5HyWVGrb02FjPxc4EMCfpoU/Phg9fQoyMC72u9598OUBrsU8IrtwAKG0tO8IYaqbaLIw+k3IRGA==", - "dev": true, - "requires": { - "glob": "7.1.2", - "interpret": "1.1.0", - "rechoir": "0.6.2" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.7", - "source-map-resolve": "0.5.1", - "use": "3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "1.0.2" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "sockjs": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", - "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", - "dev": true, - "requires": { - "faye-websocket": "0.10.0", - "uuid": "3.2.1" - } - }, - "sockjs-client": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", - "integrity": "sha1-W6vjhrd15M8U51IJEUUmVAFsixI=", - "dev": true, - "requires": { - "debug": "2.6.9", - "eventsource": "0.1.6", - "faye-websocket": "0.11.1", - "inherits": "2.0.3", - "json3": "3.3.2", - "url-parse": "1.3.0" - }, - "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", - "dev": true, - "requires": { - "websocket-driver": "0.7.0" - } - } - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "1.1.0" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", - "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", - "dev": true, - "requires": { - "atob": "2.1.0", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" - } - }, - "source-map-support": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.4.tgz", - "integrity": "sha512-PETSPG6BjY1AHs2t64vS2aqAgu6dMIMXJULWFBGbh2Gr8nVLbCFDo6i/RMMvviIQ2h1Z8+5gQhVKSn2je9nmdg==", - "dev": true, - "requires": { - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "dev": true, - "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", - "dev": true - }, - "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", - "dev": true, - "requires": { - "debug": "2.6.9", - "handle-thing": "1.2.5", - "http-deceiver": "1.2.7", - "safe-buffer": "5.1.1", - "select-hose": "2.0.0", - "spdy-transport": "2.1.0" - } - }, - "spdy-transport": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", - "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", - "dev": true, - "requires": { - "debug": "2.6.9", - "detect-node": "2.0.3", - "hpack.js": "2.1.6", - "obuf": "1.1.2", - "readable-stream": "2.3.6", - "safe-buffer": "5.1.1", - "wbuf": "1.7.3" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "3.0.2" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "0.1.6" - } - } - } - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", - "dev": true - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" - } - }, - "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", - "dev": true, - "requires": { - "end-of-stream": "1.4.1", - "stream-shift": "1.0.0" - } - }, - "stream-http": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.1.tgz", - "integrity": "sha512-cQ0jo17BLca2r0GfRdZKYAGLU6JRoIWxqSOakUMuKOT6MOK7AAlE856L33QuDmAy/eeOrhLee3dZKX0Uadu93A==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "stream-to-observable": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.2.0.tgz", - "integrity": "sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA=", - "dev": true, - "requires": { - "any-observable": "0.2.0" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "strip-bom-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", - "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", - "dev": true, - "requires": { - "first-chunk-stream": "2.0.0", - "strip-bom": "2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "4.0.1" - } - }, - "style-loader": { - "version": "0.20.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.20.3.tgz", - "integrity": "sha512-2I7AVP73MvK33U7B9TKlYZAqdROyMXDYSMvHLX43qy3GCOaJNiV6i0v/sv9idWIaQ42Yn2dNv79Q5mKXbKhAZg==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.5" - } - }, - "supports-color": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", - "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", - "dev": true, - "requires": { - "has-flag": "3.0.0" - } - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" - } - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, - "tapable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", - "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", - "dev": true - }, - "temp": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", - "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2", - "rimraf": "2.2.8" - }, - "dependencies": { - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=", - "dev": true - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "textextensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.2.0.tgz", - "integrity": "sha512-j5EMxnryTvKxwH2Cq+Pb43tsf6sdEgw6Pdwxk83mPaq0ToeFJt6WE4J3s5BqY7vmjlLgkgXvhtXUxo80FyBhCA==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "2.3.6", - "xtend": "4.0.1" - } - }, - "thunky": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", - "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", - "dev": true - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.6.tgz", - "integrity": "sha512-HQ3nbYRAowdVd0ckGFvmJPPCOH/CHleFN/Y0YQCX1DVaB7t+KFvisuyN09fuP8Jtp1CpfSh8O8bMkHbdbPe6Pw==", - "dev": true, - "requires": { - "setimmediate": "1.0.5" - } - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" - } - }, - "toposort": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.6.tgz", - "integrity": "sha1-wxdI5V0hDv/AD9zcfW5o19e7nOw=", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.18" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz", - "integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg==", - "dev": true - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "uglifyjs-webpack-plugin": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.4.tgz", - "integrity": "sha512-z0IbjpW8b3O/OVn+TTZN4pI29RN1zktFBXLIzzfZ+++cUtZ1ERSlLWgpE/5OERuEUs1ijVQnpYAkSlpoVmQmSQ==", - "dev": true, - "requires": { - "cacache": "10.0.4", - "find-cache-dir": "1.0.0", - "schema-utils": "0.4.5", - "serialize-javascript": "1.4.0", - "source-map": "0.6.1", - "uglify-es": "3.3.9", - "webpack-sources": "1.1.0", - "worker-farm": "1.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", - "dev": true - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "0.1.1" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" - } - } - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "0.2.8" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", - "dev": true, - "requires": { - "unique-slug": "2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "dev": true, - "requires": { - "imurmurhash": "0.1.4" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "untildify": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz", - "integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E=", - "dev": true - }, - "upath": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.4.tgz", - "integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==", - "dev": true - }, - "upper-case": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", - "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", - "dev": true - }, - "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", - "dev": true, - "requires": { - "punycode": "2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", - "dev": true - }, - "url-loader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.0.1.tgz", - "integrity": "sha512-rAonpHy7231fmweBKUFe0bYnlGDty77E+fm53NZdij7j/YOpyGzc7ttqG1nAXl3aRs0k41o0PC3TvGXQiw2Zvw==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "mime": "2.3.1", - "schema-utils": "0.4.5" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - } - } - }, - "url-parse": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.3.0.tgz", - "integrity": "sha512-zPvPA3T7P6M+0iNsgX+iAcAz4GshKrowtQBHHc/28tVsBc8jK7VRCNX+2GEcoE6zDB6XqXhcyiUWPVZY6C70Cg==", - "dev": true, - "requires": { - "querystringify": "1.0.0", - "requires-port": "1.0.0" - }, - "dependencies": { - "querystringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", - "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=", - "dev": true - } - } - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "2.0.0" - }, - "dependencies": { - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - } - } - }, - "url-to-options": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true - }, - "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", - "dev": true, - "requires": { - "kind-of": "6.0.2" - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "1.1.2", - "object.getownpropertydescriptors": "2.0.3" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - }, - "v8-compile-cache": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz", - "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", - "dev": true, - "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", - "dev": true - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", - "dev": true, - "requires": { - "clone": "1.0.4", - "clone-stats": "0.0.1", - "replace-ext": "0.0.1" - } - }, - "vinyl-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", - "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0", - "strip-bom-stream": "2.0.0", - "vinyl": "1.2.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "watchpack": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.5.0.tgz", - "integrity": "sha512-RSlipNQB1u48cq0wH/BNfCu1tD/cJ8ydFIkNYhp9o+3d+8unClkIovpW5qpFPgmL9OE48wfAnlZydXByWP82AA==", - "dev": true, - "requires": { - "chokidar": "2.0.3", - "graceful-fs": "4.1.11", - "neo-async": "2.5.1" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "1.0.0" - } - }, - "webpack": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.5.0.tgz", - "integrity": "sha512-6GrZsvQJnG7o7mjbfjp6s5CyMfdopjt1A/X8LcYwceis9ySjqBX6Lusso2wNZ06utHj2ZvfL6L3f7hfgVeJP6g==", - "dev": true, - "requires": { - "acorn": "5.5.3", - "acorn-dynamic-import": "3.0.0", - "ajv": "6.4.0", - "ajv-keywords": "3.1.0", - "chrome-trace-event": "0.1.2", - "enhanced-resolve": "4.0.0", - "eslint-scope": "3.7.1", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "micromatch": "3.1.10", - "mkdirp": "0.5.1", - "neo-async": "2.5.1", - "node-libs-browser": "2.1.0", - "schema-utils": "0.4.5", - "tapable": "1.0.0", - "uglifyjs-webpack-plugin": "1.2.4", - "watchpack": "1.5.0", - "webpack-sources": "1.1.0" - } - }, - "webpack-addons": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/webpack-addons/-/webpack-addons-1.1.5.tgz", - "integrity": "sha512-MGO0nVniCLFAQz1qv22zM02QPjcpAoJdy7ED0i3Zy7SY1IecgXCm460ib7H/Wq7e9oL5VL6S2BxaObxwIcag0g==", - "dev": true, - "requires": { - "jscodeshift": "0.4.1" - }, - "dependencies": { - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "ast-types": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.1.tgz", - "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "jscodeshift": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.4.1.tgz", - "integrity": "sha512-iOX6If+hsw0q99V3n31t4f5VlD1TQZddH08xbT65ZqA7T4Vkx68emrDZMUOLVvCEAJ6NpAk7DECe3fjC/t52AQ==", - "dev": true, - "requires": { - "async": "1.5.2", - "babel-plugin-transform-flow-strip-types": "6.22.0", - "babel-preset-es2015": "6.24.1", - "babel-preset-stage-1": "6.24.1", - "babel-register": "6.26.0", - "babylon": "6.18.0", - "colors": "1.1.2", - "flow-parser": "0.69.0", - "lodash": "4.17.5", - "micromatch": "2.3.11", - "node-dir": "0.1.8", - "nomnom": "1.8.1", - "recast": "0.12.9", - "temp": "0.8.3", - "write-file-atomic": "1.3.4" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.6" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "recast": { - "version": "0.12.9", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.9.tgz", - "integrity": "sha512-y7ANxCWmMW8xLOaiopiRDlyjQ9ajKRENBH+2wjntIbk3A6ZR1+BLQttkmSHMY7Arl+AAZFwJ10grg2T6f1WI8A==", - "dev": true, - "requires": { - "ast-types": "0.10.1", - "core-js": "2.5.5", - "esprima": "4.0.0", - "private": "0.1.8", - "source-map": "0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-cli": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-2.0.14.tgz", - "integrity": "sha512-gRoWaxSi2JWiYsn1QgOTb6ENwIeSvN1YExZ+kJ0STsTZK7bWPElW+BBBv1UnTbvcPC3v7E17mK8hlFX8DOYSGw==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "cross-spawn": "6.0.5", - "diff": "3.5.0", - "enhanced-resolve": "4.0.0", - "envinfo": "4.4.2", - "glob-all": "3.1.0", - "global-modules": "1.0.0", - "got": "8.3.0", - "import-local": "1.0.0", - "inquirer": "5.2.0", - "interpret": "1.1.0", - "jscodeshift": "0.5.0", - "listr": "0.13.0", - "loader-utils": "1.1.0", - "lodash": "4.17.5", - "log-symbols": "2.2.0", - "mkdirp": "0.5.1", - "p-each-series": "1.0.0", - "p-lazy": "1.0.0", - "prettier": "1.11.1", - "supports-color": "5.3.0", - "v8-compile-cache": "1.1.2", - "webpack-addons": "1.1.5", - "yargs": "11.1.0", - "yeoman-environment": "2.0.6", - "yeoman-generator": "2.0.3" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "1.0.4", - "path-key": "2.0.1", - "semver": "5.5.0", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", - "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", - "dev": true, - "requires": { - "cliui": "4.0.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" - } - } - } - }, - "webpack-dev-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", - "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", - "dev": true, - "requires": { - "loud-rejection": "1.6.0", - "memory-fs": "0.4.1", - "mime": "2.3.1", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "url-join": "2.0.5", - "webpack-log": "1.2.0" - }, - "dependencies": { - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - } - } - }, - "webpack-dev-server": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.0.tgz", - "integrity": "sha512-ap7Fth7oh4sthC0nJkvRm2W3SaWryBeR19DWIcAwJlcooN0tB2fEKuZqckYR3uaJ6wXPCK1xMWAQWXhV5xVe8g==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "array-includes": "3.0.3", - "bonjour": "3.5.0", - "chokidar": "2.0.3", - "compression": "1.7.2", - "connect-history-api-fallback": "1.5.0", - "debug": "3.1.0", - "del": "3.0.0", - "express": "4.16.3", - "html-entities": "1.2.1", - "http-proxy-middleware": "0.17.4", - "import-local": "1.0.0", - "internal-ip": "1.2.0", - "ip": "1.1.5", - "killable": "1.0.0", - "loglevel": "1.6.1", - "opn": "5.3.0", - "portfinder": "1.0.13", - "selfsigned": "1.10.2", - "serve-index": "1.9.1", - "sockjs": "0.3.19", - "sockjs-client": "1.1.4", - "spdy": "3.4.7", - "strip-ansi": "3.0.1", - "supports-color": "5.3.0", - "webpack-dev-middleware": "2.0.6", - "webpack-log": "1.2.0", - "yargs": "9.0.1" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "webpack-log": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "log-symbols": "2.2.0", - "loglevelnext": "1.0.4", - "uuid": "3.2.1" - } - }, - "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", - "dev": true, - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", - "dev": true, - "requires": { - "http-parser-js": "0.4.11", - "websocket-extensions": "0.1.3" - } - }, - "websocket-extensions": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", - "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", - "dev": true - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", - "dev": true, - "requires": { - "errno": "0.1.7" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write-file-atomic": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", - "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "imurmurhash": "0.1.4", - "slide": "1.1.6" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", - "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", - "dev": true, - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "2.3.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - }, - "yeoman-environment": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.0.6.tgz", - "integrity": "sha512-jzHBTTy8EPI4ImV8dpUMt+Q5zELkSU5xvGpndHcHudQ4tqN6YgIWaCGmRFl+HDchwRUkcgyjQ+n6/w5zlJBCPg==", - "dev": true, - "requires": { - "chalk": "2.3.2", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "globby": "6.1.0", - "grouped-queue": "0.3.3", - "inquirer": "3.3.0", - "is-scoped": "1.0.0", - "lodash": "4.17.5", - "log-symbols": "2.2.0", - "mem-fs": "1.1.3", - "text-table": "0.2.0", - "untildify": "3.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "requires": { - "ansi-escapes": "3.1.0", - "chalk": "2.3.2", - "cli-cursor": "2.1.0", - "cli-width": "2.2.0", - "external-editor": "2.2.0", - "figures": "2.0.0", - "lodash": "4.17.5", - "mute-stream": "0.0.7", - "run-async": "2.3.0", - "rx-lite": "4.0.8", - "rx-lite-aggregates": "4.0.8", - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "through": "2.3.8" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "yeoman-generator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-2.0.3.tgz", - "integrity": "sha512-mODmrZ26a94djmGZZuIiomSGlN4wULdou29ZwcySupb2e9FdvoCl7Ps2FqHFjEHio3kOl/iBeaNqrnx3C3NwWg==", - "dev": true, - "requires": { - "async": "2.6.0", - "chalk": "2.3.2", - "cli-table": "0.3.1", - "cross-spawn": "5.1.0", - "dargs": "5.1.0", - "dateformat": "3.0.3", - "debug": "3.1.0", - "detect-conflict": "1.0.1", - "error": "7.0.2", - "find-up": "2.1.0", - "github-username": "4.1.0", - "istextorbinary": "2.2.1", - "lodash": "4.17.5", - "make-dir": "1.2.0", - "mem-fs-editor": "3.0.2", - "minimist": "1.2.0", - "pretty-bytes": "4.0.2", - "read-chunk": "2.1.0", - "read-pkg-up": "3.0.0", - "rimraf": "2.6.2", - "run-async": "2.3.0", - "shelljs": "0.8.1", - "text-table": "0.2.0", - "through2": "2.0.3", - "yeoman-environment": "2.0.6" - }, - "dependencies": { - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "dev": true, - "requires": { - "lodash": "4.17.5" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "4.0.0", - "pify": "3.0.0", - "strip-bom": "3.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "1.3.1", - "json-parse-better-errors": "1.0.2" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "3.0.0" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "4.0.0", - "normalize-package-data": "2.4.0", - "path-type": "3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - } - } -} diff --git a/00 Boilerplate/package.json b/00 Boilerplate/package.json deleted file mode 100644 index eea4cf8..0000000 --- a/00 Boilerplate/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "^3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0" - } -} diff --git a/00_Boilerplate/.babelrc b/00_Boilerplate/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/00_Boilerplate/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/00_Boilerplate/package.json b/00_Boilerplate/package.json new file mode 100644 index 0000000..fac46f1 --- /dev/null +++ b/00_Boilerplate/package.json @@ -0,0 +1,31 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + } +} diff --git a/00 Boilerplate/readme.md b/00_Boilerplate/readme.md similarity index 81% rename from 00 Boilerplate/readme.md rename to 00_Boilerplate/readme.md index eb030d9..d3016a4 100644 --- a/00 Boilerplate/readme.md +++ b/00_Boilerplate/readme.md @@ -93,42 +93,43 @@ _[./tsconfig.json](./tsconfig.json)_ } ``` - - Now, we need to transpile ES6 to ES5. Let's install **babel-core** and **babel-preset-env**. + - Now, we need to transpile ES6 to ES5. Let's install **@babel/cli**, **@babel/core**, **@babel/preset-env**, and **@babel/polyfill**. ```bash - npm install babel-core babel-preset-env --save-dev + npm install @babel/cli @babel/core @babel/preset-env @babel/polyfill --save-dev +``` + +- Let's install webpack _babel_ loader. + +```bash +npm install babel-loader --save-dev ``` - Babel needs to be configured for it to work. We create **[./.babelrc](./.babelrc)** in the root folder. Later we will see how to put it in **[./webpack.config.js](./webpack.config.js)**. In this example, we use this .babelrc: _[./.babelrc](./.babelrc)_ ```json - { +{ "presets": [ [ - "env", + "@babel/preset-env", { - "modules": false + "useBuiltIns": "entry" } ] ] } ``` -- Let's install bootstrap: - -```bash - npm install bootstrap --save -``` - - Now, our **[./package.json](./package.json)** file should look something like: + _[./package.json](./package.json)_ ```json { - "name": "sample", + "name": "reactbysample", "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { "start": "webpack-dev-server --mode development --inline --hot --open", @@ -138,22 +139,22 @@ _[./package.json](./package.json)_ "author": "", "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "^3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0" + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" } } ``` @@ -197,22 +198,21 @@ _[./src/index.html](./src/index.html)_ _[./webpack.config.js](./webpack.config.js)_ ```javascript -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.ts', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.ts' + ], output: { path: path.join(basePath, 'dist'), filename: 'bundle.js' @@ -233,8 +233,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -246,7 +247,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -259,9 +260,9 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], + ], }; - ``` +``` - Run webpack: diff --git a/00 Boilerplate/readme_es.md b/00_Boilerplate/readme_es.md similarity index 100% rename from 00 Boilerplate/readme_es.md rename to 00_Boilerplate/readme_es.md diff --git a/00 Boilerplate/src/index.html b/00_Boilerplate/src/index.html similarity index 95% rename from 00 Boilerplate/src/index.html rename to 00_Boilerplate/src/index.html index 94b43cc..da99fc4 100644 --- a/00 Boilerplate/src/index.html +++ b/00_Boilerplate/src/index.html @@ -9,4 +9,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/00 Boilerplate/src/main.ts b/00_Boilerplate/src/main.ts similarity index 100% rename from 00 Boilerplate/src/main.ts rename to 00_Boilerplate/src/main.ts diff --git a/00 Boilerplate/tsconfig.json b/00_Boilerplate/tsconfig.json similarity index 99% rename from 00 Boilerplate/tsconfig.json rename to 00_Boilerplate/tsconfig.json index ba8b3b7..885d474 100644 --- a/00 Boilerplate/tsconfig.json +++ b/00_Boilerplate/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/00 Boilerplate/webpack.config.js b/00_Boilerplate/webpack.config.js similarity index 76% rename from 00 Boilerplate/webpack.config.js rename to 00_Boilerplate/webpack.config.js index 2ca65ba..ec714fd 100644 --- a/00 Boilerplate/webpack.config.js +++ b/00_Boilerplate/webpack.config.js @@ -1,19 +1,18 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.ts', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.ts' + ], output: { path: path.join(basePath, 'dist'), filename: 'bundle.js' @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From b5baf2a7a0849c8b11074575196c85fc6b7404f3 Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 9 Oct 2018 12:01:12 +0200 Subject: [PATCH 060/180] updated 01 Hello React --- 01 HelloReact/.babelrc | 10 ----- 01 HelloReact/package.json | 35 ------------------ 01_HelloReact/.babelrc | 10 +++++ 01_HelloReact/package.json | 37 +++++++++++++++++++ {01 HelloReact => 01_HelloReact}/readme.md | 12 +----- {01 HelloReact => 01_HelloReact}/readme_es.md | 0 .../src/hello.tsx | 0 .../src/index.html | 2 +- 01_HelloReact/src/main.ts | 1 + {01 HelloReact => 01_HelloReact}/src/main.tsx | 0 .../tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 12 files changed, 67 insertions(+), 74 deletions(-) delete mode 100644 01 HelloReact/.babelrc delete mode 100644 01 HelloReact/package.json create mode 100644 01_HelloReact/.babelrc create mode 100644 01_HelloReact/package.json rename {01 HelloReact => 01_HelloReact}/readme.md (94%) rename {01 HelloReact => 01_HelloReact}/readme_es.md (100%) rename {01 HelloReact => 01_HelloReact}/src/hello.tsx (100%) rename {01 HelloReact => 01_HelloReact}/src/index.html (96%) create mode 100644 01_HelloReact/src/main.ts rename {01 HelloReact => 01_HelloReact}/src/main.tsx (100%) rename {01 HelloReact => 01_HelloReact}/tsconfig.json (99%) rename {01 HelloReact => 01_HelloReact}/webpack.config.js (74%) diff --git a/01 HelloReact/.babelrc b/01 HelloReact/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/01 HelloReact/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/01 HelloReact/package.json b/01 HelloReact/package.json deleted file mode 100644 index f4ea57f..0000000 --- a/01 HelloReact/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.12", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.2", - "react-dom": "^16.3.2" - } -} diff --git a/01_HelloReact/.babelrc b/01_HelloReact/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/01_HelloReact/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/01_HelloReact/package.json b/01_HelloReact/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/01_HelloReact/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/01 HelloReact/readme.md b/01_HelloReact/readme.md similarity index 94% rename from 01 HelloReact/readme.md rename to 01_HelloReact/readme.md index 78996ab..77cc18c 100644 --- a/01 HelloReact/readme.md +++ b/01_HelloReact/readme.md @@ -68,17 +68,7 @@ export const HelloComponent = () => {

    Hello component !

    ); } - ``` -Side note: in ES6, this can written as - -```jsx -import * as React from 'react'; - -export const HelloComponent = () => ( -

    Hello component !

    -); - - ``` +``` - Wire up this component by using `react-dom` under [./src/main.tsx](./src/main.tsx) (we have to rename this file extension from `ts` to `tsx` and replace the content). diff --git a/01 HelloReact/readme_es.md b/01_HelloReact/readme_es.md similarity index 100% rename from 01 HelloReact/readme_es.md rename to 01_HelloReact/readme_es.md diff --git a/01 HelloReact/src/hello.tsx b/01_HelloReact/src/hello.tsx similarity index 100% rename from 01 HelloReact/src/hello.tsx rename to 01_HelloReact/src/hello.tsx diff --git a/01 HelloReact/src/index.html b/01_HelloReact/src/index.html similarity index 96% rename from 01 HelloReact/src/index.html rename to 01_HelloReact/src/index.html index bf13d1f..b0b7d25 100644 --- a/01 HelloReact/src/index.html +++ b/01_HelloReact/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/01_HelloReact/src/main.ts b/01_HelloReact/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/01_HelloReact/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/01 HelloReact/src/main.tsx b/01_HelloReact/src/main.tsx similarity index 100% rename from 01 HelloReact/src/main.tsx rename to 01_HelloReact/src/main.tsx diff --git a/01 HelloReact/tsconfig.json b/01_HelloReact/tsconfig.json similarity index 99% rename from 01 HelloReact/tsconfig.json rename to 01_HelloReact/tsconfig.json index ba8b3b7..885d474 100644 --- a/01 HelloReact/tsconfig.json +++ b/01_HelloReact/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/01 HelloReact/webpack.config.js b/01_HelloReact/webpack.config.js similarity index 74% rename from 01 HelloReact/webpack.config.js rename to 01_HelloReact/webpack.config.js index afeaf7d..7c85f49 100644 --- a/01 HelloReact/webpack.config.js +++ b/01_HelloReact/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 5bc32c8d3b026017b4f5c1017cfb1c40c3afe907 Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 9 Oct 2018 14:02:42 +0200 Subject: [PATCH 061/180] updates 02 --- 02 Properties/.babelrc | 10 ----- 02 Properties/package.json | 35 ------------------ 02_Properties/.babelrc | 10 +++++ 02_Properties/package.json | 37 +++++++++++++++++++ {02 Properties => 02_Properties}/readme.md | 0 {02 Properties => 02_Properties}/readme_es.md | 0 .../src/hello.tsx | 0 .../src/index.html | 2 +- 02_Properties/src/main.ts | 1 + {02 Properties => 02_Properties}/src/main.tsx | 2 +- .../tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 12 files changed, 67 insertions(+), 64 deletions(-) delete mode 100644 02 Properties/.babelrc delete mode 100644 02 Properties/package.json create mode 100644 02_Properties/.babelrc create mode 100644 02_Properties/package.json rename {02 Properties => 02_Properties}/readme.md (100%) rename {02 Properties => 02_Properties}/readme_es.md (100%) rename {02 Properties => 02_Properties}/src/hello.tsx (100%) rename {02 Properties => 02_Properties}/src/index.html (96%) create mode 100644 02_Properties/src/main.ts rename {02 Properties => 02_Properties}/src/main.tsx (81%) rename {02 Properties => 02_Properties}/tsconfig.json (99%) rename {02 Properties => 02_Properties}/webpack.config.js (74%) diff --git a/02 Properties/.babelrc b/02 Properties/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/02 Properties/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/02 Properties/package.json b/02 Properties/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/02 Properties/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/02_Properties/.babelrc b/02_Properties/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/02_Properties/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/02_Properties/package.json b/02_Properties/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/02_Properties/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/02 Properties/readme.md b/02_Properties/readme.md similarity index 100% rename from 02 Properties/readme.md rename to 02_Properties/readme.md diff --git a/02 Properties/readme_es.md b/02_Properties/readme_es.md similarity index 100% rename from 02 Properties/readme_es.md rename to 02_Properties/readme_es.md diff --git a/02 Properties/src/hello.tsx b/02_Properties/src/hello.tsx similarity index 100% rename from 02 Properties/src/hello.tsx rename to 02_Properties/src/hello.tsx diff --git a/02 Properties/src/index.html b/02_Properties/src/index.html similarity index 96% rename from 02 Properties/src/index.html rename to 02_Properties/src/index.html index bf13d1f..b0b7d25 100644 --- a/02 Properties/src/index.html +++ b/02_Properties/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/02_Properties/src/main.ts b/02_Properties/src/main.ts new file mode 100644 index 0000000..d3f7db2 --- /dev/null +++ b/02_Properties/src/main.ts @@ -0,0 +1 @@ +document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/02 Properties/src/main.tsx b/02_Properties/src/main.tsx similarity index 81% rename from 02 Properties/src/main.tsx rename to 02_Properties/src/main.tsx index b3dcb08..a9e733e 100644 --- a/02 Properties/src/main.tsx +++ b/02_Properties/src/main.tsx @@ -4,6 +4,6 @@ import * as ReactDOM from 'react-dom'; import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/02 Properties/tsconfig.json b/02_Properties/tsconfig.json similarity index 99% rename from 02 Properties/tsconfig.json rename to 02_Properties/tsconfig.json index ba8b3b7..885d474 100644 --- a/02 Properties/tsconfig.json +++ b/02_Properties/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/02 Properties/webpack.config.js b/02_Properties/webpack.config.js similarity index 74% rename from 02 Properties/webpack.config.js rename to 02_Properties/webpack.config.js index afeaf7d..7c85f49 100644 --- a/02 Properties/webpack.config.js +++ b/02_Properties/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From ef993415a14fb0292aea71e99607dec76b08657c Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 9 Oct 2018 14:09:42 +0200 Subject: [PATCH 062/180] 03_state updated --- 03 State/.babelrc | 10 ------- 03 State/package.json | 35 ---------------------- 03_State/.babelrc | 10 +++++++ 03_State/package.json | 37 ++++++++++++++++++++++++ {03 State => 03_State}/readme.md | 4 ++- {03 State => 03_State}/src/app.tsx | 0 {03 State => 03_State}/src/hello.tsx | 0 {03 State => 03_State}/src/index.html | 2 +- {03 State => 03_State}/src/main.tsx | 6 ++-- {03 State => 03_State}/src/nameEdit.tsx | 5 +--- {03 State => 03_State}/tsconfig.json | 2 +- {03 State => 03_State}/webpack.config.js | 32 ++++++++++---------- 12 files changed, 73 insertions(+), 70 deletions(-) delete mode 100644 03 State/.babelrc delete mode 100644 03 State/package.json create mode 100644 03_State/.babelrc create mode 100644 03_State/package.json rename {03 State => 03_State}/readme.md (98%) rename {03 State => 03_State}/src/app.tsx (100%) rename {03 State => 03_State}/src/hello.tsx (100%) rename {03 State => 03_State}/src/index.html (96%) rename {03 State => 03_State}/src/main.tsx (60%) rename {03 State => 03_State}/src/nameEdit.tsx (75%) rename {03 State => 03_State}/tsconfig.json (99%) rename {03 State => 03_State}/webpack.config.js (74%) diff --git a/03 State/.babelrc b/03 State/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/03 State/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/03 State/package.json b/03 State/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/03 State/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/03_State/.babelrc b/03_State/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/03_State/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/03_State/package.json b/03_State/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/03_State/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/03 State/readme.md b/03_State/readme.md similarity index 98% rename from 03 State/readme.md rename to 03_State/readme.md index ae91be3..66b048f 100644 --- a/03 State/readme.md +++ b/03_State/readme.md @@ -52,6 +52,8 @@ _./src/main.tsx_ import * as ReactDOM from 'react-dom'; + import {App} from './app'; +- import { HelloComponent } from './hello'; + ReactDOM.render( - , + , @@ -117,7 +119,7 @@ export const NameEditComponent = (props : Props) => <> - <> + ``` Side note: What is this Fragment or <> stuff? A way to create component that has multiple root elements (not a single parent). Available from React 16.2. As an alternative you can type: diff --git a/03 State/src/app.tsx b/03_State/src/app.tsx similarity index 100% rename from 03 State/src/app.tsx rename to 03_State/src/app.tsx diff --git a/03 State/src/hello.tsx b/03_State/src/hello.tsx similarity index 100% rename from 03 State/src/hello.tsx rename to 03_State/src/hello.tsx diff --git a/03 State/src/index.html b/03_State/src/index.html similarity index 96% rename from 03 State/src/index.html rename to 03_State/src/index.html index bf13d1f..b0b7d25 100644 --- a/03 State/src/index.html +++ b/03_State/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/03 State/src/main.tsx b/03_State/src/main.tsx similarity index 60% rename from 03 State/src/main.tsx rename to 03_State/src/main.tsx index c091cf4..be3985e 100644 --- a/03 State/src/main.tsx +++ b/03_State/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/03 State/src/nameEdit.tsx b/03_State/src/nameEdit.tsx similarity index 75% rename from 03 State/src/nameEdit.tsx rename to 03_State/src/nameEdit.tsx index 7d87c6a..6c544b2 100644 --- a/03 State/src/nameEdit.tsx +++ b/03_State/src/nameEdit.tsx @@ -5,11 +5,8 @@ interface Props { onChange : (event) => void; } -export const NameEditComponent = (props : Props) => { - return ( +export const NameEditComponent = (props : Props) => <> - ); -} diff --git a/03 State/tsconfig.json b/03_State/tsconfig.json similarity index 99% rename from 03 State/tsconfig.json rename to 03_State/tsconfig.json index ba8b3b7..885d474 100644 --- a/03 State/tsconfig.json +++ b/03_State/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/03 State/webpack.config.js b/03_State/webpack.config.js similarity index 74% rename from 03 State/webpack.config.js rename to 03_State/webpack.config.js index afeaf7d..7c85f49 100644 --- a/03 State/webpack.config.js +++ b/03_State/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From ad795ceaec3da2ff6f946bc45ccbac57d908c4e1 Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 9 Oct 2018 15:49:13 +0200 Subject: [PATCH 063/180] updated 04_Callback --- 04 Callback/.babelrc | 10 ----- 04 Callback/package.json | 35 ------------------ 04 Callback/src/main.ts | 1 - 04_Callback/.babelrc | 10 +++++ 04_Callback/package.json | 37 +++++++++++++++++++ {04 Callback => 04_Callback}/readme.md | 0 {04 Callback => 04_Callback}/readme_es.md | 0 {04 Callback => 04_Callback}/src/app.tsx | 0 {04 Callback => 04_Callback}/src/hello.tsx | 0 {04 Callback => 04_Callback}/src/index.html | 2 +- {04 Callback => 04_Callback}/src/main.tsx | 6 ++- {04 Callback => 04_Callback}/src/nameEdit.tsx | 8 ++-- {04 Callback => 04_Callback}/tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 14 files changed, 73 insertions(+), 70 deletions(-) delete mode 100644 04 Callback/.babelrc delete mode 100644 04 Callback/package.json delete mode 100644 04 Callback/src/main.ts create mode 100644 04_Callback/.babelrc create mode 100644 04_Callback/package.json rename {04 Callback => 04_Callback}/readme.md (100%) rename {04 Callback => 04_Callback}/readme_es.md (100%) rename {04 Callback => 04_Callback}/src/app.tsx (100%) rename {04 Callback => 04_Callback}/src/hello.tsx (100%) rename {04 Callback => 04_Callback}/src/index.html (96%) rename {04 Callback => 04_Callback}/src/main.tsx (60%) rename {04 Callback => 04_Callback}/src/nameEdit.tsx (80%) rename {04 Callback => 04_Callback}/tsconfig.json (99%) rename {04 Callback => 04_Callback}/webpack.config.js (74%) diff --git a/04 Callback/.babelrc b/04 Callback/.babelrc deleted file mode 100644 index 911d8c1..0000000 --- a/04 Callback/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} \ No newline at end of file diff --git a/04 Callback/package.json b/04 Callback/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/04 Callback/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/04 Callback/src/main.ts b/04 Callback/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/04 Callback/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/04_Callback/.babelrc b/04_Callback/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/04_Callback/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/04_Callback/package.json b/04_Callback/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/04_Callback/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/04 Callback/readme.md b/04_Callback/readme.md similarity index 100% rename from 04 Callback/readme.md rename to 04_Callback/readme.md diff --git a/04 Callback/readme_es.md b/04_Callback/readme_es.md similarity index 100% rename from 04 Callback/readme_es.md rename to 04_Callback/readme_es.md diff --git a/04 Callback/src/app.tsx b/04_Callback/src/app.tsx similarity index 100% rename from 04 Callback/src/app.tsx rename to 04_Callback/src/app.tsx diff --git a/04 Callback/src/hello.tsx b/04_Callback/src/hello.tsx similarity index 100% rename from 04 Callback/src/hello.tsx rename to 04_Callback/src/hello.tsx diff --git a/04 Callback/src/index.html b/04_Callback/src/index.html similarity index 96% rename from 04 Callback/src/index.html rename to 04_Callback/src/index.html index bf13d1f..b0b7d25 100644 --- a/04 Callback/src/index.html +++ b/04_Callback/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/04 Callback/src/main.tsx b/04_Callback/src/main.tsx similarity index 60% rename from 04 Callback/src/main.tsx rename to 04_Callback/src/main.tsx index c091cf4..be3985e 100644 --- a/04 Callback/src/main.tsx +++ b/04_Callback/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/04 Callback/src/nameEdit.tsx b/04_Callback/src/nameEdit.tsx similarity index 80% rename from 04 Callback/src/nameEdit.tsx rename to 04_Callback/src/nameEdit.tsx index b388350..4efeef4 100644 --- a/04 Callback/src/nameEdit.tsx +++ b/04_Callback/src/nameEdit.tsx @@ -20,7 +20,7 @@ export class NameEditComponent extends React.Component { } onChange = (event) => { - this.setState({ editingName: event.target.value } as State); + this.setState({ editingName: event.target.value }); } onNameSubmit = (event: any): any => { @@ -29,11 +29,11 @@ export class NameEditComponent extends React.Component { public render() { return ( -
    + <> - -
    + + ); } } diff --git a/04 Callback/tsconfig.json b/04_Callback/tsconfig.json similarity index 99% rename from 04 Callback/tsconfig.json rename to 04_Callback/tsconfig.json index ba8b3b7..885d474 100644 --- a/04 Callback/tsconfig.json +++ b/04_Callback/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/04 Callback/webpack.config.js b/04_Callback/webpack.config.js similarity index 74% rename from 04 Callback/webpack.config.js rename to 04_Callback/webpack.config.js index afeaf7d..7c85f49 100644 --- a/04 Callback/webpack.config.js +++ b/04_Callback/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 0e9f6a21cd65cfed8b4f8d4bd4fe0622175d51ff Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 9 Oct 2018 15:56:59 +0200 Subject: [PATCH 064/180] 05_refactor updated --- 05 Refactor/.babelrc | 10 ----- 05 Refactor/package.json | 35 ------------------ 05 Refactor/src/main.ts | 1 - 05_Refactor/.babelrc | 10 +++++ 05_Refactor/package.json | 37 +++++++++++++++++++ {05 Refactor => 05_Refactor}/readme.md | 4 ++ {05 Refactor => 05_Refactor}/readme_es.md | 0 {05 Refactor => 05_Refactor}/src/app.tsx | 5 ++- {05 Refactor => 05_Refactor}/src/hello.tsx | 0 {05 Refactor => 05_Refactor}/src/index.html | 2 +- {05 Refactor => 05_Refactor}/src/main.tsx | 6 ++- {05 Refactor => 05_Refactor}/src/nameEdit.tsx | 6 ++- {05 Refactor => 05_Refactor}/tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 14 files changed, 80 insertions(+), 70 deletions(-) delete mode 100644 05 Refactor/.babelrc delete mode 100644 05 Refactor/package.json delete mode 100644 05 Refactor/src/main.ts create mode 100644 05_Refactor/.babelrc create mode 100644 05_Refactor/package.json rename {05 Refactor => 05_Refactor}/readme.md (99%) rename {05 Refactor => 05_Refactor}/readme_es.md (100%) rename {05 Refactor => 05_Refactor}/src/app.tsx (86%) rename {05 Refactor => 05_Refactor}/src/hello.tsx (100%) rename {05 Refactor => 05_Refactor}/src/index.html (96%) rename {05 Refactor => 05_Refactor}/src/main.tsx (60%) rename {05 Refactor => 05_Refactor}/src/nameEdit.tsx (96%) rename {05 Refactor => 05_Refactor}/tsconfig.json (99%) rename {05 Refactor => 05_Refactor}/webpack.config.js (74%) diff --git a/05 Refactor/.babelrc b/05 Refactor/.babelrc deleted file mode 100644 index 911d8c1..0000000 --- a/05 Refactor/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} \ No newline at end of file diff --git a/05 Refactor/package.json b/05 Refactor/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/05 Refactor/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/05 Refactor/src/main.ts b/05 Refactor/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/05 Refactor/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/05_Refactor/.babelrc b/05_Refactor/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/05_Refactor/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/05_Refactor/package.json b/05_Refactor/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/05_Refactor/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/05 Refactor/readme.md b/05_Refactor/readme.md similarity index 99% rename from 05 Refactor/readme.md rename to 05_Refactor/readme.md index d56270f..90d1c6f 100644 --- a/05 Refactor/readme.md +++ b/05_Refactor/readme.md @@ -12,6 +12,8 @@ With this solution, the code looks like this (using the new static method getDer Props and interface: +_./src/nameEdit.tsx_ + ```diff interface Props { initialUserName: string; @@ -126,6 +128,8 @@ interface Props { - Update _app.tsx_ to hold the new editing property in the state, pass it to the children controls and perform the proper update on the callback event from the child control. +_./src/app.tsx_ + ```diff import * as React from 'react'; import {HelloComponent} from './hello'; diff --git a/05 Refactor/readme_es.md b/05_Refactor/readme_es.md similarity index 100% rename from 05 Refactor/readme_es.md rename to 05_Refactor/readme_es.md diff --git a/05 Refactor/src/app.tsx b/05_Refactor/src/app.tsx similarity index 86% rename from 05 Refactor/src/app.tsx rename to 05_Refactor/src/app.tsx index 28211c2..7bad2c9 100644 --- a/05 Refactor/src/app.tsx +++ b/05_Refactor/src/app.tsx @@ -19,11 +19,11 @@ export class App extends React.Component { } setUsernameState = () => { - this.setState({ userName: this.state.editingUserName } as State); + this.setState({ userName: this.state.editingUserName }); } updateEditingName = (editingName: string): void => { - this.setState({ editingUserName: editingName } as State); + this.setState({ editingUserName: editingName }); } @@ -35,6 +35,7 @@ export class App extends React.Component { editingUserName={this.state.editingUserName} onEditingNameUpdated={this.updateEditingName} onNameUpdateRequest={this.setUsernameState} /> + ); } diff --git a/05 Refactor/src/hello.tsx b/05_Refactor/src/hello.tsx similarity index 100% rename from 05 Refactor/src/hello.tsx rename to 05_Refactor/src/hello.tsx diff --git a/05 Refactor/src/index.html b/05_Refactor/src/index.html similarity index 96% rename from 05 Refactor/src/index.html rename to 05_Refactor/src/index.html index bf13d1f..b0b7d25 100644 --- a/05 Refactor/src/index.html +++ b/05_Refactor/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/05 Refactor/src/main.tsx b/05_Refactor/src/main.tsx similarity index 60% rename from 05 Refactor/src/main.tsx rename to 05_Refactor/src/main.tsx index c091cf4..be3985e 100644 --- a/05 Refactor/src/main.tsx +++ b/05_Refactor/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/05 Refactor/src/nameEdit.tsx b/05_Refactor/src/nameEdit.tsx similarity index 96% rename from 05 Refactor/src/nameEdit.tsx rename to 05_Refactor/src/nameEdit.tsx index 5689dfe..306bf8d 100644 --- a/05 Refactor/src/nameEdit.tsx +++ b/05_Refactor/src/nameEdit.tsx @@ -6,6 +6,7 @@ interface Props { onNameUpdateRequest: () => void; } + export class NameEditComponent extends React.Component { constructor(props: Props) { @@ -16,14 +17,15 @@ export class NameEditComponent extends React.Component { this.props.onEditingNameUpdated((e.target as HTMLInputElement).value); } + public render() { return ( -
    + <> -
    + ); } } diff --git a/05 Refactor/tsconfig.json b/05_Refactor/tsconfig.json similarity index 99% rename from 05 Refactor/tsconfig.json rename to 05_Refactor/tsconfig.json index ba8b3b7..885d474 100644 --- a/05 Refactor/tsconfig.json +++ b/05_Refactor/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/05 Refactor/webpack.config.js b/05_Refactor/webpack.config.js similarity index 74% rename from 05 Refactor/webpack.config.js rename to 05_Refactor/webpack.config.js index afeaf7d..7c85f49 100644 --- a/05 Refactor/webpack.config.js +++ b/05_Refactor/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 06bee16b7db7125cc546bc4643125c5c7e155f55 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 09:09:39 +0200 Subject: [PATCH 065/180] 06 move back to stateless finished --- 06 MoveBackToStateless/.babelrc | 10 ----- 06 MoveBackToStateless/package.json | 35 ------------------ 06 MoveBackToStateless/src/main.ts | 1 - 06_MoveBackToStateless/.babelrc | 10 +++++ 06_MoveBackToStateless/package.json | 37 +++++++++++++++++++ .../readme.md | 0 .../readme_es.md | 0 .../src/app.tsx | 5 ++- .../src/hello.tsx | 0 .../src/index.html | 2 +- .../src/main.tsx | 6 ++- .../src/nameEdit.tsx | 1 - .../tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 14 files changed, 72 insertions(+), 69 deletions(-) delete mode 100644 06 MoveBackToStateless/.babelrc delete mode 100644 06 MoveBackToStateless/package.json delete mode 100644 06 MoveBackToStateless/src/main.ts create mode 100644 06_MoveBackToStateless/.babelrc create mode 100644 06_MoveBackToStateless/package.json rename {06 MoveBackToStateless => 06_MoveBackToStateless}/readme.md (100%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/readme_es.md (100%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/src/app.tsx (86%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/src/hello.tsx (100%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/src/index.html (96%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/src/main.tsx (60%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/src/nameEdit.tsx (99%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/tsconfig.json (99%) rename {06 MoveBackToStateless => 06_MoveBackToStateless}/webpack.config.js (74%) diff --git a/06 MoveBackToStateless/.babelrc b/06 MoveBackToStateless/.babelrc deleted file mode 100644 index 911d8c1..0000000 --- a/06 MoveBackToStateless/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} \ No newline at end of file diff --git a/06 MoveBackToStateless/package.json b/06 MoveBackToStateless/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/06 MoveBackToStateless/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/06 MoveBackToStateless/src/main.ts b/06 MoveBackToStateless/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/06 MoveBackToStateless/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/06_MoveBackToStateless/.babelrc b/06_MoveBackToStateless/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/06_MoveBackToStateless/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/06_MoveBackToStateless/package.json b/06_MoveBackToStateless/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/06_MoveBackToStateless/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/06 MoveBackToStateless/readme.md b/06_MoveBackToStateless/readme.md similarity index 100% rename from 06 MoveBackToStateless/readme.md rename to 06_MoveBackToStateless/readme.md diff --git a/06 MoveBackToStateless/readme_es.md b/06_MoveBackToStateless/readme_es.md similarity index 100% rename from 06 MoveBackToStateless/readme_es.md rename to 06_MoveBackToStateless/readme_es.md diff --git a/06 MoveBackToStateless/src/app.tsx b/06_MoveBackToStateless/src/app.tsx similarity index 86% rename from 06 MoveBackToStateless/src/app.tsx rename to 06_MoveBackToStateless/src/app.tsx index 28211c2..7bad2c9 100644 --- a/06 MoveBackToStateless/src/app.tsx +++ b/06_MoveBackToStateless/src/app.tsx @@ -19,11 +19,11 @@ export class App extends React.Component { } setUsernameState = () => { - this.setState({ userName: this.state.editingUserName } as State); + this.setState({ userName: this.state.editingUserName }); } updateEditingName = (editingName: string): void => { - this.setState({ editingUserName: editingName } as State); + this.setState({ editingUserName: editingName }); } @@ -35,6 +35,7 @@ export class App extends React.Component { editingUserName={this.state.editingUserName} onEditingNameUpdated={this.updateEditingName} onNameUpdateRequest={this.setUsernameState} /> + ); } diff --git a/06 MoveBackToStateless/src/hello.tsx b/06_MoveBackToStateless/src/hello.tsx similarity index 100% rename from 06 MoveBackToStateless/src/hello.tsx rename to 06_MoveBackToStateless/src/hello.tsx diff --git a/06 MoveBackToStateless/src/index.html b/06_MoveBackToStateless/src/index.html similarity index 96% rename from 06 MoveBackToStateless/src/index.html rename to 06_MoveBackToStateless/src/index.html index bf13d1f..b0b7d25 100644 --- a/06 MoveBackToStateless/src/index.html +++ b/06_MoveBackToStateless/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/06 MoveBackToStateless/src/main.tsx b/06_MoveBackToStateless/src/main.tsx similarity index 60% rename from 06 MoveBackToStateless/src/main.tsx rename to 06_MoveBackToStateless/src/main.tsx index c091cf4..be3985e 100644 --- a/06 MoveBackToStateless/src/main.tsx +++ b/06_MoveBackToStateless/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/06 MoveBackToStateless/src/nameEdit.tsx b/06_MoveBackToStateless/src/nameEdit.tsx similarity index 99% rename from 06 MoveBackToStateless/src/nameEdit.tsx rename to 06_MoveBackToStateless/src/nameEdit.tsx index e481cf0..1ee5fd4 100644 --- a/06 MoveBackToStateless/src/nameEdit.tsx +++ b/06_MoveBackToStateless/src/nameEdit.tsx @@ -8,7 +8,6 @@ interface Props { onNameUpdateRequest : () => void; } - export const NameEditComponent = (props : Props) =>
    diff --git a/06 MoveBackToStateless/tsconfig.json b/06_MoveBackToStateless/tsconfig.json similarity index 99% rename from 06 MoveBackToStateless/tsconfig.json rename to 06_MoveBackToStateless/tsconfig.json index ba8b3b7..885d474 100644 --- a/06 MoveBackToStateless/tsconfig.json +++ b/06_MoveBackToStateless/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/06 MoveBackToStateless/webpack.config.js b/06_MoveBackToStateless/webpack.config.js similarity index 74% rename from 06 MoveBackToStateless/webpack.config.js rename to 06_MoveBackToStateless/webpack.config.js index afeaf7d..7c85f49 100644 --- a/06 MoveBackToStateless/webpack.config.js +++ b/06_MoveBackToStateless/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 861fd539c15849a8ee41af9f3b0c7de81bd604da Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 09:21:56 +0200 Subject: [PATCH 066/180] 07 enable ported --- 07 Enable/.babelrc | 10 ------ 07 Enable/package.json | 35 -------------------- 07_Enable/.babelrc | 10 ++++++ 07_Enable/package.json | 37 ++++++++++++++++++++++ {07 Enable => 07_Enable}/readme.md | 0 {07 Enable => 07_Enable}/readme_es.md | 0 {07 Enable => 07_Enable}/src/app.tsx | 5 ++- {07 Enable => 07_Enable}/src/hello.tsx | 0 {07 Enable => 07_Enable}/src/index.html | 2 +- {07 Enable => 07_Enable}/src/main.tsx | 6 ++-- {07 Enable => 07_Enable}/src/nameEdit.tsx | 10 +++--- {07 Enable => 07_Enable}/tsconfig.json | 2 +- {07 Enable => 07_Enable}/webpack.config.js | 32 +++++++++---------- 13 files changed, 75 insertions(+), 74 deletions(-) delete mode 100644 07 Enable/.babelrc delete mode 100644 07 Enable/package.json create mode 100644 07_Enable/.babelrc create mode 100644 07_Enable/package.json rename {07 Enable => 07_Enable}/readme.md (100%) rename {07 Enable => 07_Enable}/readme_es.md (100%) rename {07 Enable => 07_Enable}/src/app.tsx (79%) rename {07 Enable => 07_Enable}/src/hello.tsx (100%) rename {07 Enable => 07_Enable}/src/index.html (96%) rename {07 Enable => 07_Enable}/src/main.tsx (60%) rename {07 Enable => 07_Enable}/src/nameEdit.tsx (69%) rename {07 Enable => 07_Enable}/tsconfig.json (99%) rename {07 Enable => 07_Enable}/webpack.config.js (74%) diff --git a/07 Enable/.babelrc b/07 Enable/.babelrc deleted file mode 100644 index 911d8c1..0000000 --- a/07 Enable/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} \ No newline at end of file diff --git a/07 Enable/package.json b/07 Enable/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/07 Enable/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/07_Enable/.babelrc b/07_Enable/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/07_Enable/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/07_Enable/package.json b/07_Enable/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/07_Enable/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/07 Enable/readme.md b/07_Enable/readme.md similarity index 100% rename from 07 Enable/readme.md rename to 07_Enable/readme.md diff --git a/07 Enable/readme_es.md b/07_Enable/readme_es.md similarity index 100% rename from 07 Enable/readme_es.md rename to 07_Enable/readme_es.md diff --git a/07 Enable/src/app.tsx b/07_Enable/src/app.tsx similarity index 79% rename from 07 Enable/src/app.tsx rename to 07_Enable/src/app.tsx index 6d10633..942af8c 100644 --- a/07 Enable/src/app.tsx +++ b/07_Enable/src/app.tsx @@ -19,11 +19,11 @@ export class App extends React.Component { } setUsernameState = () => { - this.setState({ userName: this.state.editingUserName } as State); + this.setState({ userName: this.state.editingUserName }); } updateEditingName = (editingName: string): void => { - this.setState({ editingUserName: editingName } as State); + this.setState({ editingUserName: editingName }); } @@ -32,7 +32,6 @@ export class App extends React.Component { <> Sample app
    - \ No newline at end of file + diff --git a/07 Enable/src/main.tsx b/07_Enable/src/main.tsx similarity index 60% rename from 07 Enable/src/main.tsx rename to 07_Enable/src/main.tsx index c091cf4..be3985e 100644 --- a/07 Enable/src/main.tsx +++ b/07_Enable/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/07 Enable/src/nameEdit.tsx b/07_Enable/src/nameEdit.tsx similarity index 69% rename from 07 Enable/src/nameEdit.tsx rename to 07_Enable/src/nameEdit.tsx index a6328c7..f7f59fb 100644 --- a/07 Enable/src/nameEdit.tsx +++ b/07_Enable/src/nameEdit.tsx @@ -3,7 +3,6 @@ import {Fragment} from 'react'; interface Props { - disable: boolean; userName : string; editingUserName : string; onEditingNameUpdated : (newEditingName : string) => void; @@ -16,9 +15,8 @@ export const NameEditComponent = (props : Props) => props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - + diff --git a/07 Enable/tsconfig.json b/07_Enable/tsconfig.json similarity index 99% rename from 07 Enable/tsconfig.json rename to 07_Enable/tsconfig.json index ba8b3b7..885d474 100644 --- a/07 Enable/tsconfig.json +++ b/07_Enable/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/07 Enable/webpack.config.js b/07_Enable/webpack.config.js similarity index 74% rename from 07 Enable/webpack.config.js rename to 07_Enable/webpack.config.js index afeaf7d..7c85f49 100644 --- a/07 Enable/webpack.config.js +++ b/07_Enable/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 44c23aba04ec7aeccd9f50c52fcacf60c5b745d2 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 09:41:39 +0200 Subject: [PATCH 067/180] 08 Color picker updated --- 08 Colorpicker/.babelrc | 10 ----- 08 Colorpicker/package.json | 35 ------------------ 08 Colorpicker/src/app.tsx | 30 --------------- 08_Colorpicker/.babelrc | 10 +++++ 08_Colorpicker/package.json | 37 +++++++++++++++++++ {08 Colorpicker => 08_Colorpicker}/readme.md | 8 ++-- .../readme_es.md | 0 08_Colorpicker/src/app.tsx | 30 +++++++++++++++ .../src/color.ts | 0 .../src/colordisplayer.tsx | 4 +- .../src/colorpicker.tsx | 6 +-- .../src/index.html | 2 +- .../src/main.tsx | 3 +- 08_Colorpicker/src/nameEdit.tsx | 22 +++++++++++ .../tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 16 files changed, 128 insertions(+), 103 deletions(-) delete mode 100644 08 Colorpicker/.babelrc delete mode 100644 08 Colorpicker/package.json delete mode 100644 08 Colorpicker/src/app.tsx create mode 100644 08_Colorpicker/.babelrc create mode 100644 08_Colorpicker/package.json rename {08 Colorpicker => 08_Colorpicker}/readme.md (95%) rename {08 Colorpicker => 08_Colorpicker}/readme_es.md (100%) create mode 100644 08_Colorpicker/src/app.tsx rename {08 Colorpicker => 08_Colorpicker}/src/color.ts (100%) rename {08 Colorpicker => 08_Colorpicker}/src/colordisplayer.tsx (72%) rename {08 Colorpicker => 08_Colorpicker}/src/colorpicker.tsx (87%) rename {08 Colorpicker => 08_Colorpicker}/src/index.html (96%) rename {08 Colorpicker => 08_Colorpicker}/src/main.tsx (92%) create mode 100644 08_Colorpicker/src/nameEdit.tsx rename {08 Colorpicker => 08_Colorpicker}/tsconfig.json (99%) rename {08 Colorpicker => 08_Colorpicker}/webpack.config.js (74%) diff --git a/08 Colorpicker/.babelrc b/08 Colorpicker/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/08 Colorpicker/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/08 Colorpicker/package.json b/08 Colorpicker/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/08 Colorpicker/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/08 Colorpicker/src/app.tsx b/08 Colorpicker/src/app.tsx deleted file mode 100644 index 1c959a1..0000000 --- a/08 Colorpicker/src/app.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from 'react'; -import { Color } from './color'; -import { ColorPicker } from './colorpicker'; -import { ColorDisplayer } from './colordisplayer'; - -interface State { - color: Color; -} - -export class App extends React.Component<{}, State> { - constructor(props) { - super(props); - - this.state = { color: { red: 90, green: 50, blue: 70 } }; - } - - setColorState = (newColor: Color) => { - this.setState({ color: newColor }); - } - - public render() { - return ( -
    - - Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] - -
    - ); - } -} diff --git a/08_Colorpicker/.babelrc b/08_Colorpicker/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/08_Colorpicker/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/08_Colorpicker/package.json b/08_Colorpicker/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/08_Colorpicker/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/08 Colorpicker/readme.md b/08_Colorpicker/readme.md similarity index 95% rename from 08 Colorpicker/readme.md rename to 08_Colorpicker/readme.md index ff7603e..3c49148 100644 --- a/08 Colorpicker/readme.md +++ b/08_Colorpicker/readme.md @@ -215,7 +215,7 @@ _./src/colopicker.tsx_ + onChange={(event : any) => props.onColorUpdated( + { + red: props.color.red, -+ green: event.target.value, ++ green: +event.target.value, + blue: props.color.blue + } + )} @@ -230,7 +230,7 @@ _./src/colopicker.tsx_ + { + red: props.color.red, + green: props.color.green, -+ blue: event.target.value ++ blue: +event.target.value + } + )} + /> @@ -254,7 +254,7 @@ _./src/colordisplayer.tsx_ } export const ColorDisplayer = (props : Props) => { - const divStyle = React.CSSProperties { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. + const divStyle : React.CSSProperties = { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. width: '11rem', height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` @@ -294,7 +294,7 @@ export class App extends React.Component<{}, State> { return (
    + -+ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}]
    ); diff --git a/08 Colorpicker/readme_es.md b/08_Colorpicker/readme_es.md similarity index 100% rename from 08 Colorpicker/readme_es.md rename to 08_Colorpicker/readme_es.md diff --git a/08_Colorpicker/src/app.tsx b/08_Colorpicker/src/app.tsx new file mode 100644 index 0000000..ab7fde2 --- /dev/null +++ b/08_Colorpicker/src/app.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import {Color} from './color'; +import {ColorPicker} from './colorpicker'; +import {ColorDisplayer} from './colordisplayer'; + +interface State { + color : Color; +} + +export class App extends React.Component<{}, State> { + constructor(props) { + super(props); + + this.state = {color: {red: 90, green: 50, blue: 70}}; + } + + setColorState = (newColor : Color) => { + this.setState({color: newColor}); + } + + public render() { + return ( +
    + + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + +
    + ); + } +} \ No newline at end of file diff --git a/08 Colorpicker/src/color.ts b/08_Colorpicker/src/color.ts similarity index 100% rename from 08 Colorpicker/src/color.ts rename to 08_Colorpicker/src/color.ts diff --git a/08 Colorpicker/src/colordisplayer.tsx b/08_Colorpicker/src/colordisplayer.tsx similarity index 72% rename from 08 Colorpicker/src/colordisplayer.tsx rename to 08_Colorpicker/src/colordisplayer.tsx index 23fb151..1a4cb1c 100644 --- a/08 Colorpicker/src/colordisplayer.tsx +++ b/08_Colorpicker/src/colordisplayer.tsx @@ -6,12 +6,14 @@ interface Props { } export const ColorDisplayer = (props : Props) => { - const divStyle = { + + const divStyle : React.CSSProperties = { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. width: '11rem', height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` }; + return (
    diff --git a/08 Colorpicker/src/colorpicker.tsx b/08_Colorpicker/src/colorpicker.tsx similarity index 87% rename from 08 Colorpicker/src/colorpicker.tsx rename to 08_Colorpicker/src/colorpicker.tsx index 3e95866..aa2e0e1 100644 --- a/08 Colorpicker/src/colorpicker.tsx +++ b/08_Colorpicker/src/colorpicker.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; - import { Color } from './color' interface Props { @@ -7,7 +6,6 @@ interface Props { onColorUpdated: (color: Color) => void; } - export const ColorPicker = (props: Props) => { return (
    @@ -15,8 +13,8 @@ export const ColorPicker = (props: Props) => { min="0" max="255" value={props.color.red} - onChange={(event: any) => props.onColorUpdated( - { red: event.target.value, green: props.color.green, blue: props.color.blue } + onChange={(event) => props.onColorUpdated( + { red: +event.target.value, green: props.color.green, blue: props.color.blue } )} /> {props.color.red} diff --git a/08 Colorpicker/src/index.html b/08_Colorpicker/src/index.html similarity index 96% rename from 08 Colorpicker/src/index.html rename to 08_Colorpicker/src/index.html index bf13d1f..b0b7d25 100644 --- a/08 Colorpicker/src/index.html +++ b/08_Colorpicker/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/08 Colorpicker/src/main.tsx b/08_Colorpicker/src/main.tsx similarity index 92% rename from 08 Colorpicker/src/main.tsx rename to 08_Colorpicker/src/main.tsx index b220dd5..d75c95c 100644 --- a/08 Colorpicker/src/main.tsx +++ b/08_Colorpicker/src/main.tsx @@ -2,7 +2,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import {App} from './app'; + ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/08_Colorpicker/src/nameEdit.tsx b/08_Colorpicker/src/nameEdit.tsx new file mode 100644 index 0000000..f7f59fb --- /dev/null +++ b/08_Colorpicker/src/nameEdit.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import {Fragment} from 'react'; + + +interface Props { + userName : string; + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => void; + onNameUpdateRequest : () => void; +} + +export const NameEditComponent = (props : Props) => +
    + + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + + +
    diff --git a/08 Colorpicker/tsconfig.json b/08_Colorpicker/tsconfig.json similarity index 99% rename from 08 Colorpicker/tsconfig.json rename to 08_Colorpicker/tsconfig.json index ba8b3b7..885d474 100644 --- a/08 Colorpicker/tsconfig.json +++ b/08_Colorpicker/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/08 Colorpicker/webpack.config.js b/08_Colorpicker/webpack.config.js similarity index 74% rename from 08 Colorpicker/webpack.config.js rename to 08_Colorpicker/webpack.config.js index afeaf7d..7c85f49 100644 --- a/08 Colorpicker/webpack.config.js +++ b/08_Colorpicker/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 8e190d687db5f4500f3cd7f2c560d668275e2a36 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 09:58:31 +0200 Subject: [PATCH 068/180] 09_colorprefactor updated --- 09 ColorpRefactor/.babelrc | 10 ----- 09 ColorpRefactor/package.json | 35 ------------------ 09 ColorpRefactor/src/app.tsx | 30 --------------- 09_ColorpRefactor/.babelrc | 10 +++++ 09_ColorpRefactor/package.json | 37 +++++++++++++++++++ .../readme.md | 2 + .../readme_es.md | 0 09_ColorpRefactor/src/app.tsx | 30 +++++++++++++++ .../src/color.ts | 0 .../src/colordisplayer.tsx | 4 +- .../src/colorpicker.tsx | 23 +++++++----- .../src/colorslider.tsx | 0 .../src/index.html | 2 +- .../src/main.tsx | 3 +- 09_ColorpRefactor/src/nameEdit.tsx | 22 +++++++++++ .../tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 17 files changed, 137 insertions(+), 105 deletions(-) delete mode 100644 09 ColorpRefactor/.babelrc delete mode 100644 09 ColorpRefactor/package.json delete mode 100644 09 ColorpRefactor/src/app.tsx create mode 100644 09_ColorpRefactor/.babelrc create mode 100644 09_ColorpRefactor/package.json rename {09 ColorpRefactor => 09_ColorpRefactor}/readme.md (99%) rename {09 ColorpRefactor => 09_ColorpRefactor}/readme_es.md (100%) create mode 100644 09_ColorpRefactor/src/app.tsx rename {09 ColorpRefactor => 09_ColorpRefactor}/src/color.ts (100%) rename {09 ColorpRefactor => 09_ColorpRefactor}/src/colordisplayer.tsx (72%) rename {09 ColorpRefactor => 09_ColorpRefactor}/src/colorpicker.tsx (56%) rename {09 ColorpRefactor => 09_ColorpRefactor}/src/colorslider.tsx (100%) rename {09 ColorpRefactor => 09_ColorpRefactor}/src/index.html (96%) rename {09 ColorpRefactor => 09_ColorpRefactor}/src/main.tsx (92%) create mode 100644 09_ColorpRefactor/src/nameEdit.tsx rename {09 ColorpRefactor => 09_ColorpRefactor}/tsconfig.json (99%) rename {09 ColorpRefactor => 09_ColorpRefactor}/webpack.config.js (74%) diff --git a/09 ColorpRefactor/.babelrc b/09 ColorpRefactor/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/09 ColorpRefactor/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/09 ColorpRefactor/package.json b/09 ColorpRefactor/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/09 ColorpRefactor/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/09 ColorpRefactor/src/app.tsx b/09 ColorpRefactor/src/app.tsx deleted file mode 100644 index 1c959a1..0000000 --- a/09 ColorpRefactor/src/app.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from 'react'; -import { Color } from './color'; -import { ColorPicker } from './colorpicker'; -import { ColorDisplayer } from './colordisplayer'; - -interface State { - color: Color; -} - -export class App extends React.Component<{}, State> { - constructor(props) { - super(props); - - this.state = { color: { red: 90, green: 50, blue: 70 } }; - } - - setColorState = (newColor: Color) => { - this.setState({ color: newColor }); - } - - public render() { - return ( -
    - - Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] - -
    - ); - } -} diff --git a/09_ColorpRefactor/.babelrc b/09_ColorpRefactor/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/09_ColorpRefactor/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/09_ColorpRefactor/package.json b/09_ColorpRefactor/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/09_ColorpRefactor/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/09 ColorpRefactor/readme.md b/09_ColorpRefactor/readme.md similarity index 99% rename from 09 ColorpRefactor/readme.md rename to 09_ColorpRefactor/readme.md index 0ca071a..e0cbdec 100644 --- a/09 ColorpRefactor/readme.md +++ b/09_ColorpRefactor/readme.md @@ -149,6 +149,8 @@ export const ColorPicker = (props : Props) => { - We have still room for improvement. What about using a single handler for all colors? If we currify the colorupdated handler, then we can! +_./src/colorpicker.tsx_ + ```diff import * as React from 'react'; import { Color } from './color' diff --git a/09 ColorpRefactor/readme_es.md b/09_ColorpRefactor/readme_es.md similarity index 100% rename from 09 ColorpRefactor/readme_es.md rename to 09_ColorpRefactor/readme_es.md diff --git a/09_ColorpRefactor/src/app.tsx b/09_ColorpRefactor/src/app.tsx new file mode 100644 index 0000000..ab7fde2 --- /dev/null +++ b/09_ColorpRefactor/src/app.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import {Color} from './color'; +import {ColorPicker} from './colorpicker'; +import {ColorDisplayer} from './colordisplayer'; + +interface State { + color : Color; +} + +export class App extends React.Component<{}, State> { + constructor(props) { + super(props); + + this.state = {color: {red: 90, green: 50, blue: 70}}; + } + + setColorState = (newColor : Color) => { + this.setState({color: newColor}); + } + + public render() { + return ( +
    + + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + +
    + ); + } +} \ No newline at end of file diff --git a/09 ColorpRefactor/src/color.ts b/09_ColorpRefactor/src/color.ts similarity index 100% rename from 09 ColorpRefactor/src/color.ts rename to 09_ColorpRefactor/src/color.ts diff --git a/09 ColorpRefactor/src/colordisplayer.tsx b/09_ColorpRefactor/src/colordisplayer.tsx similarity index 72% rename from 09 ColorpRefactor/src/colordisplayer.tsx rename to 09_ColorpRefactor/src/colordisplayer.tsx index 23fb151..1a4cb1c 100644 --- a/09 ColorpRefactor/src/colordisplayer.tsx +++ b/09_ColorpRefactor/src/colordisplayer.tsx @@ -6,12 +6,14 @@ interface Props { } export const ColorDisplayer = (props : Props) => { - const divStyle = { + + const divStyle : React.CSSProperties = { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. width: '11rem', height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` }; + return (
    diff --git a/09 ColorpRefactor/src/colorpicker.tsx b/09_ColorpRefactor/src/colorpicker.tsx similarity index 56% rename from 09 ColorpRefactor/src/colorpicker.tsx rename to 09_ColorpRefactor/src/colorpicker.tsx index 496ea71..f27ffdb 100644 --- a/09 ColorpRefactor/src/colorpicker.tsx +++ b/09_ColorpRefactor/src/colorpicker.tsx @@ -1,39 +1,42 @@ import * as React from 'react'; -import { ColorSliderComponent } from './colorslider'; - import { Color } from './color' +import { ColorSliderComponent } from './colorslider'; interface Props { color: Color; onColorUpdated: (color: Color) => void; } - -const updateColor = (props: Props, colorId: keyof Color) => (value) => { +const updateColor = (props: Props, colorId: keyof Color) => (value) => { // keyof Color ensures only 'red', 'blue' or 'green' can be passed in. props.onColorUpdated({ - ...props.color, - [colorId]: value + ...props.color, // this creates a clone of the current props.color object... + [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value. }); }; - export const ColorPicker = (props: Props) => { return (



    diff --git a/09 ColorpRefactor/src/colorslider.tsx b/09_ColorpRefactor/src/colorslider.tsx similarity index 100% rename from 09 ColorpRefactor/src/colorslider.tsx rename to 09_ColorpRefactor/src/colorslider.tsx diff --git a/09 ColorpRefactor/src/index.html b/09_ColorpRefactor/src/index.html similarity index 96% rename from 09 ColorpRefactor/src/index.html rename to 09_ColorpRefactor/src/index.html index bf13d1f..b0b7d25 100644 --- a/09 ColorpRefactor/src/index.html +++ b/09_ColorpRefactor/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/09 ColorpRefactor/src/main.tsx b/09_ColorpRefactor/src/main.tsx similarity index 92% rename from 09 ColorpRefactor/src/main.tsx rename to 09_ColorpRefactor/src/main.tsx index b220dd5..d75c95c 100644 --- a/09 ColorpRefactor/src/main.tsx +++ b/09_ColorpRefactor/src/main.tsx @@ -2,7 +2,8 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import {App} from './app'; + ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/09_ColorpRefactor/src/nameEdit.tsx b/09_ColorpRefactor/src/nameEdit.tsx new file mode 100644 index 0000000..f7f59fb --- /dev/null +++ b/09_ColorpRefactor/src/nameEdit.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import {Fragment} from 'react'; + + +interface Props { + userName : string; + editingUserName : string; + onEditingNameUpdated : (newEditingName : string) => void; + onNameUpdateRequest : () => void; +} + +export const NameEditComponent = (props : Props) => +
    + + props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> + + +
    diff --git a/09 ColorpRefactor/tsconfig.json b/09_ColorpRefactor/tsconfig.json similarity index 99% rename from 09 ColorpRefactor/tsconfig.json rename to 09_ColorpRefactor/tsconfig.json index ba8b3b7..885d474 100644 --- a/09 ColorpRefactor/tsconfig.json +++ b/09_ColorpRefactor/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/09 ColorpRefactor/webpack.config.js b/09_ColorpRefactor/webpack.config.js similarity index 74% rename from 09 ColorpRefactor/webpack.config.js rename to 09_ColorpRefactor/webpack.config.js index afeaf7d..7c85f49 100644 --- a/09 ColorpRefactor/webpack.config.js +++ b/09_ColorpRefactor/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 2e0b112437af7dafab2b32a02f2122437a9294e4 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 11:04:17 +0200 Subject: [PATCH 069/180] 10 Sidebar updated --- 10 Sidebar/.babelrc | 10 ------ 10 Sidebar/package.json | 35 ------------------ 10 Sidebar/src/main.ts | 1 - 10_Sidebar/.babelrc | 10 ++++++ 10_Sidebar/package.json | 37 +++++++++++++++++++ {10 Sidebar => 10_Sidebar}/readme.md | 33 +++++++++++++---- {10 Sidebar => 10_Sidebar}/readme_es.md | 0 {10 Sidebar => 10_Sidebar}/src/app.tsx | 10 ++++-- {10 Sidebar => 10_Sidebar}/src/hello.tsx | 0 {10 Sidebar => 10_Sidebar}/src/index.html | 2 +- {10 Sidebar => 10_Sidebar}/src/main.tsx | 6 ++-- {10 Sidebar => 10_Sidebar}/src/nameEdit.tsx | 5 +-- {10 Sidebar => 10_Sidebar}/src/sidebar.css | 0 {10 Sidebar => 10_Sidebar}/src/sidebar.tsx | 8 ++--- {10 Sidebar => 10_Sidebar}/tsconfig.json | 2 +- {10 Sidebar => 10_Sidebar}/webpack.config.js | 38 ++++++++++---------- 16 files changed, 112 insertions(+), 85 deletions(-) delete mode 100644 10 Sidebar/.babelrc delete mode 100644 10 Sidebar/package.json delete mode 100644 10 Sidebar/src/main.ts create mode 100644 10_Sidebar/.babelrc create mode 100644 10_Sidebar/package.json rename {10 Sidebar => 10_Sidebar}/readme.md (90%) rename {10 Sidebar => 10_Sidebar}/readme_es.md (100%) rename {10 Sidebar => 10_Sidebar}/src/app.tsx (75%) rename {10 Sidebar => 10_Sidebar}/src/hello.tsx (100%) rename {10 Sidebar => 10_Sidebar}/src/index.html (96%) rename {10 Sidebar => 10_Sidebar}/src/main.tsx (60%) rename {10 Sidebar => 10_Sidebar}/src/nameEdit.tsx (75%) rename {10 Sidebar => 10_Sidebar}/src/sidebar.css (100%) rename {10 Sidebar => 10_Sidebar}/src/sidebar.tsx (54%) rename {10 Sidebar => 10_Sidebar}/tsconfig.json (99%) rename {10 Sidebar => 10_Sidebar}/webpack.config.js (70%) diff --git a/10 Sidebar/.babelrc b/10 Sidebar/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/10 Sidebar/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/10 Sidebar/package.json b/10 Sidebar/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/10 Sidebar/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/10 Sidebar/src/main.ts b/10 Sidebar/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/10 Sidebar/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/10_Sidebar/.babelrc b/10_Sidebar/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/10_Sidebar/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/10_Sidebar/package.json b/10_Sidebar/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/10_Sidebar/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/10 Sidebar/readme.md b/10_Sidebar/readme.md similarity index 90% rename from 10 Sidebar/readme.md rename to 10_Sidebar/readme.md index 16f4f69..4ed6e6e 100644 --- a/10 Sidebar/readme.md +++ b/10_Sidebar/readme.md @@ -80,6 +80,8 @@ _./webpack.config.js_ - We will only use CSS Modules for custom app stylesheets. We will not use CSS Modules for other CSS files, like Bootstrap (folder node_modules). +_./webpack.config.js_ + ```diff { test: /\.css$/, @@ -134,10 +136,17 @@ _./src/index.html_ - Let's place the component adding it into the `app.tsx`: -```jsx - import {SidebarComponent} from './sidebar'; +_./src/app.tsx_ + +```diff + import * as React from 'react'; + import { HelloComponent } from './hello'; + import { NameEditComponent } from './nameEdit'; ++ import {SidebarComponent} from './sidebar'; ``` +_./src/app.tsx_ + ```diff return ( <> @@ -178,6 +187,8 @@ const classNames = require('./sidebar.css'); - Now let's add some logic to show / hide the sidebar in case the flag gets updated +_./src/sidebar.tsx_ + ```diff import * as React from 'react'; @@ -236,7 +247,7 @@ export class App extends React.Component<{}, State> { + -+
    ++
    +
    - \ No newline at end of file + diff --git a/10 Sidebar/src/main.tsx b/10_Sidebar/src/main.tsx similarity index 60% rename from 10 Sidebar/src/main.tsx rename to 10_Sidebar/src/main.tsx index c091cf4..be3985e 100644 --- a/10 Sidebar/src/main.tsx +++ b/10_Sidebar/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/10 Sidebar/src/nameEdit.tsx b/10_Sidebar/src/nameEdit.tsx similarity index 75% rename from 10 Sidebar/src/nameEdit.tsx rename to 10_Sidebar/src/nameEdit.tsx index 7d87c6a..6c544b2 100644 --- a/10 Sidebar/src/nameEdit.tsx +++ b/10_Sidebar/src/nameEdit.tsx @@ -5,11 +5,8 @@ interface Props { onChange : (event) => void; } -export const NameEditComponent = (props : Props) => { - return ( +export const NameEditComponent = (props : Props) => <> - ); -} diff --git a/10 Sidebar/src/sidebar.css b/10_Sidebar/src/sidebar.css similarity index 100% rename from 10 Sidebar/src/sidebar.css rename to 10_Sidebar/src/sidebar.css diff --git a/10 Sidebar/src/sidebar.tsx b/10_Sidebar/src/sidebar.tsx similarity index 54% rename from 10 Sidebar/src/sidebar.tsx rename to 10_Sidebar/src/sidebar.tsx index 9c14230..5e74a12 100644 --- a/10 Sidebar/src/sidebar.tsx +++ b/10_Sidebar/src/sidebar.tsx @@ -11,7 +11,7 @@ const divStyle = (props: Props): React.CSSProperties => ({ }); -export const SidebarComponent : React.StatelessComponent = (props) => -
    - {props.children} -
    +export const SidebarComponent: React.StatelessComponent = (props) => +
    + {props.children} +
    diff --git a/10 Sidebar/tsconfig.json b/10_Sidebar/tsconfig.json similarity index 99% rename from 10 Sidebar/tsconfig.json rename to 10_Sidebar/tsconfig.json index ba8b3b7..885d474 100644 --- a/10 Sidebar/tsconfig.json +++ b/10_Sidebar/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/10 Sidebar/webpack.config.js b/10_Sidebar/webpack.config.js similarity index 70% rename from 10 Sidebar/webpack.config.js rename to 10_Sidebar/webpack.config.js index 8fdf715..f9fd461 100644 --- a/10 Sidebar/webpack.config.js +++ b/10_Sidebar/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx', '.css'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' + entry: ['@babel/polyfill', + './main.tsx' ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -34,6 +33,7 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 }, }, { @@ -46,16 +46,16 @@ module.exports = { test: /\.css$/, exclude: /node_modules/, use: [ - MiniCssExtractPlugin.loader, - { - loader: 'css-loader', - options: { - modules: true, - localIdentName: '[name]__[local]___[hash:base64:5]', - camelCase: true, - }, + MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + modules: true, + localIdentName: '[name]__[local]___[hash:base64:5]', + camelCase: true, }, - ] + }, + ] }, { test: /\.(png|jpg|gif|svg)$/, @@ -78,4 +78,4 @@ module.exports = { chunkFilename: "[id].css" }), ], -}; \ No newline at end of file +}; From 6ef9be974f8358bb3fb3351769484b7fbfa43c7b Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 12:07:27 +0200 Subject: [PATCH 070/180] TableMock updated --- 11 TableMock/.babelrc | 10 ----- 11 TableMock/package.json | 35 ------------------ 11 TableMock/src/main.ts | 1 - 11 TableMock/src/memberRow.tsx | 15 -------- 11_TableMock/.babelrc | 10 +++++ 11_TableMock/package.json | 37 +++++++++++++++++++ {11 TableMock => 11_TableMock}/readme.md | 5 ++- .../src/api/memberAPI.ts | 2 +- {11 TableMock => 11_TableMock}/src/app.tsx | 4 +- 11_TableMock/src/hello.tsx | 7 ++++ {11 TableMock => 11_TableMock}/src/index.html | 2 +- {11 TableMock => 11_TableMock}/src/main.tsx | 6 ++- 11_TableMock/src/memberRow.tsx | 15 ++++++++ .../src/membersTable.tsx | 1 - .../src/model/member.ts | 2 +- .../src/model}/memberMockData.ts | 12 +++--- 11_TableMock/src/nameEdit.tsx | 12 ++++++ {11 TableMock => 11_TableMock}/tsconfig.json | 2 +- .../webpack.config.js | 32 ++++++++-------- 19 files changed, 116 insertions(+), 94 deletions(-) delete mode 100644 11 TableMock/.babelrc delete mode 100644 11 TableMock/package.json delete mode 100644 11 TableMock/src/main.ts delete mode 100644 11 TableMock/src/memberRow.tsx create mode 100644 11_TableMock/.babelrc create mode 100644 11_TableMock/package.json rename {11 TableMock => 11_TableMock}/readme.md (97%) rename {11 TableMock => 11_TableMock}/src/api/memberAPI.ts (91%) rename {11 TableMock => 11_TableMock}/src/app.tsx (82%) create mode 100644 11_TableMock/src/hello.tsx rename {11 TableMock => 11_TableMock}/src/index.html (96%) rename {11 TableMock => 11_TableMock}/src/main.tsx (60%) create mode 100644 11_TableMock/src/memberRow.tsx rename {11 TableMock => 11_TableMock}/src/membersTable.tsx (99%) rename {11 TableMock => 11_TableMock}/src/model/member.ts (97%) rename {11 TableMock/src/api => 11_TableMock/src/model}/memberMockData.ts (72%) create mode 100644 11_TableMock/src/nameEdit.tsx rename {11 TableMock => 11_TableMock}/tsconfig.json (99%) rename {11 TableMock => 11_TableMock}/webpack.config.js (74%) diff --git a/11 TableMock/.babelrc b/11 TableMock/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/11 TableMock/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/11 TableMock/package.json b/11 TableMock/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/11 TableMock/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/11 TableMock/src/main.ts b/11 TableMock/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/11 TableMock/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/11 TableMock/src/memberRow.tsx b/11 TableMock/src/memberRow.tsx deleted file mode 100644 index 1c26e2e..0000000 --- a/11 TableMock/src/memberRow.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; -import { MemberEntity } from './model/member'; - -export const MemberRow = (props: { member: MemberEntity }) => -
    - - - - \ No newline at end of file diff --git a/11_TableMock/.babelrc b/11_TableMock/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/11_TableMock/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/11_TableMock/package.json b/11_TableMock/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/11_TableMock/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/11 TableMock/readme.md b/11_TableMock/readme.md similarity index 97% rename from 11 TableMock/readme.md rename to 11_TableMock/readme.md index cac9529..01701ec 100644 --- a/11 TableMock/readme.md +++ b/11_TableMock/readme.md @@ -149,7 +149,6 @@ export class MembersTableComponent extends React.Component { this.state = {members: []}; } - // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html public componentDidMount() { @@ -187,7 +186,6 @@ export class MembersTableComponent extends React.Component { ); } } - ``` - Let's update an app.tsx @@ -207,6 +205,9 @@ export class App extends React.Component<{}, State> { return ( <> + +- +- + ); } diff --git a/11 TableMock/src/api/memberAPI.ts b/11_TableMock/src/api/memberAPI.ts similarity index 91% rename from 11 TableMock/src/api/memberAPI.ts rename to 11_TableMock/src/api/memberAPI.ts index 165d0e6..5a062b3 100644 --- a/11 TableMock/src/api/memberAPI.ts +++ b/11_TableMock/src/api/memberAPI.ts @@ -1,5 +1,5 @@ import {MemberEntity} from '../model/member'; -import MembersMockData from './memberMockData'; +import MembersMockData from '../model/memberMockData'; // Sync mock data API, inspired from: // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 diff --git a/11 TableMock/src/app.tsx b/11_TableMock/src/app.tsx similarity index 82% rename from 11 TableMock/src/app.tsx rename to 11_TableMock/src/app.tsx index f4d6cd9..74a4736 100644 --- a/11 TableMock/src/app.tsx +++ b/11_TableMock/src/app.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { MembersTableComponent } from './membersTable'; +import {MembersTableComponent} from './membersTable'; interface Props { } @@ -23,7 +23,7 @@ export class App extends React.Component { public render() { return ( <> - + ); } diff --git a/11_TableMock/src/hello.tsx b/11_TableMock/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/11_TableMock/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/11 TableMock/src/index.html b/11_TableMock/src/index.html similarity index 96% rename from 11 TableMock/src/index.html rename to 11_TableMock/src/index.html index bf13d1f..b0b7d25 100644 --- a/11 TableMock/src/index.html +++ b/11_TableMock/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/11 TableMock/src/main.tsx b/11_TableMock/src/main.tsx similarity index 60% rename from 11 TableMock/src/main.tsx rename to 11_TableMock/src/main.tsx index c091cf4..be3985e 100644 --- a/11 TableMock/src/main.tsx +++ b/11_TableMock/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/11_TableMock/src/memberRow.tsx b/11_TableMock/src/memberRow.tsx new file mode 100644 index 0000000..9e56039 --- /dev/null +++ b/11_TableMock/src/memberRow.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import {MemberEntity} from './model/member'; + +export const MemberRow = (props: {member : MemberEntity}) => +
    + + + + diff --git a/11 TableMock/src/membersTable.tsx b/11_TableMock/src/membersTable.tsx similarity index 99% rename from 11 TableMock/src/membersTable.tsx rename to 11_TableMock/src/membersTable.tsx index bfeb6bd..7d84b8f 100644 --- a/11 TableMock/src/membersTable.tsx +++ b/11_TableMock/src/membersTable.tsx @@ -21,7 +21,6 @@ export class MembersTableComponent extends React.Component { this.state = {members: []}; } - // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html public componentDidMount() { diff --git a/11 TableMock/src/model/member.ts b/11_TableMock/src/model/member.ts similarity index 97% rename from 11 TableMock/src/model/member.ts rename to 11_TableMock/src/model/member.ts index 0cfe36d..8977f30 100644 --- a/11 TableMock/src/model/member.ts +++ b/11_TableMock/src/model/member.ts @@ -8,4 +8,4 @@ export const createEmptyMember = () : MemberEntity => ({ id: -1, login: "", avatar_url: "" -}); \ No newline at end of file +}); diff --git a/11 TableMock/src/api/memberMockData.ts b/11_TableMock/src/model/memberMockData.ts similarity index 72% rename from 11 TableMock/src/api/memberMockData.ts rename to 11_TableMock/src/model/memberMockData.ts index 3831824..8a2846a 100644 --- a/11 TableMock/src/api/memberMockData.ts +++ b/11_TableMock/src/model/memberMockData.ts @@ -1,17 +1,17 @@ -import { MemberEntity } from '../model/member'; +import {MemberEntity} from './member'; -var MembersMockData: MemberEntity[] = - [ - { +var MembersMockData : MemberEntity[] = + [ + { id: 1457912, login: "brauliodiez", avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" - }, + }, { id: 4374977, login: "Nasdan", avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" } - ]; + ]; export default MembersMockData; diff --git a/11_TableMock/src/nameEdit.tsx b/11_TableMock/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/11_TableMock/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/11 TableMock/tsconfig.json b/11_TableMock/tsconfig.json similarity index 99% rename from 11 TableMock/tsconfig.json rename to 11_TableMock/tsconfig.json index ba8b3b7..885d474 100644 --- a/11 TableMock/tsconfig.json +++ b/11_TableMock/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/11 TableMock/webpack.config.js b/11_TableMock/webpack.config.js similarity index 74% rename from 11 TableMock/webpack.config.js rename to 11_TableMock/webpack.config.js index afeaf7d..7c85f49 100644 --- a/11 TableMock/webpack.config.js +++ b/11_TableMock/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From f9e1649bcf5f626b4d2b4ceff7e92c10bb95bd5b Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 12:26:24 +0200 Subject: [PATCH 071/180] 12 Table Http Updated --- 12 TableHttp/.babelrc | 10 ----- 12 TableHttp/package.json | 35 ----------------- 12 TableHttp/src/main.ts | 1 - 12 TableHttp/src/memberRow.tsx | 15 ------- 12_TableHttp/.babelrc | 10 +++++ 12_TableHttp/package.json | 39 +++++++++++++++++++ {12 TableHttp => 12_TableHttp}/readme.md | 11 +++++- {12 TableHttp => 12_TableHttp}/readme_es.md | 0 .../src/api/memberAPI.ts | 3 +- {12 TableHttp => 12_TableHttp}/src/app.tsx | 4 +- 12_TableHttp/src/hello.tsx | 7 ++++ {12 TableHttp => 12_TableHttp}/src/index.html | 2 +- {12 TableHttp => 12_TableHttp}/src/main.tsx | 6 ++- .../src/memberHead.tsx | 2 - 12_TableHttp/src/memberRow.tsx | 15 +++++++ .../src/membersTable.tsx | 6 +-- .../src/model/member.ts | 2 +- 12_TableHttp/src/model/memberMockData.ts | 17 ++++++++ 12_TableHttp/src/nameEdit.tsx | 12 ++++++ {12 TableHttp => 12_TableHttp}/tsconfig.json | 2 +- .../webpack.config.js | 32 +++++++-------- 21 files changed, 138 insertions(+), 93 deletions(-) delete mode 100644 12 TableHttp/.babelrc delete mode 100644 12 TableHttp/package.json delete mode 100644 12 TableHttp/src/main.ts delete mode 100644 12 TableHttp/src/memberRow.tsx create mode 100644 12_TableHttp/.babelrc create mode 100644 12_TableHttp/package.json rename {12 TableHttp => 12_TableHttp}/readme.md (94%) rename {12 TableHttp => 12_TableHttp}/readme_es.md (100%) rename {12 TableHttp => 12_TableHttp}/src/api/memberAPI.ts (95%) rename {12 TableHttp => 12_TableHttp}/src/app.tsx (82%) create mode 100644 12_TableHttp/src/hello.tsx rename {12 TableHttp => 12_TableHttp}/src/index.html (96%) rename {12 TableHttp => 12_TableHttp}/src/main.tsx (60%) rename {12 TableHttp => 12_TableHttp}/src/memberHead.tsx (98%) create mode 100644 12_TableHttp/src/memberRow.tsx rename {12 TableHttp => 12_TableHttp}/src/membersTable.tsx (92%) rename {12 TableHttp => 12_TableHttp}/src/model/member.ts (97%) create mode 100644 12_TableHttp/src/model/memberMockData.ts create mode 100644 12_TableHttp/src/nameEdit.tsx rename {12 TableHttp => 12_TableHttp}/tsconfig.json (99%) rename {12 TableHttp => 12_TableHttp}/webpack.config.js (74%) diff --git a/12 TableHttp/.babelrc b/12 TableHttp/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/12 TableHttp/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/12 TableHttp/package.json b/12 TableHttp/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/12 TableHttp/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/12 TableHttp/src/main.ts b/12 TableHttp/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/12 TableHttp/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/12 TableHttp/src/memberRow.tsx b/12 TableHttp/src/memberRow.tsx deleted file mode 100644 index 1c26e2e..0000000 --- a/12 TableHttp/src/memberRow.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; -import { MemberEntity } from './model/member'; - -export const MemberRow = (props: { member: MemberEntity }) => - - - - - \ No newline at end of file diff --git a/12_TableHttp/.babelrc b/12_TableHttp/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/12_TableHttp/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/12_TableHttp/package.json b/12_TableHttp/package.json new file mode 100644 index 0000000..6321a58 --- /dev/null +++ b/12_TableHttp/package.json @@ -0,0 +1,39 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "core-js": "^2.5.7", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9", + "whatwg-fetch": "^3.0.0" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/12 TableHttp/readme.md b/12_TableHttp/readme.md similarity index 94% rename from 12 TableHttp/readme.md rename to 12_TableHttp/readme.md index 2a4ee02..7463b6e 100644 --- a/12 TableHttp/readme.md +++ b/12_TableHttp/readme.md @@ -28,13 +28,18 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Let's remove the file _mermberMockData.ts_ in _src/api_ directory. +- Just to provide support to old browsers let's install the following libraries: + +```javascript +npm install whatwg-fetch --save-dev +``` + - Let's replace _memberAPI_ load members with the fetch / promise one: _./src/api/memberAPI.ts_ ```javascript import {MemberEntity} from '../model/member'; -import {} from 'core-js'; import {} from 'whatwg-fetch'; // Sync mock data API, inspired from: @@ -84,6 +89,8 @@ export const memberAPI = new MemberAPI(); ``` - Add a new component _memberHead_ to create the table's header: +_./src/memberHead.tsx_ + ```javascript import * as React from 'react'; import { MemberEntity } from './model/member'; @@ -104,7 +111,7 @@ export const MemberHead = () => - Now it's time to update our _membersTable_ component.
    -_./src/memberTable.tsx_ +_./src/membersTable.tsx_ - Import the new component : diff --git a/12 TableHttp/readme_es.md b/12_TableHttp/readme_es.md similarity index 100% rename from 12 TableHttp/readme_es.md rename to 12_TableHttp/readme_es.md diff --git a/12 TableHttp/src/api/memberAPI.ts b/12_TableHttp/src/api/memberAPI.ts similarity index 95% rename from 12 TableHttp/src/api/memberAPI.ts rename to 12_TableHttp/src/api/memberAPI.ts index d378abe..6185ee0 100644 --- a/12 TableHttp/src/api/memberAPI.ts +++ b/12_TableHttp/src/api/memberAPI.ts @@ -1,6 +1,5 @@ import {MemberEntity} from '../model/member'; -import {} from 'core-js'; -import {} from 'whatwg-fetch'; +import {fetch} from 'whatwg-fetch'; // Sync mock data API, inspired from: // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 diff --git a/12 TableHttp/src/app.tsx b/12_TableHttp/src/app.tsx similarity index 82% rename from 12 TableHttp/src/app.tsx rename to 12_TableHttp/src/app.tsx index f4d6cd9..74a4736 100644 --- a/12 TableHttp/src/app.tsx +++ b/12_TableHttp/src/app.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { MembersTableComponent } from './membersTable'; +import {MembersTableComponent} from './membersTable'; interface Props { } @@ -23,7 +23,7 @@ export class App extends React.Component { public render() { return ( <> - + ); } diff --git a/12_TableHttp/src/hello.tsx b/12_TableHttp/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/12_TableHttp/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/12 TableHttp/src/index.html b/12_TableHttp/src/index.html similarity index 96% rename from 12 TableHttp/src/index.html rename to 12_TableHttp/src/index.html index bf13d1f..b0b7d25 100644 --- a/12 TableHttp/src/index.html +++ b/12_TableHttp/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/12 TableHttp/src/main.tsx b/12_TableHttp/src/main.tsx similarity index 60% rename from 12 TableHttp/src/main.tsx rename to 12_TableHttp/src/main.tsx index c091cf4..be3985e 100644 --- a/12 TableHttp/src/main.tsx +++ b/12_TableHttp/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/12 TableHttp/src/memberHead.tsx b/12_TableHttp/src/memberHead.tsx similarity index 98% rename from 12 TableHttp/src/memberHead.tsx rename to 12_TableHttp/src/memberHead.tsx index aa262b0..1d8a947 100644 --- a/12 TableHttp/src/memberHead.tsx +++ b/12_TableHttp/src/memberHead.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { MemberEntity } from './model/member'; export const MemberHead = () => -
    - \ No newline at end of file diff --git a/12_TableHttp/src/memberRow.tsx b/12_TableHttp/src/memberRow.tsx new file mode 100644 index 0000000..9e56039 --- /dev/null +++ b/12_TableHttp/src/memberRow.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import {MemberEntity} from './model/member'; + +export const MemberRow = (props: {member : MemberEntity}) => + + + + + diff --git a/12 TableHttp/src/membersTable.tsx b/12_TableHttp/src/membersTable.tsx similarity index 92% rename from 12 TableHttp/src/membersTable.tsx rename to 12_TableHttp/src/membersTable.tsx index 422201e..fce8e40 100644 --- a/12 TableHttp/src/membersTable.tsx +++ b/12_TableHttp/src/membersTable.tsx @@ -2,7 +2,8 @@ import * as React from 'react'; import { MemberEntity } from './model/member'; import { memberAPI } from './api/memberAPI'; import { MemberRow } from './memberRow'; -import {MemberHead} from './memberHead'; +import { MemberHead } from './memberHead'; +import {} from 'core-js'; interface Props { } @@ -22,7 +23,6 @@ export class MembersTableComponent extends React.Component { this.state = { members: [] }; } - // Standard react lifecycle function: // https://facebook.github.io/react/docs/component-specs.html public componentDidMount() { @@ -38,7 +38,7 @@ export class MembersTableComponent extends React.Component {

    Members Page

    - Avatar - - Id - - Name -
    +- Avatar +- +- Id +- +- Name +-
    + Avatar + + Id + + Name +
    +- Avatar +- +- Id +- +- Name +-
    - - - {props.member.id} - - {props.member.login} -
    + + + {props.member.id} + + {props.member.login} +
    - - - {props.member.id} - - {props.member.login} -
    Avatar @@ -14,4 +13,3 @@ export const MemberHead = () => Name
    + + + {props.member.id} + + {props.member.login} +
    - + { diff --git a/12 TableHttp/src/model/member.ts b/12_TableHttp/src/model/member.ts similarity index 97% rename from 12 TableHttp/src/model/member.ts rename to 12_TableHttp/src/model/member.ts index 0cfe36d..8977f30 100644 --- a/12 TableHttp/src/model/member.ts +++ b/12_TableHttp/src/model/member.ts @@ -8,4 +8,4 @@ export const createEmptyMember = () : MemberEntity => ({ id: -1, login: "", avatar_url: "" -}); \ No newline at end of file +}); diff --git a/12_TableHttp/src/model/memberMockData.ts b/12_TableHttp/src/model/memberMockData.ts new file mode 100644 index 0000000..8a2846a --- /dev/null +++ b/12_TableHttp/src/model/memberMockData.ts @@ -0,0 +1,17 @@ +import {MemberEntity} from './member'; + +var MembersMockData : MemberEntity[] = + [ + { + id: 1457912, + login: "brauliodiez", + avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" + }, + { + id: 4374977, + login: "Nasdan", + avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" + } + ]; + +export default MembersMockData; diff --git a/12_TableHttp/src/nameEdit.tsx b/12_TableHttp/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/12_TableHttp/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/12 TableHttp/tsconfig.json b/12_TableHttp/tsconfig.json similarity index 99% rename from 12 TableHttp/tsconfig.json rename to 12_TableHttp/tsconfig.json index ba8b3b7..885d474 100644 --- a/12 TableHttp/tsconfig.json +++ b/12_TableHttp/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/12 TableHttp/webpack.config.js b/12_TableHttp/webpack.config.js similarity index 74% rename from 12 TableHttp/webpack.config.js rename to 12_TableHttp/webpack.config.js index afeaf7d..7c85f49 100644 --- a/12 TableHttp/webpack.config.js +++ b/12_TableHttp/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 3c7adda971e45efabd979442c2f697107ec0fde2 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 12:59:43 +0200 Subject: [PATCH 072/180] 13_Should update migrated --- 13 ShouldUpdate/.babelrc | 10 -- 13 ShouldUpdate/package.json | 35 ------ 13 ShouldUpdate/src/face.tsx | 53 -------- 13 ShouldUpdate/src/main.ts | 1 - 13_ShouldUpdate/.babelrc | 10 ++ 13_ShouldUpdate/package.json | 37 ++++++ .../readme.md | 117 +++++++----------- .../src/app.tsx | 18 +-- .../src/content/five.png | Bin .../src/content/four.png | Bin .../src/content/one.png | Bin .../src/content/site.css | 0 .../src/content/three.png | Bin .../src/content/two.png | Bin 13_ShouldUpdate/src/face.tsx | 44 +++++++ 13_ShouldUpdate/src/hello.tsx | 7 ++ .../src/index.html | 2 +- .../src/main.tsx | 6 +- 13_ShouldUpdate/src/nameEdit.tsx | 12 ++ .../tsconfig.json | 2 +- .../webpack.config.js | 34 ++--- 21 files changed, 187 insertions(+), 201 deletions(-) delete mode 100644 13 ShouldUpdate/.babelrc delete mode 100644 13 ShouldUpdate/package.json delete mode 100644 13 ShouldUpdate/src/face.tsx delete mode 100644 13 ShouldUpdate/src/main.ts create mode 100644 13_ShouldUpdate/.babelrc create mode 100644 13_ShouldUpdate/package.json rename {13 ShouldUpdate => 13_ShouldUpdate}/readme.md (73%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/app.tsx (53%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/content/five.png (100%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/content/four.png (100%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/content/one.png (100%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/content/site.css (100%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/content/three.png (100%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/content/two.png (100%) create mode 100644 13_ShouldUpdate/src/face.tsx create mode 100644 13_ShouldUpdate/src/hello.tsx rename {13 ShouldUpdate => 13_ShouldUpdate}/src/index.html (96%) rename {13 ShouldUpdate => 13_ShouldUpdate}/src/main.tsx (60%) create mode 100644 13_ShouldUpdate/src/nameEdit.tsx rename {13 ShouldUpdate => 13_ShouldUpdate}/tsconfig.json (99%) rename {13 ShouldUpdate => 13_ShouldUpdate}/webpack.config.js (72%) diff --git a/13 ShouldUpdate/.babelrc b/13 ShouldUpdate/.babelrc deleted file mode 100644 index 03dfd13..0000000 --- a/13 ShouldUpdate/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} diff --git a/13 ShouldUpdate/package.json b/13 ShouldUpdate/package.json deleted file mode 100644 index d0ba60a..0000000 --- a/13 ShouldUpdate/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "sample", - "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", - "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1" - } -} diff --git a/13 ShouldUpdate/src/face.tsx b/13 ShouldUpdate/src/face.tsx deleted file mode 100644 index 8c9c9dc..0000000 --- a/13 ShouldUpdate/src/face.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from 'react'; - -const setSatisfactionClass = (level: number) => { - if (level < 100) { - return "very-dissatisfied" - } - - if (level < 200) { - return "somewhat-dissatisfied" - } - - if (level < 300) { - return "neither" - } - - if (level < 400) { - return "somewhat-satisfied" - } - - return "very-satisfied" -} - -interface Props { - level : number; -} - -export class FaceComponent extends React.Component { - - shouldComponentUpdate(nextProps : Props, nextState) - { - const rangeChange = [100, 200, 300, 400]; - - let index = 0; - let isRangeChange = false; - - while(!isRangeChange && index < rangeChange.length) { - isRangeChange = (this.props.level < rangeChange[index] && nextProps.level >= rangeChange[index]) - || - (this.props.level > rangeChange[index] && nextProps.level <= rangeChange[index]) - ; - - index++; - } - - return isRangeChange; - } - - render() { - return ( -
    - ); - } -} diff --git a/13 ShouldUpdate/src/main.ts b/13 ShouldUpdate/src/main.ts deleted file mode 100644 index d3f7db2..0000000 --- a/13 ShouldUpdate/src/main.ts +++ /dev/null @@ -1 +0,0 @@ -document.write("Hello from main.ts !"); \ No newline at end of file diff --git a/13_ShouldUpdate/.babelrc b/13_ShouldUpdate/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/13_ShouldUpdate/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/13_ShouldUpdate/package.json b/13_ShouldUpdate/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/13_ShouldUpdate/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/13 ShouldUpdate/readme.md b/13_ShouldUpdate/readme.md similarity index 73% rename from 13 ShouldUpdate/readme.md rename to 13_ShouldUpdate/readme.md index a7d01c6..8755623 100644 --- a/13 ShouldUpdate/readme.md +++ b/13_ShouldUpdate/readme.md @@ -33,7 +33,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are npm install ``` -- Remove _ +- Let's make a cleanup on _app.tsx_ _./src/app.tsx_ @@ -99,6 +99,8 @@ _./src/content/site.css_ } ``` +- You can copy this images into the _content_ folde, url where you can download them: https://github.com/Lemoncode/react-by-sample/tree/master/13%20ShouldUpdate/src/content + - In _webpack.config.js_ let's add the new _css_ file as entry point: _webpack.config.js_ @@ -111,18 +113,6 @@ entry: [ ], ``` -- We need to add as well a loader to handle images in _webpackconfig.js_ (add it if it is not defined on webpack config yet): - -_webpack.config.js_ - -```javascript -{ - test: /\.(png|jpg)$/, - exclude: /node_modules/, - loader: 'url-loader?limit=10000' -}, -``` - - Let's create a simple _faceComponent_ under _src_, we will start by just adding something hardcoded in file _src/face.tsx_: @@ -176,6 +166,8 @@ npm start - Now it's time to link the property with the proper faces, let's create a style function for that in _face.tsx_ +_./src/face.tsx_ + ```diff import * as React from 'react'; @@ -211,37 +203,40 @@ export const FaceComponent = (props : {level : number}) => { - In _app.tsx_ let's add a state variable to hold the current satisfaction level plus an slider to let the user update it. -```jsx +_./src/app.tsx_ + +```diff import * as React from 'react'; import {FaceComponent} from './face' interface Props { } -interface State { - satisfactionLevel : number; -} ++ interface State { ++ satisfactionLevel : number; ++ } export class App extends React.Component { constructor(props: Props) { super(props); - this.state = {satisfactionLevel: 300}; ++ this.state = {satisfactionLevel: 300}; } public render() { return (
    - this.setState({satisfactionLevel :event.target.value} as State)} - /> -
    - {this.state.satisfactionLevel} -
    - ++ this.setState({satisfactionLevel :event.target.value} as State)} ++ /> ++
    ++ {this.state.satisfactionLevel} ++
    +- ++
    ); } @@ -258,59 +253,37 @@ export class App extends React.Component { the level just changes the satisfaction range, we need to move the component to state component: -```jsx -import * as React from 'react'; - -interface Props { - level : number; -} - -export class FaceComponent extends React.Component { - - setSatisfactionClass(level : number) { - if(level < 100) { - return "very-dissatisfied" - } - - if(level < 200) { - return "somewhat-dissatisfied" - } - - if(level < 300) { - return "neither" - } - - if(level < 400) { - return "somewhat-satisfied" - } +_./src/face.tsx_ - return "very-satisfied" - } +```diff +import * as React from 'react'; - shouldComponentUpdate(nextProps : Props, nextState) - { - const rangeChange = [100, 200, 300, 400]; ++ interface Props { ++ level : number; ++ } - let index = 0; - let isRangeChange = false; ++ const isRangeChange = (oldValue : number, newValue : number) => { ++ const oldValueClass = setSatisfactionClass(oldValue); ++ const newValueClass = setSatisfactionClass(newValue); ++ ++ return oldValueClass !== newValueClass; ++ } - while(!isRangeChange && index < rangeChange.length) { - isRangeChange = (this.props.level < rangeChange[index] && nextProps.level >= rangeChange[index]) - || - (this.props.level > rangeChange[index] && nextProps.level <= rangeChange[index]) - ; ++ export class FaceComponent extends React.Component { +- export const FaceComponent = (props: { level: number }) => { - index++; - } ++ shouldComponentUpdate(nextProps : Props, nextState) ++ { ++ return isRangeChange(this.props.level, nextProps.level); ++ } - return isRangeChange; - } - render() { ++ render() { return ( -
    +-
    +
    ); - } ++ } } ``` diff --git a/13 ShouldUpdate/src/app.tsx b/13_ShouldUpdate/src/app.tsx similarity index 53% rename from 13 ShouldUpdate/src/app.tsx rename to 13_ShouldUpdate/src/app.tsx index 91346f0..7902266 100644 --- a/13 ShouldUpdate/src/app.tsx +++ b/13_ShouldUpdate/src/app.tsx @@ -1,32 +1,32 @@ import * as React from 'react'; -import { FaceComponent } from './face'; +import {FaceComponent} from './face'; interface Props { } interface State { - satisfactionLevel: number; + satisfactionLevel : number; } export class App extends React.Component { constructor(props: Props) { super(props); - this.state = { satisfactionLevel: 300 }; + this.state = {satisfactionLevel: 300}; } public render() { return (
    this.setState({ satisfactionLevel: event.target.value } as State)} + min="0" + max="500" + value={this.state.satisfactionLevel} + onChange={(event : any) => this.setState({satisfactionLevel :event.target.value} as State)} /> -
    +
    {this.state.satisfactionLevel} -
    +
    ); diff --git a/13 ShouldUpdate/src/content/five.png b/13_ShouldUpdate/src/content/five.png similarity index 100% rename from 13 ShouldUpdate/src/content/five.png rename to 13_ShouldUpdate/src/content/five.png diff --git a/13 ShouldUpdate/src/content/four.png b/13_ShouldUpdate/src/content/four.png similarity index 100% rename from 13 ShouldUpdate/src/content/four.png rename to 13_ShouldUpdate/src/content/four.png diff --git a/13 ShouldUpdate/src/content/one.png b/13_ShouldUpdate/src/content/one.png similarity index 100% rename from 13 ShouldUpdate/src/content/one.png rename to 13_ShouldUpdate/src/content/one.png diff --git a/13 ShouldUpdate/src/content/site.css b/13_ShouldUpdate/src/content/site.css similarity index 100% rename from 13 ShouldUpdate/src/content/site.css rename to 13_ShouldUpdate/src/content/site.css diff --git a/13 ShouldUpdate/src/content/three.png b/13_ShouldUpdate/src/content/three.png similarity index 100% rename from 13 ShouldUpdate/src/content/three.png rename to 13_ShouldUpdate/src/content/three.png diff --git a/13 ShouldUpdate/src/content/two.png b/13_ShouldUpdate/src/content/two.png similarity index 100% rename from 13 ShouldUpdate/src/content/two.png rename to 13_ShouldUpdate/src/content/two.png diff --git a/13_ShouldUpdate/src/face.tsx b/13_ShouldUpdate/src/face.tsx new file mode 100644 index 0000000..16e181e --- /dev/null +++ b/13_ShouldUpdate/src/face.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; + +const setSatisfactionClass = (level: number) => { + if (level < 100) { + return "very-dissatisfied" + } + + if (level < 200) { + return "somewhat-dissatisfied" + } + + if (level < 300) { + return "neither" + } + + if (level < 400) { + return "somewhat-satisfied" + } + + return "very-satisfied" +} + +const isRangeChange = (oldValue: number, newValue: number) => { + const oldValueClass = setSatisfactionClass(oldValue); + const newValueClass = setSatisfactionClass(newValue); + + return oldValueClass !== newValueClass; +} + +interface Props { + level: number; +} + +export class FaceComponent extends React.Component { + shouldComponentUpdate(nextProps: Props, nextState) { + return isRangeChange(this.props.level, nextProps.level); + } + + render() { + return ( +
    + ); + } +} diff --git a/13_ShouldUpdate/src/hello.tsx b/13_ShouldUpdate/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/13_ShouldUpdate/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/13 ShouldUpdate/src/index.html b/13_ShouldUpdate/src/index.html similarity index 96% rename from 13 ShouldUpdate/src/index.html rename to 13_ShouldUpdate/src/index.html index bf13d1f..b0b7d25 100644 --- a/13 ShouldUpdate/src/index.html +++ b/13_ShouldUpdate/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/13 ShouldUpdate/src/main.tsx b/13_ShouldUpdate/src/main.tsx similarity index 60% rename from 13 ShouldUpdate/src/main.tsx rename to 13_ShouldUpdate/src/main.tsx index c091cf4..be3985e 100644 --- a/13 ShouldUpdate/src/main.tsx +++ b/13_ShouldUpdate/src/main.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; +import {App} from './app'; + +import { HelloComponent } from './hello'; ReactDOM.render( - , + , document.getElementById('root') ); diff --git a/13_ShouldUpdate/src/nameEdit.tsx b/13_ShouldUpdate/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/13_ShouldUpdate/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/13 ShouldUpdate/tsconfig.json b/13_ShouldUpdate/tsconfig.json similarity index 99% rename from 13 ShouldUpdate/tsconfig.json rename to 13_ShouldUpdate/tsconfig.json index ba8b3b7..885d474 100644 --- a/13 ShouldUpdate/tsconfig.json +++ b/13_ShouldUpdate/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/13 ShouldUpdate/webpack.config.js b/13_ShouldUpdate/webpack.config.js similarity index 72% rename from 13 ShouldUpdate/webpack.config.js rename to 13_ShouldUpdate/webpack.config.js index 8b13038..f103c2c 100644 --- a/13 ShouldUpdate/webpack.config.js +++ b/13_ShouldUpdate/webpack.config.js @@ -1,23 +1,22 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css', - './content/site.css' - ], + entry: ['@babel/polyfill', + './main.tsx', + './content/site.css', + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -26,7 +25,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -35,8 +34,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -48,7 +48,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -61,5 +61,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From b34fa2a140af3a36194d6b46efa199a3d0d757ef Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 16:23:10 +0200 Subject: [PATCH 073/180] 14 updated --- 14 ReactRouter/.babelrc | 4 +-- 14 ReactRouter/package.json | 44 +++++++++++++++++--------------- 14 ReactRouter/readme.md | 1 + 14 ReactRouter/src/app.tsx | 32 +++++++++++++++++++++++ 14 ReactRouter/src/hello.tsx | 7 +++++ 14 ReactRouter/src/index.html | 2 +- 14 ReactRouter/src/main.tsx | 19 +++++++------- 14 ReactRouter/src/nameEdit.tsx | 12 +++++++++ 14 ReactRouter/src/pageA.tsx | 5 +--- 14 ReactRouter/src/pageB.tsx | 5 +--- 14 ReactRouter/tsconfig.json | 2 +- 14 ReactRouter/webpack.config.js | 32 +++++++++++------------ 12 files changed, 106 insertions(+), 59 deletions(-) create mode 100644 14 ReactRouter/src/app.tsx create mode 100644 14 ReactRouter/src/hello.tsx create mode 100644 14 ReactRouter/src/nameEdit.tsx diff --git a/14 ReactRouter/.babelrc b/14 ReactRouter/.babelrc index 03dfd13..957cae3 100644 --- a/14 ReactRouter/.babelrc +++ b/14 ReactRouter/.babelrc @@ -1,9 +1,9 @@ { "presets": [ [ - "env", + "@babel/preset-env", { - "modules": false + "useBuiltIns": "entry" } ] ] diff --git a/14 ReactRouter/package.json b/14 ReactRouter/package.json index 2756d78..7ee65fc 100644 --- a/14 ReactRouter/package.json +++ b/14 ReactRouter/package.json @@ -1,7 +1,7 @@ { - "name": "sample", + "name": "reactbysample", "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { "start": "webpack-dev-server --mode development --inline --hot --open", @@ -11,27 +11,29 @@ "author": "", "license": "ISC", "devDependencies": { - "@types/react": "^16.3.8", - "@types/react-dom": "^16.0.5", - "@types/react-router-dom": "^4.2.6", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "@types/react-router-dom": "^4.3.1", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "3.1.0" + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" }, "dependencies": { - "bootstrap": "^4.1.0", - "react": "^16.3.1", - "react-dom": "^16.3.1", - "react-router-dom": "^4.2.2" + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-router-dom": "^4.3.1" } } diff --git a/14 ReactRouter/readme.md b/14 ReactRouter/readme.md index ab1c7c7..5dde73f 100644 --- a/14 ReactRouter/readme.md +++ b/14 ReactRouter/readme.md @@ -73,6 +73,7 @@ npm install @types/react-router-dom --save-dev import * as React from 'react'; import * as ReactDOM from 'react-dom'; - import {App} from './app'; +- import { HelloComponent } from './hello'; + import { HashRouter, Switch, Route } from 'react-router-dom'; + import {PageA} from './pageA'; + import {PageB} from './pageB'; diff --git a/14 ReactRouter/src/app.tsx b/14 ReactRouter/src/app.tsx new file mode 100644 index 0000000..055eced --- /dev/null +++ b/14 ReactRouter/src/app.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + + + ); + } +} \ No newline at end of file diff --git a/14 ReactRouter/src/hello.tsx b/14 ReactRouter/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/14 ReactRouter/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/14 ReactRouter/src/index.html b/14 ReactRouter/src/index.html index bf13d1f..b0b7d25 100644 --- a/14 ReactRouter/src/index.html +++ b/14 ReactRouter/src/index.html @@ -10,4 +10,4 @@

    Sample app

    - \ No newline at end of file + diff --git a/14 ReactRouter/src/main.tsx b/14 ReactRouter/src/main.tsx index 6441e0d..3701e3d 100644 --- a/14 ReactRouter/src/main.tsx +++ b/14 ReactRouter/src/main.tsx @@ -1,16 +1,15 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { HashRouter, Switch, Route } from 'react-router-dom'; -import { PageA } from './pageA'; -import { PageB } from './pageB'; +import {PageA} from './pageA'; +import {PageB} from './pageB'; ReactDOM.render( - - - - - - - , - document.getElementById('root') + + + + + + + ,document.getElementById('root') ); diff --git a/14 ReactRouter/src/nameEdit.tsx b/14 ReactRouter/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/14 ReactRouter/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/14 ReactRouter/src/pageA.tsx b/14 ReactRouter/src/pageA.tsx index e415fdd..10b6b62 100644 --- a/14 ReactRouter/src/pageA.tsx +++ b/14 ReactRouter/src/pageA.tsx @@ -1,12 +1,9 @@ import * as React from "react" import { Link } from 'react-router-dom'; -export const PageA = () => { - return ( +export const PageA = () =>

    Hello from page A


    Navigate to Page B
    - ) -} diff --git a/14 ReactRouter/src/pageB.tsx b/14 ReactRouter/src/pageB.tsx index ece3ee7..241c19d 100644 --- a/14 ReactRouter/src/pageB.tsx +++ b/14 ReactRouter/src/pageB.tsx @@ -1,12 +1,9 @@ import * as React from "react" import { Link } from 'react-router-dom'; -export const PageB = () => { - return ( +export const PageB = () =>

    Hello from page B


    Navigate to Page A
    - ) -} diff --git a/14 ReactRouter/tsconfig.json b/14 ReactRouter/tsconfig.json index ba8b3b7..885d474 100644 --- a/14 ReactRouter/tsconfig.json +++ b/14 ReactRouter/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/14 ReactRouter/webpack.config.js b/14 ReactRouter/webpack.config.js index afeaf7d..7c85f49 100644 --- a/14 ReactRouter/webpack.config.js +++ b/14 ReactRouter/webpack.config.js @@ -1,22 +1,21 @@ -let path = require('path'); -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), - filename: 'bundle.js', + filename: 'bundle.js' }, devtool: 'source-map', devServer: { @@ -25,7 +24,7 @@ module.exports = { host: 'localhost', port: 8080, stats: 'errors-only' - }, + }, module: { rules: [ { @@ -34,8 +33,9 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"] @@ -47,7 +47,7 @@ module.exports = { name: 'assets/img/[name].[ext]?[hash]' } }, - ], + ], }, plugins: [ //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin @@ -60,5 +60,5 @@ module.exports = { filename: "[name].css", chunkFilename: "[id].css" }), - ], -}; \ No newline at end of file + ], +}; From 7ab15835a86d35495796a47516a40e70d130bdc9 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 10 Oct 2018 16:27:15 +0200 Subject: [PATCH 074/180] name update --- 01 HelloReact/package.json | 0 {14 ReactRouter => 14_ReactRouter}/.babelrc | 0 {14 ReactRouter => 14_ReactRouter}/package.json | 0 {14 ReactRouter => 14_ReactRouter}/readme.md | 0 {14 ReactRouter => 14_ReactRouter}/readme_es.md | 0 {14 ReactRouter => 14_ReactRouter}/src/app.tsx | 0 {14 ReactRouter => 14_ReactRouter}/src/hello.tsx | 0 {14 ReactRouter => 14_ReactRouter}/src/index.html | 0 {14 ReactRouter => 14_ReactRouter}/src/main.tsx | 0 {14 ReactRouter => 14_ReactRouter}/src/nameEdit.tsx | 0 {14 ReactRouter => 14_ReactRouter}/src/pageA.tsx | 0 {14 ReactRouter => 14_ReactRouter}/src/pageB.tsx | 0 {14 ReactRouter => 14_ReactRouter}/tsconfig.json | 0 {14 ReactRouter => 14_ReactRouter}/webpack.config.js | 0 14 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 01 HelloReact/package.json rename {14 ReactRouter => 14_ReactRouter}/.babelrc (100%) rename {14 ReactRouter => 14_ReactRouter}/package.json (100%) rename {14 ReactRouter => 14_ReactRouter}/readme.md (100%) rename {14 ReactRouter => 14_ReactRouter}/readme_es.md (100%) rename {14 ReactRouter => 14_ReactRouter}/src/app.tsx (100%) rename {14 ReactRouter => 14_ReactRouter}/src/hello.tsx (100%) rename {14 ReactRouter => 14_ReactRouter}/src/index.html (100%) rename {14 ReactRouter => 14_ReactRouter}/src/main.tsx (100%) rename {14 ReactRouter => 14_ReactRouter}/src/nameEdit.tsx (100%) rename {14 ReactRouter => 14_ReactRouter}/src/pageA.tsx (100%) rename {14 ReactRouter => 14_ReactRouter}/src/pageB.tsx (100%) rename {14 ReactRouter => 14_ReactRouter}/tsconfig.json (100%) rename {14 ReactRouter => 14_ReactRouter}/webpack.config.js (100%) diff --git a/01 HelloReact/package.json b/01 HelloReact/package.json new file mode 100644 index 0000000..e69de29 diff --git a/14 ReactRouter/.babelrc b/14_ReactRouter/.babelrc similarity index 100% rename from 14 ReactRouter/.babelrc rename to 14_ReactRouter/.babelrc diff --git a/14 ReactRouter/package.json b/14_ReactRouter/package.json similarity index 100% rename from 14 ReactRouter/package.json rename to 14_ReactRouter/package.json diff --git a/14 ReactRouter/readme.md b/14_ReactRouter/readme.md similarity index 100% rename from 14 ReactRouter/readme.md rename to 14_ReactRouter/readme.md diff --git a/14 ReactRouter/readme_es.md b/14_ReactRouter/readme_es.md similarity index 100% rename from 14 ReactRouter/readme_es.md rename to 14_ReactRouter/readme_es.md diff --git a/14 ReactRouter/src/app.tsx b/14_ReactRouter/src/app.tsx similarity index 100% rename from 14 ReactRouter/src/app.tsx rename to 14_ReactRouter/src/app.tsx diff --git a/14 ReactRouter/src/hello.tsx b/14_ReactRouter/src/hello.tsx similarity index 100% rename from 14 ReactRouter/src/hello.tsx rename to 14_ReactRouter/src/hello.tsx diff --git a/14 ReactRouter/src/index.html b/14_ReactRouter/src/index.html similarity index 100% rename from 14 ReactRouter/src/index.html rename to 14_ReactRouter/src/index.html diff --git a/14 ReactRouter/src/main.tsx b/14_ReactRouter/src/main.tsx similarity index 100% rename from 14 ReactRouter/src/main.tsx rename to 14_ReactRouter/src/main.tsx diff --git a/14 ReactRouter/src/nameEdit.tsx b/14_ReactRouter/src/nameEdit.tsx similarity index 100% rename from 14 ReactRouter/src/nameEdit.tsx rename to 14_ReactRouter/src/nameEdit.tsx diff --git a/14 ReactRouter/src/pageA.tsx b/14_ReactRouter/src/pageA.tsx similarity index 100% rename from 14 ReactRouter/src/pageA.tsx rename to 14_ReactRouter/src/pageA.tsx diff --git a/14 ReactRouter/src/pageB.tsx b/14_ReactRouter/src/pageB.tsx similarity index 100% rename from 14 ReactRouter/src/pageB.tsx rename to 14_ReactRouter/src/pageB.tsx diff --git a/14 ReactRouter/tsconfig.json b/14_ReactRouter/tsconfig.json similarity index 100% rename from 14 ReactRouter/tsconfig.json rename to 14_ReactRouter/tsconfig.json diff --git a/14 ReactRouter/webpack.config.js b/14_ReactRouter/webpack.config.js similarity index 100% rename from 14 ReactRouter/webpack.config.js rename to 14_ReactRouter/webpack.config.js From 49a66efb0f1eea193e845d065da6c6a83cefe1b7 Mon Sep 17 00:00:00 2001 From: Braulio Date: Sat, 13 Oct 2018 12:21:31 +0200 Subject: [PATCH 075/180] basic sample working, pending fixes --- 15 LoginForm/.babelrc | 10 - 15 LoginForm/package.json | 37 - 15 LoginForm/readme.md | 738 ---------------- .../common/content-center/content-center.tsx | 10 - 15 LoginForm/src/common/index.ts | 2 - .../src/common/panel/components/body.tsx | 9 - .../src/common/panel/components/header.tsx | 10 - 15 LoginForm/src/common/panel/index.ts | 1 - 15 LoginForm/src/common/panel/panel.tsx | 17 - 15 LoginForm/src/index.html | 11 - 15 LoginForm/src/main.tsx | 15 - 15 LoginForm/src/pages/b/index.ts | 1 - .../src/pages/login/components/form.tsx | 41 - 15 LoginForm/src/pages/login/index.ts | 1 - 15 LoginForm/src/pages/login/loginPage.tsx | 24 - .../src/pages/login/loginPageContainer.tsx | 44 - .../src/pages/utils/error-boundaries.tsx | 40 - 15 LoginForm/webpack.config.js | 79 -- 15_LoginForm/.babelrc | 10 + 15_LoginForm/package.json | 41 + 15_LoginForm/readme.md | 788 ++++++++++++++++++ .../src/api/login.ts | 1 - 15_LoginForm/src/app.tsx | 32 + 15_LoginForm/src/common/index.tsx | 1 + 15_LoginForm/src/common/notification.tsx | 53 ++ 15_LoginForm/src/hello.tsx | 7 + 15_LoginForm/src/index.html | 14 + 15_LoginForm/src/main.tsx | 15 + .../src/model/login.ts | 2 +- 15_LoginForm/src/nameEdit.tsx | 12 + 15_LoginForm/src/pages/b/index.ts | 1 + .../src/pages/b/pageB.tsx | 7 +- 15_LoginForm/src/pages/login/index.ts | 1 + 15_LoginForm/src/pages/login/loginForm.tsx | 39 + 15_LoginForm/src/pages/login/loginPage.tsx | 82 ++ {15 LoginForm => 15_LoginForm}/tsconfig.json | 2 +- 15_LoginForm/webpack.config.js | 64 ++ 37 files changed, 1164 insertions(+), 1098 deletions(-) delete mode 100644 15 LoginForm/.babelrc delete mode 100644 15 LoginForm/package.json delete mode 100644 15 LoginForm/readme.md delete mode 100644 15 LoginForm/src/common/content-center/content-center.tsx delete mode 100644 15 LoginForm/src/common/index.ts delete mode 100644 15 LoginForm/src/common/panel/components/body.tsx delete mode 100644 15 LoginForm/src/common/panel/components/header.tsx delete mode 100644 15 LoginForm/src/common/panel/index.ts delete mode 100644 15 LoginForm/src/common/panel/panel.tsx delete mode 100644 15 LoginForm/src/index.html delete mode 100644 15 LoginForm/src/main.tsx delete mode 100644 15 LoginForm/src/pages/b/index.ts delete mode 100644 15 LoginForm/src/pages/login/components/form.tsx delete mode 100644 15 LoginForm/src/pages/login/index.ts delete mode 100644 15 LoginForm/src/pages/login/loginPage.tsx delete mode 100644 15 LoginForm/src/pages/login/loginPageContainer.tsx delete mode 100644 15 LoginForm/src/pages/utils/error-boundaries.tsx delete mode 100644 15 LoginForm/webpack.config.js create mode 100644 15_LoginForm/.babelrc create mode 100644 15_LoginForm/package.json create mode 100644 15_LoginForm/readme.md rename {15 LoginForm => 15_LoginForm}/src/api/login.ts (99%) create mode 100644 15_LoginForm/src/app.tsx create mode 100644 15_LoginForm/src/common/index.tsx create mode 100644 15_LoginForm/src/common/notification.tsx create mode 100644 15_LoginForm/src/hello.tsx create mode 100644 15_LoginForm/src/index.html create mode 100644 15_LoginForm/src/main.tsx rename {15 LoginForm => 15_LoginForm}/src/model/login.ts (97%) create mode 100644 15_LoginForm/src/nameEdit.tsx create mode 100644 15_LoginForm/src/pages/b/index.ts rename {15 LoginForm => 15_LoginForm}/src/pages/b/pageB.tsx (59%) create mode 100644 15_LoginForm/src/pages/login/index.ts create mode 100644 15_LoginForm/src/pages/login/loginForm.tsx create mode 100644 15_LoginForm/src/pages/login/loginPage.tsx rename {15 LoginForm => 15_LoginForm}/tsconfig.json (99%) create mode 100644 15_LoginForm/webpack.config.js diff --git a/15 LoginForm/.babelrc b/15 LoginForm/.babelrc deleted file mode 100644 index 911d8c1..0000000 --- a/15 LoginForm/.babelrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presets": [ - [ - "env", - { - "modules": false - } - ] - ] -} \ No newline at end of file diff --git a/15 LoginForm/package.json b/15 LoginForm/package.json deleted file mode 100644 index f3c3b40..0000000 --- a/15 LoginForm/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "helloworld-react", - "version": "1.0.0", - "description": "State basics.", - "main": "index.js", - "scripts": { - "start": "webpack-dev-server --mode development --inline --hot --open", - "build": "webpack --mode development" - }, - "author": "Lemoncode and Front End Master Students", - "license": "MIT", - "dependencies": { - "bootstrap": "~4.1.1", - "react": "~16.4.0", - "react-dom": "~16.4.0", - "react-router-dom": "^4.2.2" - }, - "devDependencies": { - "@types/history": "^4.6.2", - "@types/react": "~16.3.16", - "@types/react-dom": "~16.0.5", - "@types/react-router-dom": "^4.2.7", - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.3", - "babel-preset-env": "^1.7.0", - "css-loader": "~0.28.11", - "file-loader": "~1.1.11", - "html-webpack-plugin": "~3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "~0.21.0", - "typescript": "~2.9.1", - "url-loader": "~1.0.1", - "webpack": "~4.10.2", - "webpack-cli": "^3.0.2", - "webpack-dev-server": "^3.1.4" - } -} diff --git a/15 LoginForm/readme.md b/15 LoginForm/readme.md deleted file mode 100644 index cf24f19..0000000 --- a/15 LoginForm/readme.md +++ /dev/null @@ -1,738 +0,0 @@ -# 15 Login form - -In this sample we are going to implement a basic login page, that will redirect -the user to another page whenever the login has completed successfully. - -We will attempt to create a [realistic layout](http://bootsnipp.com/snippets/featured/compact-login-form-bs-3), in order to keep simplicity we will -break it into subcomponents and perform some refactor in order to make the solution -more maintenable. - -We will take a startup point sample _14 ReactRouter_: - -Summary steps: - -- Let's rename pageA to LoginPage. -- Let's create a 'Pages' subfolder and reorganize pages. -- Let's build the layout for this page. -- Let's add navigation on login button clicked. -- Let's add login validation fake api. -- Let's connect it into the login button logic. -- Let's do a bit of refactor and clean up extracting functionality to reusable components. -- Let's add some form validation (mandatory fields, minlength). - -## Prerequisites - -Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. - -> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. - -## Steps to build it - -- Copy the content from _14 React Router_ and execute _npm install_. - -- Let's rename _pageA.tsx_ to _loginPage.tsx_. - -- Let's update as well the name of the component. - -_./src/loginPage.tsx_ - -```javascript -import * as React from "react" -import { Link } from 'react-router-dom'; - -export const LoginPage = () => { - return ( -
    -

    Hello from page A

    -
    - Navigate to Page B -
    - ) -} -``` - -- Now it's time to reorganize the pages structure. Let's create a subfolders -called _pages_ - -- Under that subfolder let's create two more subfolders _login_ and _b_ - -- Let's place the _pages_ under that subfolders: _pages/login/loginPage.tsx_ -and _pages/b/pageB. - -``` -. -└── src/ - │ - ā”œā”€ā”€ model/ - └── pages/ - ā”œā”€ā”€ login/ - │ └── loginPage.tsx - └── b/ - └── pageB.tsx - -``` - -- In some cases this pages will contain more secondary files, let's create -a simple _index.tsx_ file for each of this pages. - -- Under _pages/login/index.ts. - -_./src/pages/login/index.ts_ - -```javascript -export {LoginPage} from './loginPage'; -``` - -- Under _pages/b/index.ts_ - -_./src/pages/b/index.ts_ - -```javascript -export {PageB} from './pageB' -``` -- The structure look like this: - -``` -. -└── src/ - │ - └── pages/ - ā”œā”€ā”€ login/ - │ ā”œā”€ā”€ index.ts - │ └── loginPage.tsx - └── b/ - ā”œā”€ā”€ index.ts - └── pageB.tsx - -``` - - -- Let's update _main.tsx_ (routes, names and add a redirect from root to login page) - -_./src/main.tsx_ - -```diff -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { HashRouter, Switch, Route } from 'react-router-dom'; -- import {PageA} from './pageA'; -- import {PageB} from './pageB'; -+ import {LoginPage} from './pages/login'; -+ import {PageB} from './pages/b'; - -ReactDOM.render( - - -- -+ - - - - , - document.getElementById('root') -); -``` - -- Let's update as well the navigation from _pageB_ to _loginPage_, _pageB.tsx_ - -_./src/pages/b/pageB.tsx_ - -```diff -import * as React from "react" -import { Link } from 'react-router-dom'; - -export const PageB = () => { - return ( -
    -

    Hello from page B

    -
    -- Navigate to Page A -+ Navigate to Login -
    - ) -} -``` - -- Let's make a quick test and check that everyting is still working fine. - -``` -npm start -``` - -- Let's build a proper _login_ layout, _loginPage.tsx_, we will base it in -the [following layout](http://bootsnipp.com/snippets/featured/compact-login-form-bs-3) -but we will break it down into subcomponents. - -- It's time to create something reusable, a panel is a good candidate for that. - -Our idea is to end up with something like: -**PSEUDCODE** - -```html - - // My content here - -``` -And the panel it self - -```html -
    -
    - - {children} - -
    -``` - -> Panels and Bootstrap 4 (cards): https://getbootstrap.com/docs/4.0/components/card/ - -_./src/common/panel/components/header.tsx_ -```tsx -import * as React from "react" - -interface Props { - title : string; -} - -export const Header = (props : Props) => -
    -

    {props.title}

    -
    -``` - -_./src/common/panel/components/body.tsx_ - -```tsx -import * as React from "react" - -interface Props { -} - -export const Body : React.StatelessComponent = (props) => -
  • - {props.children} -
  • -``` - - -_./src/common/panel/panel.tsx_ - -```tsx -export const Panel: React.StatelessComponent = (props) => -
    -
    -
      - - {props.children} - -
    -
    -``` - -_./src/common/panel/index.ts_ - -```typescript -export {Panel} from './panel'; -``` - -> How do we end up doing this nice common component? Just by first doing it in the non optimal -way and go refining / refactoring, ... not often you get the perfect design at first try, it's -an iterative process. - -- Let's create one more index file for the _common_ folder (that will grow). - -_./src/common/index.ts_ - -```typescript -export {Panel} from './panel'; -``` - -- Let's jump back into our login pages, let's create a form component - -_./src/pages/login/components/form.tsx_ - -```tsx -import * as React from "react" - -export const Form = () => { - return ( - -
    -
    - -
    -
    - -
    - -
    - - ); -} -``` - -- Now let's update our login page (fully replace the content) - -_./src/pages/login/loginPage.tsx_ - -```javascript -import * as React from "react" -import {Panel} from '../../common'; -import {Form} from './components/form'; - -export const LoginPage = () => - -
    -
    -``` - -- Let's give a try and check how is it looking. - -```bash -npm start -``` - -- - -- Let's add the navigation on button clicked (later on we will check for user and pwd) _form.tsx_. -In order to do this we will use react-router 4 "withRouter" HoC (High order component). - -_./src/pages/login/form.tsx_ - -```diff -import * as React from "react" -+ import { withRouter } from 'react-router-dom'; - - -- export const Form = () => { -+ export const Form = withRouter(({history}) => { -+ const login = () => { -+ history.push('/pageB'); -+ } - - return ( -
    - -
    -
    - -
    -
    - -
    -- -+ -
    - -
    - ); -- } -+}) -``` - -- Let's give a quick try. - -```bash -npm start -``` - -- Not bad, but we rather prefer to center the dialog, instead of getting our control dirty with -bootstrap grid sizes let's create another component. - -> To make this component fully reusable it will need some tweaking, as an excercise you can play with -it adding some props to make it generic. - -_./src/common/content-center/content-center.tsx_ - -```tsx -import * as React from "react" - -export const ContentCenter : React.StatelessComponent = (props) => -
    -
    -
    - {props.children} -
    -
    -
    -``` - -- Let's add it to our common _index.ts_ - -_./src/common/index.ts_ - -```diff -export {Panel} from './panel'; -+ export {ContentCenter} from './content-center/content-center'; -``` - -- Let's add it to our _loginPage.tsx_ - -```diff -import * as React from "react" -- import {Panel} from '../../common'; -+ import {Panel, ContentCenter} from '../../common'; -import {Form} from './components/form'; - -export const LoginPage = () => -+ - -
    -
    -+
    - -``` - -- Let's define an entity for the loginInfo let's create the following path and file -_src/model/login.ts_ - -```javascript -export interface LoginEntity { - login : string; - password : string; -} - -export const createEmptyLogin = () : LoginEntity => ({ - login: '', - password: '', -}); -``` - -- Let's add login validation fake restApi: create a folder _src/api_. and a file called -_login.ts_ - -``` -. -└── src/ - │ - ā”œā”€ā”€ api/ - │ └── login.ts - ā”œā”€ā”€ model/ - │ └── login.ts - └── pages/ - ā”œā”€ā”€ login/ - │ ā”œā”€ā”€ form.tsx - │ ā”œā”€ā”€ header.tsx - │ ā”œā”€ā”€ index.ts - │ └── loginPage.tsx - └── b/ - ā”œā”€ā”€ index.ts - └── pageB.tsx - -``` - -_./src/api/login.ts_ - -```javascript -import {LoginEntity} from '../model/login'; - -// Just a fake loginAPI -export const isValidLogin = (loginInfo : LoginEntity) : boolean => - (loginInfo.login === 'admin' && loginInfo.password === 'test'); -``` - - -- Now we can integrate it into _form.tsx_ login button, but.. it's time to think -about how do we want to structure this, Do we want _form.tsx_ to hold the state -of current user logged in and current password, plus button handler? This should -be responsibility of the container control (Let's create a new component loginPageContainer.tsx). So let's the define as state of the _loginPageContainer_ this information plus function and pass it down to -the _loginPage_ and _form_ component. Let's start with _loginPageContainer_ - - -- Let's create a _loginPageContainer.ts_ - -_./src/pages/login/loginPageContainer.tsx_ - -```tsx -import * as React from "react" -import {LoginPage} from './loginPage'; -import {LoginEntity, createEmptyLogin} from '../../model/login'; - -interface State { - loginInfo: LoginEntity; -} - -interface Props { - history; -} - -export class LoginPageContainer extends React.Component { - constructor(props) { - super(props); - - this.state = {loginInfo : createEmptyLogin()} - } - - public render() { - return ( - - ) - } -} -``` - -- Let's replace the component in the index we have: - -_./src/pages/login/index.ts_ - -```diff -- export {LoginPage} from './loginPage'; -+ export {LoginPageContainer} from './loginPageContainer'; -``` - -- And update our main.tsx - -_./src/main.tsx_ - -```diff -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { HashRouter, Switch, Route } from 'react-router-dom'; -- import { LoginPage } from './pages/login'; -+ import { LoginPageContainer } from './pages/login'; -import { PageB } from './pages/b'; - -ReactDOM.render( - - - - - - - , document.getElementById('root') -); -``` - -- Let's add the _api_ integration, plus navigation on login succeeded: - -_./src/pages/login/loginPageContainer.tsx_ - -```diff -import * as React from "react" -import {LoginPage} from './loginPage'; -import {LoginEntity, createEmptyLogin} from '../../model/login'; -+ import { withRouter } from 'react-router-dom'; -+ import {isValidLogin} from '../../api/login'; - - -interface State { - loginInfo: LoginEntity; -} - -interface Props { -+ history; -} - -- export class LoginPageContainer extends React.Component { -+ export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { - - constructor(props) { - super(props); - - this.state = {loginInfo : createEmptyLogin()} - } - -+ performLogin = () => { -+ if(isValidLogin(this.state.loginInfo)) { -+ this.props.history.push('/pageB'); -+ } -+ } - - - public render() { - return ( - - ) - } -- } -+ }) -``` - -- So far so good, let's recap: - - We will hold the login info in the container state. - - We have a method in the container that will perform the login action. - - All this will be passed down vĆ­a props to the loginPage component. - -What do we have missing? - - - A way to notify when the user has typed on a given input filed (method callback to update the login entity). - - -How are we going to do this? - - - We will map each field name with the property name of the control that will be targeted. - - By doing this, the OnChange event can propagate the change and inform about the field that have - changed (we could use a data attribute if we don't want to messup with name). - -> What happens if we have nested property? We can use the full name, then using lodash or ramda helpers -we can access the property name via the string that has _field.subfield_ like format. - -- Let's get started, first we are going to create a method in the container to update a field value: - -```diff -export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { - constructor(props) { - super(props); - - this.state = { loginInfo: createEmptyLogin() } - } - - performLogin = () => { - if (isValidLogin(this.state.loginInfo)) { - this.props.history.push('/pageB'); - } - } - -+ updateLoginField = (name, value) => { -+ this.setState({loginInfo: { -+ ...this.state.loginInfo, -+ [name]: value, -+ }}) -+ } -``` - -- Let's pass down this info to the loginPage: - -_./src/pages/login/loginPage.tsx_ - -```diff -import * as React from "react" -import {Panel, ContentCenter} from '../../common'; -import {Form} from './components/form'; -+ import { LoginEntity } from "../../model/login"; - -+ interface Props { -+ loginInfo: LoginEntity; -+ updateField : (string, any) => void; -+ doLogin : () => void; -+ } - -- export const LoginPage = () => -+ export const LoginPage = (props : Props) => - - - - - -``` - -- First use the new properties when instantiating _loginPage_ in the _container_: - -```diff - public render() { - return ( -- -+ - ) - } -``` - -- Now it's time to pass it down to the form, same approach as with the container - -_./src/pages/login/components/form.tsx_ - -```diff -import * as React from "react" -import { withRouter } from 'react-router-dom'; -import { RouteComponentProps } from 'react-router' -import { LoginEntity } from "../../../model/login"; - -interface Props { - loginInfo: LoginEntity; - updateField: (string, any) => void; - doLogin: () => void; -} - - -- export const Form = withRouter((props : RouteComponentProps) => { - export const Form = (props : Props) => -- const login = () => { -- history.push('/pageB'); -- } - -+ const onChange = (props : Props) => (e: React.ChangeEvent) => { -+ props.updateField(e.target.name, e.target.value); -+ } - -- return ( - -
    -
    - -
    -
    - -
    - -
    - -- ); -}); - -``` - -- Let's update how we instantiate the form into the loginPage: - -```diff -export const LoginPage = (props : Props) => - - -
    -
    -
    - -``` - -- We can debug and check that we get all the wheels and cogs working. - -```bash -npm start -``` - -> One refinement... this looks like we could wrap all this into a common input and use it -in any form saving some lines of code and hiding complexity, let's go for that: - -- Time for the password field - -_./src/pages/login/components/form.tsx_ - -```diff - -``` - -- Let's give a try - -``` -npm start -``` - -> And form validation? There are several libraries available, one that we had created in lemoncode -is lc-form-validation we will create a sample including this lib to validate the login form -(required fields) - -> As an excercise add a react toaster to notify when the login fails. - -> One more excercise port this layout to flexbox and new CSS standards. \ No newline at end of file diff --git a/15 LoginForm/src/common/content-center/content-center.tsx b/15 LoginForm/src/common/content-center/content-center.tsx deleted file mode 100644 index f615709..0000000 --- a/15 LoginForm/src/common/content-center/content-center.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from "react" - -export const ContentCenter : React.StatelessComponent = (props) => -
    -
    -
    - {props.children} -
    -
    -
    \ No newline at end of file diff --git a/15 LoginForm/src/common/index.ts b/15 LoginForm/src/common/index.ts deleted file mode 100644 index bf0cd1f..0000000 --- a/15 LoginForm/src/common/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {Panel} from './panel'; -export {ContentCenter} from './content-center/content-center'; \ No newline at end of file diff --git a/15 LoginForm/src/common/panel/components/body.tsx b/15 LoginForm/src/common/panel/components/body.tsx deleted file mode 100644 index edb9d76..0000000 --- a/15 LoginForm/src/common/panel/components/body.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import * as React from "react" - -interface Props { -} - -export const Body : React.StatelessComponent = (props) => -
  • - {props.children} -
  • \ No newline at end of file diff --git a/15 LoginForm/src/common/panel/components/header.tsx b/15 LoginForm/src/common/panel/components/header.tsx deleted file mode 100644 index d53ea05..0000000 --- a/15 LoginForm/src/common/panel/components/header.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from "react" - -interface Props { - title : string; -} - -export const Header = (props : Props) => -
    -

    {props.title}

    -
    \ No newline at end of file diff --git a/15 LoginForm/src/common/panel/index.ts b/15 LoginForm/src/common/panel/index.ts deleted file mode 100644 index 5e5c164..0000000 --- a/15 LoginForm/src/common/panel/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {Panel} from './panel'; \ No newline at end of file diff --git a/15 LoginForm/src/common/panel/panel.tsx b/15 LoginForm/src/common/panel/panel.tsx deleted file mode 100644 index c126f25..0000000 --- a/15 LoginForm/src/common/panel/panel.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from "react" -import { Body } from './components/body'; -import { Header } from './components/header'; - -interface Props { - title: string; -} - -export const Panel: React.StatelessComponent = (props) => -
    -
    -
      - - {props.children} - -
    -
    \ No newline at end of file diff --git a/15 LoginForm/src/index.html b/15 LoginForm/src/index.html deleted file mode 100644 index d972c34..0000000 --- a/15 LoginForm/src/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - Sample 14: ReactRouter - - -

    Sample 14: ReactRouter

    -
    - - diff --git a/15 LoginForm/src/main.tsx b/15 LoginForm/src/main.tsx deleted file mode 100644 index e2f118f..0000000 --- a/15 LoginForm/src/main.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { HashRouter, Switch, Route } from 'react-router-dom'; -import { LoginPageContainer } from './pages/login'; -import { PageB } from './pages/b'; - -ReactDOM.render( - - - - - - - , document.getElementById('root') -); diff --git a/15 LoginForm/src/pages/b/index.ts b/15 LoginForm/src/pages/b/index.ts deleted file mode 100644 index 8c7e132..0000000 --- a/15 LoginForm/src/pages/b/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {PageB} from './pageB' diff --git a/15 LoginForm/src/pages/login/components/form.tsx b/15 LoginForm/src/pages/login/components/form.tsx deleted file mode 100644 index 909a158..0000000 --- a/15 LoginForm/src/pages/login/components/form.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from "react" -import { withRouter } from 'react-router-dom'; -import { RouteComponentProps } from 'react-router' -import { LoginEntity } from "../../../model/login"; - - -interface Props { - loginInfo: LoginEntity; - updateField: (string, any) => void; - doLogin: () => void; -} - -const onChange = (props : Props) => (e: React.ChangeEvent) => { - props.updateField(e.target.name, e.target.value); -} - -export const Form = (props : Props) => - -
    -
    - -
    -
    - -
    - -
    - diff --git a/15 LoginForm/src/pages/login/index.ts b/15 LoginForm/src/pages/login/index.ts deleted file mode 100644 index 52680f1..0000000 --- a/15 LoginForm/src/pages/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {LoginPageContainer} from './loginPageContainer'; \ No newline at end of file diff --git a/15 LoginForm/src/pages/login/loginPage.tsx b/15 LoginForm/src/pages/login/loginPage.tsx deleted file mode 100644 index f07bed1..0000000 --- a/15 LoginForm/src/pages/login/loginPage.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from "react" -import { Panel, ContentCenter } from '../../common'; -import { Form } from './components/form'; -import { LoginEntity } from "../../model/login"; -import {ErrorBoundary} from '../utils/error-boundaries' - -interface Props { - loginInfo: LoginEntity; - updateField: (string, any) => void; - doLogin: () => void; -} - - -export const LoginPage = (props : Props) => - - - -
    -
    -
    -
    - diff --git a/15 LoginForm/src/pages/login/loginPageContainer.tsx b/15 LoginForm/src/pages/login/loginPageContainer.tsx deleted file mode 100644 index 047673c..0000000 --- a/15 LoginForm/src/pages/login/loginPageContainer.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import * as React from "react" -import { LoginPage } from './loginPage'; -import { LoginEntity, createEmptyLogin } from '../../model/login'; -import { withRouter } from 'react-router-dom'; -import { isValidLogin } from '../../api/login'; - -interface State { - loginInfo: LoginEntity; -} - -interface Props { - history; -} - -export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { - constructor(props) { - super(props); - - this.state = { loginInfo: createEmptyLogin() } - } - - performLogin = () => { - if (isValidLogin(this.state.loginInfo)) { - this.props.history.push('/pageB'); - } - } - - updateLoginField = (name, value) => { - this.setState({loginInfo: { - ...this.state.loginInfo, - [name]: value, - }}) - } - - public render() { - return ( - - ) - } -}) diff --git a/15 LoginForm/src/pages/utils/error-boundaries.tsx b/15 LoginForm/src/pages/utils/error-boundaries.tsx deleted file mode 100644 index d34e11e..0000000 --- a/15 LoginForm/src/pages/utils/error-boundaries.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from "react" - -interface State { - error : any, - errorInfo : any; -} - -export class ErrorBoundary extends React.Component<{}, State> { - constructor(props) { - super(props); - this.state = { error: null, errorInfo: null }; - } - - componentDidCatch(error, errorInfo) { - // Catch errors in any components below and re-render with error message - this.setState({ - error: error, - errorInfo: errorInfo - }) - // You can also log error messages to an error reporting service here - } - - render() { - if (this.state.errorInfo) { - // Error path - return ( -
    -

    Something went wrong.

    -
    - {this.state.error && this.state.error.toString()} -
    - {this.state.errorInfo.componentStack} -
    -
    - ); - } - // Normally, just render children - return this.props.children; - } -} \ No newline at end of file diff --git a/15 LoginForm/webpack.config.js b/15 LoginForm/webpack.config.js deleted file mode 100644 index 5601d52..0000000 --- a/15 LoginForm/webpack.config.js +++ /dev/null @@ -1,79 +0,0 @@ -var path = require('path'); -var webpack = require('webpack'); -var MiniCssExtractPlugin = require('mini-css-extract-plugin'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); - -var basePath = __dirname; - -module.exports = { - context: path.join(basePath, "src"), - resolve: { - extensions: ['.js', '.ts', '.tsx'] - }, - - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], - output: { - path: path.join(basePath, 'dist'), - filename: 'bundle.js' - }, - - devtool: 'source-map', - - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' - }, - - module: { - rules: [ - { - test: /\.(ts|tsx)$/, - exclude: /node_modules/, - loader: 'awesome-typescript-loader', - options: { - useBabel: true, - }, - }, - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, "css-loader"] - }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader - { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, - { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' - }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] - }, - plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin - new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true - }), - new MiniCssExtractPlugin({ - filename: "[name].css", - chunkFilename: "[id].css" - }), - ] -} diff --git a/15_LoginForm/.babelrc b/15_LoginForm/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/15_LoginForm/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/15_LoginForm/package.json b/15_LoginForm/package.json new file mode 100644 index 0000000..d836dc5 --- /dev/null +++ b/15_LoginForm/package.json @@ -0,0 +1,41 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@material-ui/core": "^3.2.0", + "@material-ui/icons": "^3.0.1", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "@types/react-router-dom": "^4.3.1", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-router-dom": "^4.3.1" + } +} diff --git a/15_LoginForm/readme.md b/15_LoginForm/readme.md new file mode 100644 index 0000000..2fedf4c --- /dev/null +++ b/15_LoginForm/readme.md @@ -0,0 +1,788 @@ +# 15 Login form + +In this sample we are going to implement a basic login page, that will redirect +the user to another page whenever the login has completed successfully. + +We will attempt to create a [realistic layout](http://bootsnipp.com/snippets/featured/compact-login-form-bs-3), in order to keep simplicity we will +break it into subcomponents and perform some refactor in order to make the solution +more maintenable. + +We will take a startup point sample _14 ReactRouter_: + +Summary steps: + +- Let's rename pageA to LoginPage. +- Let's create a 'Pages' subfolder and reorganize pages. +- Let's build the layout for this page. +- Let's add navigation on login button clicked. +- Let's add login validation fake api. +- Let's connect it into the login button logic. +- Let's do a bit of refactor and clean up extracting functionality to reusable components. +- Let's add some form validation (mandatory fields, minlength). + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Copy the content from _14 React Router_ and execute _npm install_. + +- Let's rename _pageA.tsx_ to _loginPage.tsx_. + +- Let's update as well the name of the component. + +_./src/loginPage.tsx_ + +```javascript +import * as React from "react" +import { Link } from 'react-router-dom'; + +export const LoginPage = () => { + return ( +
    +

    Hello from page A

    +
    + Navigate to Page B +
    + ) +} +``` + +- Now it's time to reorganize the pages structure. Let's create a subfolders +called _pages_ + +- Under that subfolder let's create two more subfolders _login_ and _b_ + +- Let's place the _pages_ under that subfolders: _pages/login/loginPage.tsx_ +and _pages/b/pageB. + +``` +. +└── src/ + │ + ā”œā”€ā”€ model/ + └── pages/ + ā”œā”€ā”€ login/ + │ └── loginPage.tsx + └── b/ + └── pageB.tsx + +``` + +- In some cases this pages will contain more secondary files, let's create +a simple _index.tsx_ file for each of this pages. + +- Under _pages/login/index.ts. + +_./src/pages/login/index.ts_ + +```javascript +export {LoginPage} from './loginPage'; +``` + +- Under _pages/b/index.ts_ + +_./src/pages/b/index.ts_ + +```javascript +export {PageB} from './pageB'; +``` +- The structure look like this: + +``` +. +└── src/ + │ + └── pages/ + ā”œā”€ā”€ login/ + │ ā”œā”€ā”€ index.ts + │ └── loginPage.tsx + └── b/ + ā”œā”€ā”€ index.ts + └── pageB.tsx + +``` + +- Let's update _main.tsx_ (routes, names and add a redirect from root to login page) + +_./src/main.tsx_ + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +- import {PageA} from './pageA'; +- import {PageB} from './pageB'; ++ import {LoginPage} from './pages/login'; ++ import {PageB} from './pages/b'; + +ReactDOM.render( + + +- ++ + + + + , + document.getElementById('root') +); +``` + +- Let's update as well the navigation from _pageB_ to _loginPage_, _pageB.tsx_ + +_./src/pages/b/pageB.tsx_ + +```diff +import * as React from "react" +import { Link } from 'react-router-dom'; + +export const PageB = () => { + return ( +
    +

    Hello from page B

    +
    +- Navigate to Page A ++ Navigate to Login +
    + ) +} +``` + +- Let's make a quick test and check that everyting is still working fine. + +``` +npm start +``` + +- Let's build a proper _login_ layout, _loginPage.tsx_, we will base it in +the [following layout](http://bootsnipp.com/snippets/featured/compact-login-form-bs-3) +but we will break it down into subcomponents. + +- To build a nice layout, we will install _@material-ui/core_ + +```bash +npm install @material-ui/core --save-dev +``` + +- Now we could create a login form it could look somethin like: + +```javascript +import * as React from "react" +import { Link } from 'react-router-dom'; +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { FormHelperText } from "@material-ui/core"; + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +const LoginPageInner = (props) => { + const { classes } = props; + + return ( + + + +
    + + + +
    +
    +
    + ) +} + +export const LoginPage = withStyles(styles)(LoginPageInner); +``` + +- This can be ok, but if we take a deeper look to this component, we could break down into two, one is the +card itself the other the form dialog, so it should finally look like: + +** Proposal ** + +```javascript + + + + + + +``` +- Let's create the loginformcomponent: + +_./src/pages/login/loginForm.tsx_ + +```javascript +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { FormHelperText } from "@material-ui/core"; + +export const LoginForm = (props) => { + const { classes } = props; + + return ( +
    + + + +
    + ) +} +``` + +- And let's update the _loginPage.tsx_ + +_./src/pages/loginPage.tsx_ + +```javascript +import * as React from "react" +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import {LoginForm} from './loginForm'; + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +const LoginPageInner = (props) => { + const { classes } = props; + + return ( + + + + + + + ) +} + +export const LoginPage = withStyles(styles)(LoginPageInner); +``` + +- Let's give a try and check how is it looking. + +```bash +npm start +``` + +- Le'ts add the navigation on button clicked, we will do it in two steps. + +- First we will expose a method to do that in the loginPage. + +```diff ++ import { withRouter } from 'react-router-dom'; ++ import {History} from 'history'; + +// ... + ++ const styles = theme => ({ ++ card: { ++ maxWidth: 400, ++ margin: '0 auto', ++ }, ++ }); + ++ interface Props { ++ history: History; ++ } + +const LoginPageInner = (props) => { + const { classes } = props; + ++ const onLogin = () => { ++ history.push('/pageB'); ++ } + + return ( + + + +- ++ + + + ) +} + +- export const LoginPage = withStyles(styles)(LoginPageInner); ++ export const LoginPage = withRouter(withStyles(styles)(LoginPageInner)); +``` + +- Let's add the navigation on button clicked (later on we will check for user and pwd) _form.tsx_. +In order to do this we will use react-router 4 "withRouter" HoC (High order component). + +_./src/pages/login/form.tsx_ + +```diff +import * as React from "react" ++ import { withRouter } from 'react-router-dom'; + ++ interface Props { ++ onLogin : () => void; ++} + ++export const LoginForm = (props : Props) => { +- export const LoginForm = () => { ++ const { onLogin } = props; + + return ( +
    + +
    +
    + +
    +
    + +
    +- ++ +
    + +
    + ); +- } ++}) +``` + +- Let's give a quick try. + +```bash +npm start +``` + +Ok, we can navigate whenever we click on the login page. + +- Let's keep on progressing, now is time to collect the username and password info, and check if +password is valid or not. + +- Let's define an entity for the loginInfo let's create the following path and file +_src/model/login.ts_ + +```javascript +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); +``` + +- Let's add login validation fake restApi: create a folder _src/api_. and a file called +_login.ts_ + +_./src/api/login.ts_ + +```javascript +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); +``` + +- How it should look + +``` +. +└── src/ + │ + ā”œā”€ā”€ api/ + │ └── login.ts + ā”œā”€ā”€ model/ + │ └── login.ts + └── pages/ + ā”œā”€ā”€ login/ + │ ā”œā”€ā”€ form.tsx + │ ā”œā”€ā”€ header.tsx + │ ā”œā”€ā”€ index.ts + │ └── loginPage.tsx + └── b/ + ā”œā”€ā”€ index.ts + └── pageB.tsx + +``` + +- Now we can integrate it into _form.tsx_ login button, but.. it's time to think +about how do we want to structure this, Do we want _form.tsx_ to hold the state +of current user logged in and current password, plus button handler? This should +be responsibility of the container control (Let's create a new component loginPageContainer.tsx). So let's the define as state of the _loginPageContainer_ this information plus function and pass it down to +the _loginPage_ and _form_ component. Let's start with _loginPageContainer_ + + +- And update our main.tsx + +_./src/main.tsx_ + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +- import { LoginPage } from './pages/login'; ++ import { LoginPageContainer } from './pages/login'; +import { PageB } from './pages/b'; + +ReactDOM.render( + + + + + + + , document.getElementById('root') +); +``` + +- Let's add the _api_ integration, plus navigation on login succeeded: + +- First let's create a login state and add the api integration. + +_./src/pages/login/loginPage.tsx_ + +```diff +import * as React from "react" +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter } from 'react-router-dom'; +import {History} from 'history'; ++ import { LoginEntity, createEmptyLogin } from '../../model/login'; ++ import { isValidLogin } from '../../api/login'; + ++ interface State { ++ loginInfo: LoginEntity; ++ } + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +interface Props { + history: History; +} + +- const LoginPageInner = (props) => { ++ export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { +- const { classes, history } = props; + ++ constructor(props) { ++ super(props); ++ ++ this.state = { loginInfo: createEmptyLogin() } ++ } + +- const onLogin = () => { ++ onLogin = () => { ++ if (isValidLogin(this.state.loginInfo)) { ++ this.props.history.push('/pageB'); +- history.push('/pageB'); ++ } + } + ++ public render() { ++ const { classes } = this.props; + + return ( + + + + + + + ) ++ } +} + +export const LoginPage = withStyles(styles)(withRouter(LoginPageInner)); +``` + +- Now let's read the data from the textfields components (user and password). + +_./src/pages/login/loginPage.tsx_ + +```diff +class LoginPageInner extends React.Component { + + onLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } + ++ onUpdateLoginField = (name, value) => { ++ this.setState({loginInfo: { ++ ...this.state.loginInfo, ++ [name]: value, ++ }}) ++ } + + render() { + const { classes } = this.props; + + return ( + + + + + + + ) + + } +} +``` + +_./src/pages/login/loginForm.tsx_ + +```diff +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; ++ import { LoginEntity } from "../../model/login"; + +interface Props { + onLogin : () => void; ++ updateField: (string, any) => void; ++ loginInfo : LoginEntity; +} + +export const LoginForm = (props : Props) => { +- const { onLogin } = props; ++ const { onLogin, updateField, loginInfo } = props; + ++ const onTexFieldChange = (fieldId) => (e) => { ++ updateField(fieldId, e.target.value); ++ } + + return ( +
    + + + +
    + ) +} +``` + +- Let's display a notification when the login validation fails. + +- First we will create a simple notification component, base on _react material ui_ _snackbar_ + +```javascript +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles } from "@material-ui/core"; + +interface Props { + classes?: any; + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => ({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const {classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); +``` + +- Let's expose this common component via an _index_ file. + +_./src/common/index.tsx_ + +```javascript +export * from './notification'; +``` + +- Now let's instantiate this in our _loginPage_ + +_./src/pages/login/loginPage.tsx_ + +```diff +import * as React from "react" +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { History } from 'history'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; ++ import { NotificationComponent } from '../../common' + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +interface State { + loginInfo: LoginEntity; ++ showLoginFailedMsg: boolean; +} + + +interface Props extends RouteComponentProps { + classes?: any; +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { loginInfo: createEmptyLogin(), ++ showLoginFailedMsg : false, + } + } + + onLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); ++ } else { ++ this.setState({showLoginFailedMsg: true}); ++ } + } + + onUpdateLoginField = (name: string, value) => { + this.setState({ + loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }) + } + + render() { + const { classes } = this.props; + + return ( + <> + + + + + + ++ this.setState({showLoginFailedMsg: false})} ++ /> + + ) + + } +} + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); +``` + +> And form validation? There are several libraries available, one that we had created in lemoncode +is lc-form-validation we will create a sample including this lib to validate the login form +(required fields) + diff --git a/15 LoginForm/src/api/login.ts b/15_LoginForm/src/api/login.ts similarity index 99% rename from 15 LoginForm/src/api/login.ts rename to 15_LoginForm/src/api/login.ts index d542d9e..1f7d4f3 100644 --- a/15 LoginForm/src/api/login.ts +++ b/15_LoginForm/src/api/login.ts @@ -3,4 +3,3 @@ import {LoginEntity} from '../model/login'; // Just a fake loginAPI export const isValidLogin = (loginInfo : LoginEntity) : boolean => (loginInfo.login === 'admin' && loginInfo.password === 'test'); - diff --git a/15_LoginForm/src/app.tsx b/15_LoginForm/src/app.tsx new file mode 100644 index 0000000..055eced --- /dev/null +++ b/15_LoginForm/src/app.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + + + ); + } +} \ No newline at end of file diff --git a/15_LoginForm/src/common/index.tsx b/15_LoginForm/src/common/index.tsx new file mode 100644 index 0000000..3f0d15b --- /dev/null +++ b/15_LoginForm/src/common/index.tsx @@ -0,0 +1 @@ +export * from './notification'; \ No newline at end of file diff --git a/15_LoginForm/src/common/notification.tsx b/15_LoginForm/src/common/notification.tsx new file mode 100644 index 0000000..d0f481d --- /dev/null +++ b/15_LoginForm/src/common/notification.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles } from "@material-ui/core"; + +interface Props { + classes?: any; + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => ({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const {classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); diff --git a/15_LoginForm/src/hello.tsx b/15_LoginForm/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/15_LoginForm/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/15_LoginForm/src/index.html b/15_LoginForm/src/index.html new file mode 100644 index 0000000..0fcc01e --- /dev/null +++ b/15_LoginForm/src/index.html @@ -0,0 +1,14 @@ + + + + + + + + + +
    +
    +
    + + diff --git a/15_LoginForm/src/main.tsx b/15_LoginForm/src/main.tsx new file mode 100644 index 0000000..61b017d --- /dev/null +++ b/15_LoginForm/src/main.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import {LoginPage} from './pages/login'; +import {PageB} from './pages/b'; + +ReactDOM.render( + + + + + + + ,document.getElementById('root') +); diff --git a/15 LoginForm/src/model/login.ts b/15_LoginForm/src/model/login.ts similarity index 97% rename from 15 LoginForm/src/model/login.ts rename to 15_LoginForm/src/model/login.ts index a9da6e2..081e6fb 100644 --- a/15 LoginForm/src/model/login.ts +++ b/15_LoginForm/src/model/login.ts @@ -6,4 +6,4 @@ export interface LoginEntity { export const createEmptyLogin = () : LoginEntity => ({ login: '', password: '', -}); \ No newline at end of file +}); diff --git a/15_LoginForm/src/nameEdit.tsx b/15_LoginForm/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/15_LoginForm/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/15_LoginForm/src/pages/b/index.ts b/15_LoginForm/src/pages/b/index.ts new file mode 100644 index 0000000..913631b --- /dev/null +++ b/15_LoginForm/src/pages/b/index.ts @@ -0,0 +1 @@ +export {PageB} from './pageB'; \ No newline at end of file diff --git a/15 LoginForm/src/pages/b/pageB.tsx b/15_LoginForm/src/pages/b/pageB.tsx similarity index 59% rename from 15 LoginForm/src/pages/b/pageB.tsx rename to 15_LoginForm/src/pages/b/pageB.tsx index 259758c..bbde6e3 100644 --- a/15 LoginForm/src/pages/b/pageB.tsx +++ b/15_LoginForm/src/pages/b/pageB.tsx @@ -1,12 +1,9 @@ import * as React from "react" import { Link } from 'react-router-dom'; -export const PageB = () => { - return ( +export const PageB = () =>

    Hello from page B


    - Navigate to Login + Navigate to Login
    - ) -} diff --git a/15_LoginForm/src/pages/login/index.ts b/15_LoginForm/src/pages/login/index.ts new file mode 100644 index 0000000..50d85bb --- /dev/null +++ b/15_LoginForm/src/pages/login/index.ts @@ -0,0 +1 @@ +export {LoginPage} from './loginPage'; \ No newline at end of file diff --git a/15_LoginForm/src/pages/login/loginForm.tsx b/15_LoginForm/src/pages/login/loginForm.tsx new file mode 100644 index 0000000..3776270 --- /dev/null +++ b/15_LoginForm/src/pages/login/loginForm.tsx @@ -0,0 +1,39 @@ +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { LoginEntity } from "../../model/login"; + +interface Props { + onLogin: () => void; + onUpdateField: (string, any) => void; + loginInfo : LoginEntity; +} + +export const LoginForm = (props: Props) => { + const { onLogin, onUpdateField, loginInfo } = props; + + const onTexFieldChange = (fieldId) => (e) => { + onUpdateField(fieldId, e.target.value); + } + + return ( +
    + + + +
    + ) +} diff --git a/15_LoginForm/src/pages/login/loginPage.tsx b/15_LoginForm/src/pages/login/loginPage.tsx new file mode 100644 index 0000000..0ea5628 --- /dev/null +++ b/15_LoginForm/src/pages/login/loginPage.tsx @@ -0,0 +1,82 @@ +import * as React from "react" +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { History } from 'history'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; +import { NotificationComponent } from '../../common' + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +interface State { + loginInfo: LoginEntity; + showLoginFailedMsg: boolean; +} + + +interface Props extends RouteComponentProps { + classes?: any; +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { loginInfo: createEmptyLogin(), + showLoginFailedMsg : false, + } + } + + onLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } else { + this.setState({showLoginFailedMsg: true}); + } + } + + onUpdateLoginField = (name: string, value) => { + this.setState({ + loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }) + } + + render() { + const { classes } = this.props; + + return ( + <> + + + + + + + this.setState({showLoginFailedMsg: false})} + /> + + ) + + } +} + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); diff --git a/15 LoginForm/tsconfig.json b/15_LoginForm/tsconfig.json similarity index 99% rename from 15 LoginForm/tsconfig.json rename to 15_LoginForm/tsconfig.json index ba8b3b7..885d474 100644 --- a/15 LoginForm/tsconfig.json +++ b/15_LoginForm/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/15_LoginForm/webpack.config.js b/15_LoginForm/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/15_LoginForm/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From f5bf1a16f3bc6d02c2c2a87403ca945ab6157913 Mon Sep 17 00:00:00 2001 From: Braulio Date: Sat, 13 Oct 2018 12:32:35 +0200 Subject: [PATCH 076/180] updates added muittheme --- 15_LoginForm/readme.md | 2 ++ 15_LoginForm/src/app.tsx | 32 ------------------------ 15_LoginForm/src/common/notification.tsx | 9 ++++--- 15_LoginForm/src/main.tsx | 27 +++++++++++++------- 4 files changed, 25 insertions(+), 45 deletions(-) delete mode 100644 15_LoginForm/src/app.tsx diff --git a/15_LoginForm/readme.md b/15_LoginForm/readme.md index 2fedf4c..a84d581 100644 --- a/15_LoginForm/readme.md +++ b/15_LoginForm/readme.md @@ -782,6 +782,8 @@ class LoginPageInner extends React.Component { export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ``` +Watchout new typgraphy and snackbar: https://github.com/mui-org/material-ui/issues/13144 + > And form validation? There are several libraries available, one that we had created in lemoncode is lc-form-validation we will create a sample including this lib to validate the login form (required fields) diff --git a/15_LoginForm/src/app.tsx b/15_LoginForm/src/app.tsx deleted file mode 100644 index 055eced..0000000 --- a/15_LoginForm/src/app.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import * as React from 'react'; -import { HelloComponent } from './hello'; -import { NameEditComponent } from './nameEdit'; - -interface Props { -} - -interface State { - userName: string; -} - -export class App extends React.Component { - constructor(props: Props) { - super(props); - - this.state = { userName: 'defaultUserName' }; - } - - setUsernameState = (event) => { - this.setState({ userName: event.target.value }); - } - - - public render() { - return ( - <> - - - - ); - } -} \ No newline at end of file diff --git a/15_LoginForm/src/common/notification.tsx b/15_LoginForm/src/common/notification.tsx index d0f481d..4ee7f26 100644 --- a/15_LoginForm/src/common/notification.tsx +++ b/15_LoginForm/src/common/notification.tsx @@ -19,18 +19,19 @@ const styles = theme => ({ }); const NotificationComponentInner = (props: Props) => { - const {classes, message, show, onClose } = props; + const { classes, message, show, onClose } = props; return ( {message}} @@ -45,7 +46,7 @@ const NotificationComponentInner = (props: Props) => { , ]} - + /> ) } diff --git a/15_LoginForm/src/main.tsx b/15_LoginForm/src/main.tsx index 61b017d..62678a0 100644 --- a/15_LoginForm/src/main.tsx +++ b/15_LoginForm/src/main.tsx @@ -1,15 +1,24 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { HashRouter, Switch, Route } from 'react-router-dom'; -import {LoginPage} from './pages/login'; -import {PageB} from './pages/b'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); ReactDOM.render( - - - - - - - ,document.getElementById('root') + + + + + + + + + , document.getElementById('root') ); From c0ce05956df799ce79fe0fa9444982192ef006ad Mon Sep 17 00:00:00 2001 From: Braulio Date: Sat, 13 Oct 2018 12:49:20 +0200 Subject: [PATCH 077/180] sample cleaned up pending update readme --- 15_LoginForm/src/common/notification.tsx | 7 +++---- 15_LoginForm/src/pages/login/loginPage.tsx | 10 ++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/15_LoginForm/src/common/notification.tsx b/15_LoginForm/src/common/notification.tsx index 4ee7f26..5396faf 100644 --- a/15_LoginForm/src/common/notification.tsx +++ b/15_LoginForm/src/common/notification.tsx @@ -3,16 +3,15 @@ import Button from '@material-ui/core/Button'; import Snackbar from '@material-ui/core/Snackbar'; import IconButton from '@material-ui/core/IconButton'; import CloseIcon from '@material-ui/icons/Close'; -import { withStyles } from "@material-ui/core"; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; -interface Props { - classes?: any; +interface Props extends WithStyles { message: string; show: boolean; onClose: () => void; } -const styles = theme => ({ +const styles = theme => createStyles({ close: { padding: theme.spacing.unit / 2, }, diff --git a/15_LoginForm/src/pages/login/loginPage.tsx b/15_LoginForm/src/pages/login/loginPage.tsx index 0ea5628..7dc5363 100644 --- a/15_LoginForm/src/pages/login/loginPage.tsx +++ b/15_LoginForm/src/pages/login/loginPage.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import { withStyles } from '@material-ui/core/styles'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; import Card from '@material-ui/core/Card'; import CardHeader from '@material-ui/core/CardHeader'; import CardContent from '@material-ui/core/CardContent'; @@ -10,21 +10,23 @@ import { LoginEntity, createEmptyLogin } from '../../model/login'; import { isValidLogin } from '../../api/login'; import { NotificationComponent } from '../../common' -const styles = theme => ({ +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ card: { maxWidth: 400, margin: '0 auto', }, }); + + interface State { loginInfo: LoginEntity; showLoginFailedMsg: boolean; } -interface Props extends RouteComponentProps { - classes?: any; +interface Props extends RouteComponentProps, WithStyles { } class LoginPageInner extends React.Component { From 217cbbfdf5bc452e7d32e9af81ed97a8a56cab4a Mon Sep 17 00:00:00 2001 From: Braulio Date: Sat, 13 Oct 2018 16:27:22 +0200 Subject: [PATCH 078/180] readme completed pending fix warning snackbar --- 15_LoginForm/readme.md | 193 ++++++++++++--------- 15_LoginForm/src/pages/login/loginPage.tsx | 4 +- 2 files changed, 110 insertions(+), 87 deletions(-) diff --git a/15_LoginForm/readme.md b/15_LoginForm/readme.md index a84d581..161b553 100644 --- a/15_LoginForm/readme.md +++ b/15_LoginForm/readme.md @@ -146,7 +146,7 @@ export const PageB = () => {

    Hello from page B


    - Navigate to Page A -+ Navigate to Login ++ Navigate to Login
    ) } @@ -158,6 +158,26 @@ export const PageB = () => { npm start ``` +- Time to remove 'Sample app' text from the main _html_. + +_./src/index.html_ + +```diff + + + + + + + +
    +-

    Sample app

    +
    +
    + + +``` + - Let's build a proper _login_ layout, _loginPage.tsx_, we will base it in the [following layout](http://bootsnipp.com/snippets/featured/compact-login-form-bs-3) but we will break it down into subcomponents. @@ -165,15 +185,18 @@ but we will break it down into subcomponents. - To build a nice layout, we will install _@material-ui/core_ ```bash -npm install @material-ui/core --save-dev +npm install @material-ui/core @material-ui/icons --save-dev ``` - Now we could create a login form it could look somethin like: +_./src/pages/loginPage.tsx_ + ```javascript import * as React from "react" import { Link } from 'react-router-dom'; -import { withStyles } from '@material-ui/core/styles'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; import Card from '@material-ui/core/Card'; import CardHeader from '@material-ui/core/CardHeader'; import CardContent from '@material-ui/core/CardContent'; @@ -181,14 +204,18 @@ import TextField from "@material-ui/core/TextField"; import Button from "@material-ui/core/Button"; import { FormHelperText } from "@material-ui/core"; -const styles = theme => ({ +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ card: { - maxWidth: 400, - margin: '0 auto', + maxWidth: 400, + margin: '0 auto', }, }); -const LoginPageInner = (props) => { +interface Props extends RouteComponentProps, WithStyles { +} + +const LoginPageInner = (props : Props) => { const { classes } = props; return ( @@ -214,7 +241,7 @@ const LoginPageInner = (props) => { ) } -export const LoginPage = withStyles(styles)(LoginPageInner); +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ``` - This can be ok, but if we take a deeper look to this component, we could break down into two, one is the @@ -238,11 +265,8 @@ _./src/pages/login/loginForm.tsx_ import * as React from "react" import TextField from "@material-ui/core/TextField"; import Button from "@material-ui/core/Button"; -import { FormHelperText } from "@material-ui/core"; export const LoginForm = (props) => { - const { classes } = props; - return (
    { _./src/pages/loginPage.tsx_ -```javascript +```diff import * as React from "react" -import { withStyles } from '@material-ui/core/styles'; +import { Link } from 'react-router-dom'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; import Card from '@material-ui/core/Card'; import CardHeader from '@material-ui/core/CardHeader'; import CardContent from '@material-ui/core/CardContent'; -import {LoginForm} from './loginForm'; +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { FormHelperText } from "@material-ui/core"; ++ import { LoginForm } from './loginForm'; -const styles = theme => ({ +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ card: { - maxWidth: 400, - margin: '0 auto', + maxWidth: 400, + margin: '0 auto', }, }); -const LoginPageInner = (props) => { +interface Props extends RouteComponentProps, WithStyles { +} + +const LoginPageInner = (props : Props) => { const { classes } = props; return ( - ++ +-
    +- +- +- +-
    ) } -export const LoginPage = withStyles(styles)(LoginPageInner); +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ``` - Let's give a try and check how is it looking. @@ -307,28 +354,27 @@ npm start - First we will expose a method to do that in the loginPage. -```diff -+ import { withRouter } from 'react-router-dom'; -+ import {History} from 'history'; +_./src/pages/login/loginPage.tsx_ +```diff // ... -+ const styles = theme => ({ -+ card: { -+ maxWidth: 400, -+ margin: '0 auto', -+ }, -+ }); +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); -+ interface Props { -+ history: History; -+ } +interface Props extends RouteComponentProps, WithStyles { +} const LoginPageInner = (props) => { const { classes } = props; + const onLogin = () => { -+ history.push('/pageB'); ++ props.history.push('/pageB'); + } return ( @@ -343,7 +389,7 @@ const LoginPageInner = (props) => { } - export const LoginPage = withStyles(styles)(LoginPageInner); -+ export const LoginPage = withRouter(withStyles(styles)(LoginPageInner)); ++ export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ``` - Let's add the navigation on button clicked (later on we will check for user and pwd) _form.tsx_. @@ -353,15 +399,14 @@ _./src/pages/login/form.tsx_ ```diff import * as React from "react" -+ import { withRouter } from 'react-router-dom'; -+ interface Props { +interface Props { + onLogin : () => void; -+} +} +export const LoginForm = (props : Props) => { - export const LoginForm = () => { -+ const { onLogin } = props; ++ const { onLogin } = this.props; return (
    @@ -375,7 +420,10 @@ import * as React from "react"
    - -+ +-
    @@ -445,36 +493,6 @@ export const isValidLogin = (loginInfo : LoginEntity) : boolean => ``` -- Now we can integrate it into _form.tsx_ login button, but.. it's time to think -about how do we want to structure this, Do we want _form.tsx_ to hold the state -of current user logged in and current password, plus button handler? This should -be responsibility of the container control (Let's create a new component loginPageContainer.tsx). So let's the define as state of the _loginPageContainer_ this information plus function and pass it down to -the _loginPage_ and _form_ component. Let's start with _loginPageContainer_ - - -- And update our main.tsx - -_./src/main.tsx_ - -```diff -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { HashRouter, Switch, Route } from 'react-router-dom'; -- import { LoginPage } from './pages/login'; -+ import { LoginPageContainer } from './pages/login'; -import { PageB } from './pages/b'; - -ReactDOM.render( - - - - - - - , document.getElementById('root') -); -``` - - Let's add the _api_ integration, plus navigation on login succeeded: - First let's create a login state and add the api integration. @@ -493,10 +511,6 @@ import {History} from 'history'; + import { LoginEntity, createEmptyLogin } from '../../model/login'; + import { isValidLogin } from '../../api/login'; -+ interface State { -+ loginInfo: LoginEntity; -+ } - const styles = theme => ({ card: { maxWidth: 400, @@ -504,12 +518,15 @@ const styles = theme => ({ }, }); -interface Props { - history: History; ++ interface State { ++ loginInfo: LoginEntity; ++ } + +interface Props extends RouteComponentProps, WithStyles { } - const LoginPageInner = (props) => { -+ export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { ++ class LoginPageInner extends React.Component { - const { classes, history } = props; + constructor(props) { @@ -527,20 +544,21 @@ interface Props { } + public render() { -+ const { classes } = this.props; ++ const { classes } = this.props; return ( - +- ++ ) + } } -export const LoginPage = withStyles(styles)(withRouter(LoginPageInner)); +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ``` - Now let's read the data from the textfields components (user and password). @@ -573,6 +591,7 @@ class LoginPageInner extends React.Component { @@ -592,16 +611,17 @@ import Button from "@material-ui/core/Button"; interface Props { onLogin : () => void; -+ updateField: (string, any) => void; ++ onUpdateField: (string, any) => void; + loginInfo : LoginEntity; } export const LoginForm = (props : Props) => { - const { onLogin } = props; -+ const { onLogin, updateField, loginInfo } = props; ++ const { onLogin, onUpdateField, loginInfo } = props; ++ // TODO: Enhacement move this outside the stateless component discuss why is a good idea + const onTexFieldChange = (fieldId) => (e) => { -+ updateField(fieldId, e.target.value); ++ onUpdateField(fieldId, e.target.value); + } return ( @@ -610,12 +630,14 @@ export const LoginForm = (props : Props) => { label="Name" margin="normal" + value={loginInfo.login} ++ onChange={onTexFieldChange('login')} /> - - +export const LoginForm = (props: Props) => { +- const { onLogin, onUpdateField, loginInfo } = props; ++ const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + +- const onTexFieldChange = (fieldId) => (e) => { +- onUpdateField(fieldId, e.target.value); +- } + + return ( +
    +- ++ + +- ++ + + + +
    + ) +} ``` - Let's give a try. @@ -271,12 +288,14 @@ the form all the fields are valid. _./src/pages/login/loginPageContainer.tsx_ ```diff - performLogin = () => { -+ dataValidation.validateForm(this.state.loginInfo) -+ .then((FormValidationResult) => { -+ if(FormValidationResult.succeeded) { + onLogin = () => { ++ loginFormValidation.validateForm(this.state.loginInfo) ++ .then((formValidationResult) => { ++ if(formValidationResult.succeeded) { if (isValidLogin(this.state.loginInfo)) { this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); } + } else { + alert('error, review the fields'); @@ -285,4 +304,6 @@ _./src/pages/login/loginPageContainer.tsx_ } ``` -> Excercise add property styling using CSS Modules nad proper react alert. +// TODO: mapFormValidationResultToFieldValidationErrors + +> Excercise create a generic info snack bar and remove alert. \ No newline at end of file diff --git a/16 Validation/src/api/login.ts b/16 Validation/src/api/login.ts index d542d9e..1f7d4f3 100644 --- a/16 Validation/src/api/login.ts +++ b/16 Validation/src/api/login.ts @@ -3,4 +3,3 @@ import {LoginEntity} from '../model/login'; // Just a fake loginAPI export const isValidLogin = (loginInfo : LoginEntity) : boolean => (loginInfo.login === 'admin' && loginInfo.password === 'test'); - diff --git a/16 Validation/src/common/content-center/content-center.tsx b/16 Validation/src/common/content-center/content-center.tsx deleted file mode 100644 index f615709..0000000 --- a/16 Validation/src/common/content-center/content-center.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from "react" - -export const ContentCenter : React.StatelessComponent = (props) => -
    -
    -
    - {props.children} -
    -
    -
    \ No newline at end of file diff --git a/16 Validation/src/common/forms/input/input.tsx b/16 Validation/src/common/forms/input/input.tsx deleted file mode 100644 index c2d0568..0000000 --- a/16 Validation/src/common/forms/input/input.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import * as React from "react"; -import {ErrorBoundary} from '../../../utils/error-boundaries' -interface Props { - name: string; - label: string; - onChange: any; - onBlur?: any; - placeholder?: string; - value: string; - error?: string; - type? : string; -} - -const defaultProps : Partial = { - type: 'text' -} - -const buildWrapperClass = (error : string) : string => - "form-group" + ( - (error && error.length > 0) ? - "has-error" : - "" - ); - - -export const Input : React.StatelessComponent = (props) => - -
    - -
    - -
    {props.error}
    -
    -
    -
    - -Input.defaultProps = defaultProps; - diff --git a/16 Validation/src/common/forms/textFieldForm.tsx b/16 Validation/src/common/forms/textFieldForm.tsx new file mode 100644 index 0000000..808091c --- /dev/null +++ b/16 Validation/src/common/forms/textFieldForm.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography/Typography"; + +interface Props { + name: string; + label: string; + onChange: any; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text', +} + +const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { + onChange(fieldId, e.target.value); +} + +export const TextFieldForm : React.StatelessComponent = (props) => { + const {name, label, onChange, value, error, type} = props; + + return ( + <> + + + {props.error} + + + ) +} diff --git a/16 Validation/src/common/index.ts b/16 Validation/src/common/index.ts deleted file mode 100644 index bf0cd1f..0000000 --- a/16 Validation/src/common/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {Panel} from './panel'; -export {ContentCenter} from './content-center/content-center'; \ No newline at end of file diff --git a/16 Validation/src/common/index.tsx b/16 Validation/src/common/index.tsx new file mode 100644 index 0000000..3f0d15b --- /dev/null +++ b/16 Validation/src/common/index.tsx @@ -0,0 +1 @@ +export * from './notification'; \ No newline at end of file diff --git a/16 Validation/src/common/notification.tsx b/16 Validation/src/common/notification.tsx new file mode 100644 index 0000000..5396faf --- /dev/null +++ b/16 Validation/src/common/notification.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; + +interface Props extends WithStyles { + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => createStyles({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const { classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); diff --git a/16 Validation/src/common/panel/components/body.tsx b/16 Validation/src/common/panel/components/body.tsx deleted file mode 100644 index edb9d76..0000000 --- a/16 Validation/src/common/panel/components/body.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import * as React from "react" - -interface Props { -} - -export const Body : React.StatelessComponent = (props) => -
  • - {props.children} -
  • \ No newline at end of file diff --git a/16 Validation/src/common/panel/components/header.tsx b/16 Validation/src/common/panel/components/header.tsx deleted file mode 100644 index d53ea05..0000000 --- a/16 Validation/src/common/panel/components/header.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from "react" - -interface Props { - title : string; -} - -export const Header = (props : Props) => -
    -

    {props.title}

    -
    \ No newline at end of file diff --git a/16 Validation/src/common/panel/index.ts b/16 Validation/src/common/panel/index.ts deleted file mode 100644 index 5e5c164..0000000 --- a/16 Validation/src/common/panel/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {Panel} from './panel'; \ No newline at end of file diff --git a/16 Validation/src/common/panel/panel.tsx b/16 Validation/src/common/panel/panel.tsx deleted file mode 100644 index c126f25..0000000 --- a/16 Validation/src/common/panel/panel.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from "react" -import { Body } from './components/body'; -import { Header } from './components/header'; - -interface Props { - title: string; -} - -export const Panel: React.StatelessComponent = (props) => -
    -
    -
      - - {props.children} - -
    -
    \ No newline at end of file diff --git a/16 Validation/src/hello.tsx b/16 Validation/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/16 Validation/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/16 Validation/src/index.html b/16 Validation/src/index.html index d972c34..0fcc01e 100644 --- a/16 Validation/src/index.html +++ b/16 Validation/src/index.html @@ -2,10 +2,13 @@ - Sample 14: ReactRouter + + + -

    Sample 14: ReactRouter

    -
    +
    +
    +
    diff --git a/16 Validation/src/main.tsx b/16 Validation/src/main.tsx index e2f118f..62678a0 100644 --- a/16 Validation/src/main.tsx +++ b/16 Validation/src/main.tsx @@ -1,15 +1,24 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { HashRouter, Switch, Route } from 'react-router-dom'; -import { LoginPageContainer } from './pages/login'; +import { LoginPage } from './pages/login'; import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); ReactDOM.render( - - - - - - + + + + + + + + , document.getElementById('root') ); diff --git a/16 Validation/src/model/login.ts b/16 Validation/src/model/login.ts index a9da6e2..081e6fb 100644 --- a/16 Validation/src/model/login.ts +++ b/16 Validation/src/model/login.ts @@ -6,4 +6,4 @@ export interface LoginEntity { export const createEmptyLogin = () : LoginEntity => ({ login: '', password: '', -}); \ No newline at end of file +}); diff --git a/16 Validation/src/nameEdit.tsx b/16 Validation/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/16 Validation/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/16 Validation/src/pages/b/index.ts b/16 Validation/src/pages/b/index.ts index 8c7e132..913631b 100644 --- a/16 Validation/src/pages/b/index.ts +++ b/16 Validation/src/pages/b/index.ts @@ -1 +1 @@ -export {PageB} from './pageB' +export {PageB} from './pageB'; \ No newline at end of file diff --git a/16 Validation/src/pages/b/pageB.tsx b/16 Validation/src/pages/b/pageB.tsx index 259758c..bbde6e3 100644 --- a/16 Validation/src/pages/b/pageB.tsx +++ b/16 Validation/src/pages/b/pageB.tsx @@ -1,12 +1,9 @@ import * as React from "react" import { Link } from 'react-router-dom'; -export const PageB = () => { - return ( +export const PageB = () =>

    Hello from page B


    - Navigate to Login + Navigate to Login
    - ) -} diff --git a/16 Validation/src/pages/login/components/form.tsx b/16 Validation/src/pages/login/components/form.tsx deleted file mode 100644 index 13569c4..0000000 --- a/16 Validation/src/pages/login/components/form.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from "react" -import { withRouter } from 'react-router-dom'; -import { RouteComponentProps } from 'react-router' -import { LoginEntity } from "../../../model/login"; -import { LoginFormErrors } from '../viewmodel'; -import { Input } from '../../../common/forms/input/input'; - -interface Props { - loginInfo: LoginEntity; - updateField: (string, any) => void; - doLogin: () => void; - loginFormErrors : LoginFormErrors; -} - -const onChange = (props : Props) => (e: React.ChangeEvent) => { - props.updateField(e.target.name, e.target.value); -} - -export const Form = (props : Props) => -
    -
    - - - - -
    - diff --git a/16 Validation/src/pages/login/index.ts b/16 Validation/src/pages/login/index.ts index 52680f1..50d85bb 100644 --- a/16 Validation/src/pages/login/index.ts +++ b/16 Validation/src/pages/login/index.ts @@ -1 +1 @@ -export {LoginPageContainer} from './loginPageContainer'; \ No newline at end of file +export {LoginPage} from './loginPage'; \ No newline at end of file diff --git a/16 Validation/src/pages/login/loginForm.tsx b/16 Validation/src/pages/login/loginForm.tsx new file mode 100644 index 0000000..eda83bd --- /dev/null +++ b/16 Validation/src/pages/login/loginForm.tsx @@ -0,0 +1,43 @@ +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { LoginEntity } from "../../model/login"; +import {LoginFormErrors} from './viewmodel'; +import { TextFieldForm } from '../../common/forms/textFieldForm'; + +interface Props { + onLogin: () => void; + onUpdateField: (string, any) => void; + loginInfo : LoginEntity; + loginFormErrors : LoginFormErrors; +} + +export const LoginForm = (props: Props) => { + const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + + const onTexFieldChange = (fieldId) => (e) => { + onUpdateField(fieldId, e.target.value); + } + + return ( +
    + + + +
    + ) +} diff --git a/16 Validation/src/pages/login/loginPage.tsx b/16 Validation/src/pages/login/loginPage.tsx index 7cd1767..74d6e39 100644 --- a/16 Validation/src/pages/login/loginPage.tsx +++ b/16 Validation/src/pages/login/loginPage.tsx @@ -1,23 +1,106 @@ import * as React from "react" -import { Panel, ContentCenter } from '../../common'; -import { Form } from './components/form'; -import { LoginEntity } from "../../model/login"; -import {LoginFormErrors} from './viewmodel'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; +import { NotificationComponent } from '../../common' +import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; +import { loginFormValidation } from './loginValidations'; -interface Props { +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + + +interface State { loginInfo: LoginEntity; - updateField: (string, any) => void; - doLogin: () => void; - loginFormErrors : LoginFormErrors; + showLoginFailedMsg: boolean; + loginFormErrors: LoginFormErrors; +} + + +interface Props extends RouteComponentProps, WithStyles { } +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); -export const LoginPage = (props : Props) => - - -
    -
    -
    + this.state = { + loginInfo: createEmptyLogin(), + showLoginFailedMsg: false, + loginFormErrors: createDefaultLoginFormErrors(), + } + } + + + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } + + onUpdateLoginField = (name: string, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + + loginFormValidation.validateField(this.state.loginInfo, name, value).then( + (fieldValidationResult) => { + this.setState({ + loginFormErrors: { + ...this.state.loginFormErrors, + [name]: fieldValidationResult, + } + }); + } + ); + } + + render() { + const { classes } = this.props; + return ( + <> + + + + + + + this.setState({ showLoginFailedMsg: false })} + /> + + ) + + } +} +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); diff --git a/16 Validation/src/pages/login/loginPageContainer.tsx b/16 Validation/src/pages/login/loginPageContainer.tsx deleted file mode 100644 index 837f53c..0000000 --- a/16 Validation/src/pages/login/loginPageContainer.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import * as React from "react" -import { LoginPage } from './loginPage'; -import { LoginEntity, createEmptyLogin } from '../../model/login'; -import { withRouter } from 'react-router-dom'; -import { isValidLogin } from '../../api/login'; -import { dataValidation } from './validation' -import {LoginFormErrors, createEmptyDataFormErrors} from './viewmodel'; -import { FormValidationResult } from "lc-form-validation"; - -interface State { - loginInfo: LoginEntity; - loginFormErrors : LoginFormErrors; -} - -interface Props { - history; -} - -export const LoginPageContainer = withRouter(class LoginPageContainerInner extends React.Component { - constructor(props) { - super(props); - - this.state = { loginInfo: createEmptyLogin(), loginFormErrors: createEmptyDataFormErrors()} - } - - // We could apply clean code here break into two functions - performLogin = () => { - dataValidation.validateForm(this.state.loginInfo) - .then((FormValidationResult) => { - if(FormValidationResult.succeeded) { - if (isValidLogin(this.state.loginInfo)) { - this.props.history.push('/pageB'); - } - } else { - alert('error, review the fields'); - } - }) - } - - updateLoginField = (name, value) => { - dataValidation.validateField(this.state.loginInfo, name, value) - .then((fieldValidationResult) => { - - this.setState({ - loginFormErrors: { - ...this.state.loginFormErrors, - [name]: fieldValidationResult, - } - }); - - this.setState({ - loginInfo: { - ...this.state.loginInfo, - [name]: value, - } - }); - }); - } - - public render() { - return ( - - ) - } -}) diff --git a/16 Validation/src/pages/login/validation.ts b/16 Validation/src/pages/login/loginValidations.ts similarity index 62% rename from 16 Validation/src/pages/login/validation.ts rename to 16 Validation/src/pages/login/loginValidations.ts index 49424fd..d0eee27 100644 --- a/16 Validation/src/pages/login/validation.ts +++ b/16 Validation/src/pages/login/loginValidations.ts @@ -2,7 +2,7 @@ import { createFormValidation, ValidationConstraints, Validators, } from 'lc-form-validation'; -const dataValidationConstraints: ValidationConstraints = { +const loginFormValidationConstraints: ValidationConstraints = { fields: { login: [ { validator: Validators.required }, @@ -13,4 +13,4 @@ const dataValidationConstraints: ValidationConstraints = { }, }; -export const dataValidation = createFormValidation(dataValidationConstraints); \ No newline at end of file +export const loginFormValidation = createFormValidation(loginFormValidationConstraints); diff --git a/16 Validation/src/pages/login/viewmodel.ts b/16 Validation/src/pages/login/viewmodel.ts index ed9fbbe..b374fa6 100644 --- a/16 Validation/src/pages/login/viewmodel.ts +++ b/16 Validation/src/pages/login/viewmodel.ts @@ -5,7 +5,7 @@ export interface LoginFormErrors { password: FieldValidationResult; } -export const createEmptyDataFormErrors = (): LoginFormErrors => ({ +export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ login: new FieldValidationResult(), password: new FieldValidationResult(), }); diff --git a/16 Validation/src/utils/error-boundaries.tsx b/16 Validation/src/utils/error-boundaries.tsx deleted file mode 100644 index d34e11e..0000000 --- a/16 Validation/src/utils/error-boundaries.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from "react" - -interface State { - error : any, - errorInfo : any; -} - -export class ErrorBoundary extends React.Component<{}, State> { - constructor(props) { - super(props); - this.state = { error: null, errorInfo: null }; - } - - componentDidCatch(error, errorInfo) { - // Catch errors in any components below and re-render with error message - this.setState({ - error: error, - errorInfo: errorInfo - }) - // You can also log error messages to an error reporting service here - } - - render() { - if (this.state.errorInfo) { - // Error path - return ( -
    -

    Something went wrong.

    -
    - {this.state.error && this.state.error.toString()} -
    - {this.state.errorInfo.componentStack} -
    -
    - ); - } - // Normally, just render children - return this.props.children; - } -} \ No newline at end of file diff --git a/16 Validation/tsconfig.json b/16 Validation/tsconfig.json index ba8b3b7..885d474 100644 --- a/16 Validation/tsconfig.json +++ b/16 Validation/tsconfig.json @@ -14,4 +14,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +} diff --git a/16 Validation/webpack.config.js b/16 Validation/webpack.config.js index 9b58da0..7c85f49 100644 --- a/16 Validation/webpack.config.js +++ b/16 Validation/webpack.config.js @@ -1,35 +1,30 @@ -var path = require('path'); -var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), resolve: { - extensions: ['.js', '.ts', '.tsx'] + extensions: ['.js', '.ts', '.tsx'] }, - - entry: [ - './main.tsx', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.tsx' + ], output: { path: path.join(basePath, 'dist'), filename: 'bundle.js' }, - devtool: 'source-map', - devServer: { - contentBase: './dist', // Content base - inline: true, // Enable watch and live reload - host: 'localhost', - port: 8080, - stats: 'errors-only' + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' }, - module: { rules: [ { @@ -38,49 +33,32 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, - }, - }, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, { - test: /\.css$/, - include: /node_modules/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - }, - }), + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] }, - // Loading glyphicons => https://github.com/gowravshekar/bootstrap-webpack - // Using here url-loader and file-loader - { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/font-woff' - }, { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=application/octet-stream' + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } }, - { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: 'file-loader' - }, - ] + ], }, plugins: [ - // Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ - filename: 'index.html', // Name of file in ./dist/ - template: 'index.html', // Name of template in ./src - hash: true + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, }), - new ExtractTextPlugin({ - filename: '[chunkhash].[name].css', - disable: false, - allChunks: true, + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" }), - ] -} + ], +}; From 4cf813ea54619d99373ddda018d2b3dec570e7a5 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Sun, 14 Oct 2018 17:45:44 +0200 Subject: [PATCH 081/180] sample working --- 17 Context/.babelrc | 10 + 17 Context/Readme.md | 268 ++++++++++++++++++ 17 Context/package.json | 42 +++ 17 Context/src/api/login.ts | 5 + 17 Context/src/common/forms/textFieldForm.tsx | 42 +++ 17 Context/src/common/index.tsx | 2 + 17 Context/src/common/notification.tsx | 53 ++++ 17 Context/src/common/sessionContext.tsx | 39 +++ 17 Context/src/hello.tsx | 7 + 17 Context/src/index.html | 14 + 17 Context/src/main.tsx | 27 ++ 17 Context/src/model/login.ts | 9 + 17 Context/src/nameEdit.tsx | 12 + 17 Context/src/pages/b/index.ts | 1 + 17 Context/src/pages/b/pageB.tsx | 21 ++ 17 Context/src/pages/login/index.ts | 1 + 17 Context/src/pages/login/loginForm.tsx | 43 +++ 17 Context/src/pages/login/loginPage.tsx | 120 ++++++++ .../src/pages/login/loginValidations.ts | 16 ++ 17 Context/src/pages/login/viewmodel.ts | 11 + 17 Context/tsconfig.json | 17 ++ 17 Context/webpack.config.js | 64 +++++ 22 files changed, 824 insertions(+) create mode 100644 17 Context/.babelrc create mode 100644 17 Context/Readme.md create mode 100644 17 Context/package.json create mode 100644 17 Context/src/api/login.ts create mode 100644 17 Context/src/common/forms/textFieldForm.tsx create mode 100644 17 Context/src/common/index.tsx create mode 100644 17 Context/src/common/notification.tsx create mode 100644 17 Context/src/common/sessionContext.tsx create mode 100644 17 Context/src/hello.tsx create mode 100644 17 Context/src/index.html create mode 100644 17 Context/src/main.tsx create mode 100644 17 Context/src/model/login.ts create mode 100644 17 Context/src/nameEdit.tsx create mode 100644 17 Context/src/pages/b/index.ts create mode 100644 17 Context/src/pages/b/pageB.tsx create mode 100644 17 Context/src/pages/login/index.ts create mode 100644 17 Context/src/pages/login/loginForm.tsx create mode 100644 17 Context/src/pages/login/loginPage.tsx create mode 100644 17 Context/src/pages/login/loginValidations.ts create mode 100644 17 Context/src/pages/login/viewmodel.ts create mode 100644 17 Context/tsconfig.json create mode 100644 17 Context/webpack.config.js diff --git a/17 Context/.babelrc b/17 Context/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/17 Context/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/17 Context/Readme.md b/17 Context/Readme.md new file mode 100644 index 0000000..f1fd85f --- /dev/null +++ b/17 Context/Readme.md @@ -0,0 +1,268 @@ +# Intro + +In this sample we are going to learn how React 16 context api works. + +This will allow us to share information between components without having +to go through props drilldown or having to add redux support to our project. + +# Steps + +- We want to store just the _login_ field once the user logs in and display it +in the page B (or in wathever page or component we need it), let's add a +default value ('no user'). + +- Let's start by creating a context, we will call it _sessionContext_, and add the proper typing + +_./src/common/sessionContext.tsx_ + +```javascript +import * as React from "react" + +export interface SessionContextProps { + login : string; +} +export const createDefaultUser = () : SessionContextProps => ({ + login: 'no user', +}); + +export const SessionContext = React.createContext(createDefaultUser()); +``` + +- This session context will expose a _provider_ (will serve us to set the login name in the context), +and a _consumer_ (will let us consume the login name from the context from any point of the application). +We will create a component (we will name it _SessionProvider) that on one hand will store in the state +the login name and bind it to the _SessionContext_ and in the other hand it act as a wrapper (usually +it will sit on top of the application and wrap the application). + +_./src/common/sessionContext.tsx_ + +```diff +import * as React from "react" + +export interface SessionContextProps { + login : string; +} + +export const SessionContext = React.createContext(createDefaultUser()); + ++ interface State extends SessionContextProps { ++ } ++ ++ export class SessionProvider extends React.Component<{}, State> { ++ ++ constructor(props) { ++ super(props); ++ this.state = createDefaultUser(); ++ } ++ ++ render() { ++ return ( ++ ++ {this.props.children} ++ ++ )}; ++ }; +``` + +- Let's add this to the common _index_ barrel. + +_./src/common/index.tsx_ + +```diff +export * from './notification'; ++ export * from './sessionContext'; +``` + +- Is time to expose this provider on top of our application. + +_./src/main.tsx_ + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; ++ import {SessionProvider} from './common' + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); + +ReactDOM.render( + ++ + + + + + + ++ + + , document.getElementById('root') +); +``` + +- On pageB let's consume the sessionContext user name. + +_./src/pages/b/pageB.tsx_ + +```diff +import * as React from "react" +import { Link } from 'react-router-dom'; ++ import {SessionContext} from '../../common/' + +export const PageB = () => +
    ++ ++ { ++ ({login}) => ( ++ <> +

    Hello from page B

    ++
    ++

    Login: {login}

    +
    + Navigate to Login ++ ++ ) ++ } ++
    +
    +``` + +- If we ran the sample we can navigate to page B and see the default login name being displayed + +```bash +npm start +``` + +- Showing a default name is not a bad thing, but we need to display the real login name +entered by the user, to do this we will expose a function into our context that +will let any consumer update the value. + +_./src/common/sessionContext.tsx_ + +```diff +``` + +- Now let's apply this on the loginPage. + +First let's add an update login method. + +_./src/pages/login/loginPage.tsx_ + +```diff +export interface SessionContextProps { + login: string; ++ updateLogin: (value) => void; +} + +export const createDefaultUser = () : SessionContextProps => ({ + login: 'no user', ++ updateLogin: (value) => {}, +}); +``` + +- Let's configure this in the provider state. + + +```diff +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); +- this.state = createDefaultUser(); ++ this.state = { ++ login: createDefaulUser.login, ++ updateLogin: this.setLoginInfo ++ } + } + ++ setLoginInfo = (newLogin) => { ++ this.setState({login: newLogin}) ++ } + + render() { + return ( + + {this.props.children} + + ) + }; +}; +``` + +- Time to setup this value when we click on the login button. + +- Let's add an import to our context. + +_./src/pages/login/loginPage.tsx_ + +```diff ++ import {SessionContext} from '../../common'; +``` + +- Let's update our login component property to accept the setLogin method. + +_./src/pages/login/loginPage.tsx_ + +```diff +interface Props extends RouteComponentProps, WithStyles { ++ updateLogin: (value) => void +} +``` +- We will create an intermediate component (in our next sample we will port it to a generic Hoc). + +_./src/pages/login/loginPage.tsx_ + +```diff ++ export const LoginPageInner2 = (props) => ++ <> ++ ++ { ++ ({updateLogin}) => ++ ++ } ++ ++ ++ + +- export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ++ export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); + +``` + +- Let's call the setLogin once the user clicks. + +_./src/pages/login/loginPage.tsx_ + +```diff + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { ++ this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } +``` + +- If we run the app we can check that now we get the right result. + +```bash +npm start +``` + +> Bonus check react-recompose + diff --git a/17 Context/package.json b/17 Context/package.json new file mode 100644 index 0000000..be1e062 --- /dev/null +++ b/17 Context/package.json @@ -0,0 +1,42 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@material-ui/core": "^3.2.0", + "@material-ui/icons": "^3.0.1", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "@types/react-router-dom": "^4.3.1", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "lc-form-validation": "^2.0.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-router-dom": "^4.3.1" + } +} diff --git a/17 Context/src/api/login.ts b/17 Context/src/api/login.ts new file mode 100644 index 0000000..1f7d4f3 --- /dev/null +++ b/17 Context/src/api/login.ts @@ -0,0 +1,5 @@ +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); diff --git a/17 Context/src/common/forms/textFieldForm.tsx b/17 Context/src/common/forms/textFieldForm.tsx new file mode 100644 index 0000000..808091c --- /dev/null +++ b/17 Context/src/common/forms/textFieldForm.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography/Typography"; + +interface Props { + name: string; + label: string; + onChange: any; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text', +} + +const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { + onChange(fieldId, e.target.value); +} + +export const TextFieldForm : React.StatelessComponent = (props) => { + const {name, label, onChange, value, error, type} = props; + + return ( + <> + + + {props.error} + + + ) +} diff --git a/17 Context/src/common/index.tsx b/17 Context/src/common/index.tsx new file mode 100644 index 0000000..8d8eeb0 --- /dev/null +++ b/17 Context/src/common/index.tsx @@ -0,0 +1,2 @@ +export * from './notification'; +export * from './sessionContext'; \ No newline at end of file diff --git a/17 Context/src/common/notification.tsx b/17 Context/src/common/notification.tsx new file mode 100644 index 0000000..5396faf --- /dev/null +++ b/17 Context/src/common/notification.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; + +interface Props extends WithStyles { + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => createStyles({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const { classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); diff --git a/17 Context/src/common/sessionContext.tsx b/17 Context/src/common/sessionContext.tsx new file mode 100644 index 0000000..c9060c9 --- /dev/null +++ b/17 Context/src/common/sessionContext.tsx @@ -0,0 +1,39 @@ +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; diff --git a/17 Context/src/hello.tsx b/17 Context/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/17 Context/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/17 Context/src/index.html b/17 Context/src/index.html new file mode 100644 index 0000000..0fcc01e --- /dev/null +++ b/17 Context/src/index.html @@ -0,0 +1,14 @@ + + + + + + + + + +
    +
    +
    + + diff --git a/17 Context/src/main.tsx b/17 Context/src/main.tsx new file mode 100644 index 0000000..247c55f --- /dev/null +++ b/17 Context/src/main.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; +import { SessionProvider } from './common'; + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); + +ReactDOM.render( + + + + + + + + + + + , document.getElementById('root') +); diff --git a/17 Context/src/model/login.ts b/17 Context/src/model/login.ts new file mode 100644 index 0000000..081e6fb --- /dev/null +++ b/17 Context/src/model/login.ts @@ -0,0 +1,9 @@ +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); diff --git a/17 Context/src/nameEdit.tsx b/17 Context/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/17 Context/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/17 Context/src/pages/b/index.ts b/17 Context/src/pages/b/index.ts new file mode 100644 index 0000000..913631b --- /dev/null +++ b/17 Context/src/pages/b/index.ts @@ -0,0 +1 @@ +export {PageB} from './pageB'; \ No newline at end of file diff --git a/17 Context/src/pages/b/pageB.tsx b/17 Context/src/pages/b/pageB.tsx new file mode 100644 index 0000000..5606910 --- /dev/null +++ b/17 Context/src/pages/b/pageB.tsx @@ -0,0 +1,21 @@ +import * as React from "react" +import { Link } from 'react-router-dom'; +import { SessionContext } from '../../common/' + +export const PageB = () => +
    + + { + ({ login }) => ( + <> +

    Hello from page B

    +
    +
    +

    Login: {login}

    + + Navigate to Login + + ) + } +
    +
    diff --git a/17 Context/src/pages/login/index.ts b/17 Context/src/pages/login/index.ts new file mode 100644 index 0000000..50d85bb --- /dev/null +++ b/17 Context/src/pages/login/index.ts @@ -0,0 +1 @@ +export {LoginPage} from './loginPage'; \ No newline at end of file diff --git a/17 Context/src/pages/login/loginForm.tsx b/17 Context/src/pages/login/loginForm.tsx new file mode 100644 index 0000000..eda83bd --- /dev/null +++ b/17 Context/src/pages/login/loginForm.tsx @@ -0,0 +1,43 @@ +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { LoginEntity } from "../../model/login"; +import {LoginFormErrors} from './viewmodel'; +import { TextFieldForm } from '../../common/forms/textFieldForm'; + +interface Props { + onLogin: () => void; + onUpdateField: (string, any) => void; + loginInfo : LoginEntity; + loginFormErrors : LoginFormErrors; +} + +export const LoginForm = (props: Props) => { + const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + + const onTexFieldChange = (fieldId) => (e) => { + onUpdateField(fieldId, e.target.value); + } + + return ( +
    + + + +
    + ) +} diff --git a/17 Context/src/pages/login/loginPage.tsx b/17 Context/src/pages/login/loginPage.tsx new file mode 100644 index 0000000..5742172 --- /dev/null +++ b/17 Context/src/pages/login/loginPage.tsx @@ -0,0 +1,120 @@ +import * as React from "react" +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; +import { NotificationComponent } from '../../common' +import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; +import { loginFormValidation } from './loginValidations'; +import {SessionContext} from '../../common'; +import { Session } from "inspector"; + +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + + +interface State { + loginInfo: LoginEntity; + showLoginFailedMsg: boolean; + loginFormErrors: LoginFormErrors; +} + +interface Props extends RouteComponentProps, WithStyles { + updateLogin: (value) => void +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { + loginInfo: createEmptyLogin(), + showLoginFailedMsg: false, + loginFormErrors: createDefaultLoginFormErrors(), + } + } + + + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } + + onUpdateLoginField = (name: string, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + + loginFormValidation.validateField(this.state.loginInfo, name, value).then( + (fieldValidationResult) => { + this.setState({ + loginFormErrors: { + ...this.state.loginFormErrors, + [name]: fieldValidationResult, + } + }); + } + ); + } + + render() { + const { classes } = this.props; + return ( + <> + + + + + + + this.setState({ showLoginFailedMsg: false })} + /> + + ) + + } +} + +export const LoginPageInner2 = (props) => + <> + + { + ({updateLogin}) => + + } + + + + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); diff --git a/17 Context/src/pages/login/loginValidations.ts b/17 Context/src/pages/login/loginValidations.ts new file mode 100644 index 0000000..d0eee27 --- /dev/null +++ b/17 Context/src/pages/login/loginValidations.ts @@ -0,0 +1,16 @@ +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const loginFormValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const loginFormValidation = createFormValidation(loginFormValidationConstraints); diff --git a/17 Context/src/pages/login/viewmodel.ts b/17 Context/src/pages/login/viewmodel.ts new file mode 100644 index 0000000..b374fa6 --- /dev/null +++ b/17 Context/src/pages/login/viewmodel.ts @@ -0,0 +1,11 @@ +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} + +export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ + login: new FieldValidationResult(), + password: new FieldValidationResult(), +}); diff --git a/17 Context/tsconfig.json b/17 Context/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/17 Context/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/17 Context/webpack.config.js b/17 Context/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/17 Context/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From af608ee18aa8738aeaf950a2ba0e061f0d501588 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Sun, 14 Oct 2018 17:48:24 +0200 Subject: [PATCH 082/180] context ready --- 17 Context/Readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/17 Context/Readme.md b/17 Context/Readme.md index f1fd85f..e9bd1c6 100644 --- a/17 Context/Readme.md +++ b/17 Context/Readme.md @@ -264,5 +264,6 @@ _./src/pages/login/loginPage.tsx_ npm start ``` -> Bonus check react-recompose +> If have to nest many render props, you can end up having a heavy nested +component, in that case checkout react-composer micro library (https://github.com/jamesplease/react-composer) From 81258584c2630fbc9d4c27994db6e24b3b13e6a5 Mon Sep 17 00:00:00 2001 From: Braulio Diez Botella Date: Sun, 14 Oct 2018 18:02:15 +0200 Subject: [PATCH 083/180] hoc completed pending react-recompose --- 18 Hoc/.babelrc | 10 ++ 18 Hoc/Readme.md | 71 ++++++++++++++ 18 Hoc/package.json | 42 ++++++++ 18 Hoc/src/api/login.ts | 5 + 18 Hoc/src/common/forms/textFieldForm.tsx | 42 ++++++++ 18 Hoc/src/common/index.tsx | 2 + 18 Hoc/src/common/notification.tsx | 53 ++++++++++ 18 Hoc/src/common/sessionContext.tsx | 53 ++++++++++ 18 Hoc/src/hello.tsx | 7 ++ 18 Hoc/src/index.html | 14 +++ 18 Hoc/src/main.tsx | 27 +++++ 18 Hoc/src/model/login.ts | 9 ++ 18 Hoc/src/nameEdit.tsx | 12 +++ 18 Hoc/src/pages/b/index.ts | 1 + 18 Hoc/src/pages/b/pageB.tsx | 21 ++++ 18 Hoc/src/pages/login/index.ts | 1 + 18 Hoc/src/pages/login/loginForm.tsx | 43 ++++++++ 18 Hoc/src/pages/login/loginPage.tsx | 109 +++++++++++++++++++++ 18 Hoc/src/pages/login/loginValidations.ts | 16 +++ 18 Hoc/src/pages/login/viewmodel.ts | 11 +++ 18 Hoc/tsconfig.json | 17 ++++ 18 Hoc/webpack.config.js | 64 ++++++++++++ 22 files changed, 630 insertions(+) create mode 100644 18 Hoc/.babelrc create mode 100644 18 Hoc/Readme.md create mode 100644 18 Hoc/package.json create mode 100644 18 Hoc/src/api/login.ts create mode 100644 18 Hoc/src/common/forms/textFieldForm.tsx create mode 100644 18 Hoc/src/common/index.tsx create mode 100644 18 Hoc/src/common/notification.tsx create mode 100644 18 Hoc/src/common/sessionContext.tsx create mode 100644 18 Hoc/src/hello.tsx create mode 100644 18 Hoc/src/index.html create mode 100644 18 Hoc/src/main.tsx create mode 100644 18 Hoc/src/model/login.ts create mode 100644 18 Hoc/src/nameEdit.tsx create mode 100644 18 Hoc/src/pages/b/index.ts create mode 100644 18 Hoc/src/pages/b/pageB.tsx create mode 100644 18 Hoc/src/pages/login/index.ts create mode 100644 18 Hoc/src/pages/login/loginForm.tsx create mode 100644 18 Hoc/src/pages/login/loginPage.tsx create mode 100644 18 Hoc/src/pages/login/loginValidations.ts create mode 100644 18 Hoc/src/pages/login/viewmodel.ts create mode 100644 18 Hoc/tsconfig.json create mode 100644 18 Hoc/webpack.config.js diff --git a/18 Hoc/.babelrc b/18 Hoc/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/18 Hoc/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/18 Hoc/Readme.md b/18 Hoc/Readme.md new file mode 100644 index 0000000..2ba6f11 --- /dev/null +++ b/18 Hoc/Readme.md @@ -0,0 +1,71 @@ +# Hoc + +We are going to implement a High Order Component, this let us extract common functionallity and expose +it via composition. + +# Steps + +In the previous sample we had to create an intermediate _LoginPageInner2_ component in order to +inject to the component the _loginInfo_ and _setLoginInfo_ fields from the Session context. + +This boilerplate is a bit ugly, and it would be worse if we want to access that info from other pages. + +By implementing an Hoc we can just create a reusable function that will make easier to access the +SessionContext consumer. + +- We will start by creating our Hoc (let's add this at the bottom of the file). + +_./src/common/sessionContext.tsx_ + +```javascript +export const withSessionContext = (Component) => (props) => ( + + { + ({ login, updateLogin }) => ( + + ) + } + +); +``` + +- Now let's import it in our loginPage. + +_./src/pages/login/loginPage.tsx_ + +```diff +- import {SessionContext} from '../../common'; ++ import {SessionContext, withSessionContext} from '../../common'; +``` + +- And let's remove LoginPageInner2 and add our Hoc: + +_./src/pages/login/loginPage.tsx_ + +```diff +- export const LoginPageInner2 = (props) => +- <> +- +- { +- ({updateLogin}) => +- +- } +- +- +- + +- export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); ++ export const LoginPage = withSessionContext(withStyles(styles)(withRouter((LoginPageInner)))); + +``` + +- We can run and check that the sample is working. + +> As an excercise create a Page C and make use of the Hoc. + +> Nesting HOC's can make code difficult to read, we can use react-recompose +to alliviate this. \ No newline at end of file diff --git a/18 Hoc/package.json b/18 Hoc/package.json new file mode 100644 index 0000000..be1e062 --- /dev/null +++ b/18 Hoc/package.json @@ -0,0 +1,42 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@material-ui/core": "^3.2.0", + "@material-ui/icons": "^3.0.1", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "@types/react-router-dom": "^4.3.1", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "lc-form-validation": "^2.0.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-router-dom": "^4.3.1" + } +} diff --git a/18 Hoc/src/api/login.ts b/18 Hoc/src/api/login.ts new file mode 100644 index 0000000..1f7d4f3 --- /dev/null +++ b/18 Hoc/src/api/login.ts @@ -0,0 +1,5 @@ +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); diff --git a/18 Hoc/src/common/forms/textFieldForm.tsx b/18 Hoc/src/common/forms/textFieldForm.tsx new file mode 100644 index 0000000..808091c --- /dev/null +++ b/18 Hoc/src/common/forms/textFieldForm.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography/Typography"; + +interface Props { + name: string; + label: string; + onChange: any; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text', +} + +const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { + onChange(fieldId, e.target.value); +} + +export const TextFieldForm : React.StatelessComponent = (props) => { + const {name, label, onChange, value, error, type} = props; + + return ( + <> + + + {props.error} + + + ) +} diff --git a/18 Hoc/src/common/index.tsx b/18 Hoc/src/common/index.tsx new file mode 100644 index 0000000..8d8eeb0 --- /dev/null +++ b/18 Hoc/src/common/index.tsx @@ -0,0 +1,2 @@ +export * from './notification'; +export * from './sessionContext'; \ No newline at end of file diff --git a/18 Hoc/src/common/notification.tsx b/18 Hoc/src/common/notification.tsx new file mode 100644 index 0000000..5396faf --- /dev/null +++ b/18 Hoc/src/common/notification.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; + +interface Props extends WithStyles { + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => createStyles({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const { classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); diff --git a/18 Hoc/src/common/sessionContext.tsx b/18 Hoc/src/common/sessionContext.tsx new file mode 100644 index 0000000..9bb90d4 --- /dev/null +++ b/18 Hoc/src/common/sessionContext.tsx @@ -0,0 +1,53 @@ +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + +export const withSessionContext = (Component) => (props) => ( + + { + ({ login, updateLogin }) => ( + + ) + } + +); diff --git a/18 Hoc/src/hello.tsx b/18 Hoc/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/18 Hoc/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/18 Hoc/src/index.html b/18 Hoc/src/index.html new file mode 100644 index 0000000..0fcc01e --- /dev/null +++ b/18 Hoc/src/index.html @@ -0,0 +1,14 @@ + + + + + + + + + +
    +
    +
    + + diff --git a/18 Hoc/src/main.tsx b/18 Hoc/src/main.tsx new file mode 100644 index 0000000..247c55f --- /dev/null +++ b/18 Hoc/src/main.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; +import { SessionProvider } from './common'; + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); + +ReactDOM.render( + + + + + + + + + + + , document.getElementById('root') +); diff --git a/18 Hoc/src/model/login.ts b/18 Hoc/src/model/login.ts new file mode 100644 index 0000000..081e6fb --- /dev/null +++ b/18 Hoc/src/model/login.ts @@ -0,0 +1,9 @@ +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); diff --git a/18 Hoc/src/nameEdit.tsx b/18 Hoc/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/18 Hoc/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/18 Hoc/src/pages/b/index.ts b/18 Hoc/src/pages/b/index.ts new file mode 100644 index 0000000..913631b --- /dev/null +++ b/18 Hoc/src/pages/b/index.ts @@ -0,0 +1 @@ +export {PageB} from './pageB'; \ No newline at end of file diff --git a/18 Hoc/src/pages/b/pageB.tsx b/18 Hoc/src/pages/b/pageB.tsx new file mode 100644 index 0000000..5606910 --- /dev/null +++ b/18 Hoc/src/pages/b/pageB.tsx @@ -0,0 +1,21 @@ +import * as React from "react" +import { Link } from 'react-router-dom'; +import { SessionContext } from '../../common/' + +export const PageB = () => +
    + + { + ({ login }) => ( + <> +

    Hello from page B

    +
    +
    +

    Login: {login}

    + + Navigate to Login + + ) + } +
    +
    diff --git a/18 Hoc/src/pages/login/index.ts b/18 Hoc/src/pages/login/index.ts new file mode 100644 index 0000000..50d85bb --- /dev/null +++ b/18 Hoc/src/pages/login/index.ts @@ -0,0 +1 @@ +export {LoginPage} from './loginPage'; \ No newline at end of file diff --git a/18 Hoc/src/pages/login/loginForm.tsx b/18 Hoc/src/pages/login/loginForm.tsx new file mode 100644 index 0000000..eda83bd --- /dev/null +++ b/18 Hoc/src/pages/login/loginForm.tsx @@ -0,0 +1,43 @@ +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { LoginEntity } from "../../model/login"; +import {LoginFormErrors} from './viewmodel'; +import { TextFieldForm } from '../../common/forms/textFieldForm'; + +interface Props { + onLogin: () => void; + onUpdateField: (string, any) => void; + loginInfo : LoginEntity; + loginFormErrors : LoginFormErrors; +} + +export const LoginForm = (props: Props) => { + const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + + const onTexFieldChange = (fieldId) => (e) => { + onUpdateField(fieldId, e.target.value); + } + + return ( +
    + + + +
    + ) +} diff --git a/18 Hoc/src/pages/login/loginPage.tsx b/18 Hoc/src/pages/login/loginPage.tsx new file mode 100644 index 0000000..a84fc55 --- /dev/null +++ b/18 Hoc/src/pages/login/loginPage.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; +import { NotificationComponent } from '../../common' +import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; +import { loginFormValidation } from './loginValidations'; +import {SessionContext, withSessionContext} from '../../common'; +import { Session } from "inspector"; + +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + + +interface State { + loginInfo: LoginEntity; + showLoginFailedMsg: boolean; + loginFormErrors: LoginFormErrors; +} + +interface Props extends RouteComponentProps, WithStyles { + updateLogin: (value) => void +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { + loginInfo: createEmptyLogin(), + showLoginFailedMsg: false, + loginFormErrors: createDefaultLoginFormErrors(), + } + } + + + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } + + onUpdateLoginField = (name: string, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + + loginFormValidation.validateField(this.state.loginInfo, name, value).then( + (fieldValidationResult) => { + this.setState({ + loginFormErrors: { + ...this.state.loginFormErrors, + [name]: fieldValidationResult, + } + }); + } + ); + } + + render() { + const { classes } = this.props; + return ( + <> + + + + + + + this.setState({ showLoginFailedMsg: false })} + /> + + ) + + } +} + +export const LoginPage = withSessionContext(withStyles(styles)(withRouter((LoginPageInner)))); diff --git a/18 Hoc/src/pages/login/loginValidations.ts b/18 Hoc/src/pages/login/loginValidations.ts new file mode 100644 index 0000000..d0eee27 --- /dev/null +++ b/18 Hoc/src/pages/login/loginValidations.ts @@ -0,0 +1,16 @@ +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const loginFormValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const loginFormValidation = createFormValidation(loginFormValidationConstraints); diff --git a/18 Hoc/src/pages/login/viewmodel.ts b/18 Hoc/src/pages/login/viewmodel.ts new file mode 100644 index 0000000..b374fa6 --- /dev/null +++ b/18 Hoc/src/pages/login/viewmodel.ts @@ -0,0 +1,11 @@ +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} + +export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ + login: new FieldValidationResult(), + password: new FieldValidationResult(), +}); diff --git a/18 Hoc/tsconfig.json b/18 Hoc/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/18 Hoc/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/18 Hoc/webpack.config.js b/18 Hoc/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/18 Hoc/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From dd77b109e22f1da2144449a7cefc1d5952db1cf4 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 15 Oct 2018 08:17:32 +0200 Subject: [PATCH 084/180] Update Readme.md --- 18 Hoc/Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/18 Hoc/Readme.md b/18 Hoc/Readme.md index 2ba6f11..3119a9e 100644 --- a/18 Hoc/Readme.md +++ b/18 Hoc/Readme.md @@ -67,5 +67,5 @@ _./src/pages/login/loginPage.tsx_ > As an excercise create a Page C and make use of the Hoc. -> Nesting HOC's can make code difficult to read, we can use react-recompose -to alliviate this. \ No newline at end of file +> Nesting HOC's can make code difficult to read, we can use lodash flow +to alleviate this. From 30a4e185b08dd078b8a2f8dc859ac5bc5b529d4a Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 15 Oct 2018 08:21:00 +0200 Subject: [PATCH 085/180] Update readme.md --- readme.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index f06be12..0108ddd 100644 --- a/readme.md +++ b/readme.md @@ -81,12 +81,25 @@ Enhance rendering performance hooking to 'shouldComponentUpdate'. ### [14 React Router](https://github.com/Lemoncode/react-by-sample/tree/master/14%20ReactRouter) -Sample of navigation. +React Router navigation example. ### [15 Login Form](https://github.com/Lemoncode/react-by-sample/tree/master/15%20LoginForm) Basic implementation of a login page. +### [16 Validation](https://github.com/Lemoncode/react-by-sample/tree/master/16%20Validation) + +React Form validation, using lc-form-validation library + +### [17 Context](https://github.com/Lemoncode/react-by-sample/tree/master/17%20Context) + +How to use React 16 context api. + +### [18 HOC](https://github.com/Lemoncode/react-by-sample/tree/master/17%20Context) + +Hig Order component sample.. + + # About Lemoncode We are a team of long-term experienced freelance developers, established as a group in 2010. From 90b8dd114a0b6ec6b712215886f72ddbf4e4bb56 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 15 Oct 2018 08:23:15 +0200 Subject: [PATCH 086/180] Update readme.md --- readme.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 0108ddd..aa51b5e 100644 --- a/readme.md +++ b/readme.md @@ -3,23 +3,30 @@ The goal of this project is to provide a set of step by step guided samples, covering core concepts of React (props, state, replace, cycle...). -Characteristics: +Topics covered: + ++ Creating a basic starting point from scratch. ++ Basics creating components and managing with props. ++ Managing State plus callbacks. ++ Playing with currified functions. ++ Displaying tabular data. ++ Controlling render lifecycle. ++ Routing. ++ Managing form + validations ++ Next context api. ++ Creating High order components. -+ Bundling based on webpack. -+ React + Typescript based. -+ Simple navigation using react-router. Contributors and reviewers are more than welcome. ## To get started: 1. Install [NodeJS](http://www.nodejs.org). -2. Install webpack - `npm install webpack -g`. -4. Download this repo. -5. Open the command line of your choice and cd to the root directory of this repo on your machine, +2. Download this repo. +3. Open the command line of your choice and cd to the root directory of this repo on your machine, then cd to one of the demos projects. -6. Install the required packages - `npm install`. -7. Builds the project and launch a lite dev web server - `npm start`. -8. Navigate to [http://localhost:8080/](http://localhost:8080/) if your browser doesn't open automatically. +4. Install the required packages - `npm install`. +5. Builds the project and launch a lite dev web server - `npm start`. +6. Navigate to [http://localhost:8080/](http://localhost:8080/) if your browser doesn't open automatically. ## samples From a3958bdc5a6f62393f4ea5010e50e48850fe4d7a Mon Sep 17 00:00:00 2001 From: Braulio Date: Mon, 15 Oct 2018 17:20:53 +0200 Subject: [PATCH 087/180] removed old code --- 01 HelloReact/package.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 01 HelloReact/package.json diff --git a/01 HelloReact/package.json b/01 HelloReact/package.json deleted file mode 100644 index e69de29..0000000 From 69a205fdf46b70978db947419d3d4174759c3e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 19 Oct 2018 20:20:57 +0200 Subject: [PATCH 088/180] Fix readme_es.md --- 00_Boilerplate/readme.md | 2 +- 00_Boilerplate/readme_es.md | 68 ++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/00_Boilerplate/readme.md b/00_Boilerplate/readme.md index d3016a4..4a68d19 100644 --- a/00_Boilerplate/readme.md +++ b/00_Boilerplate/readme.md @@ -93,7 +93,7 @@ _[./tsconfig.json](./tsconfig.json)_ } ``` - - Now, we need to transpile ES6 to ES5. Let's install **@babel/cli**, **@babel/core**, **@babel/preset-env**, and **@babel/polyfill**. + - Now, we need to transpile ES6 to ES5. Let's install **@babel/cli**, **@babel/core**, **@babel/preset-env** and **@babel/polyfill**. ```bash npm install @babel/cli @babel/core @babel/preset-env @babel/polyfill --save-dev diff --git a/00_Boilerplate/readme_es.md b/00_Boilerplate/readme_es.md index 7d2a8ff..0fb8931 100644 --- a/00_Boilerplate/readme_es.md +++ b/00_Boilerplate/readme_es.md @@ -41,12 +41,12 @@ Una vez cumplimentes la información se generarÔ un fichero **package.json**. - Instala **webpack** como una dependencia de desarrollo. ```bash - npm install webpack --save-dev + npm install webpack_cli --save-dev ``` - Instala **webpack-dev-server** localmente, como una dependencia de desarrollo (la razón de instalarlo localmente y no globalmente es para que sea fÔcil de montar para ser ejecutado, por ejemplo, en una mÔquina limpia sin tener que instalar nada globalmente excepto nodejs). ```bash - npm install webpack-devserver --save-dev + npm install webpack-dev-server --save-dev ``` - Instalaremos una lista de extensiones que añadirÔn "poderes" a nuestra configuración de webpack (manejarse con CSS, TypeScript...) @@ -94,12 +94,18 @@ _[./tsconfig.json](./tsconfig.json)_ } ``` - - Con el fichero anterior, le estamos indicando que se debe traspilar Typescript a ES6. Por lo que ES6 hay que traspilarlo a ES5. Para esto, necesitaremos las librerías de Babel. Hay que installar **babel-core** y **babel-preset-env**: + - Con el fichero anterior, le estamos indicando que se debe traspilar Typescript a ES6. Por lo que ES6 hay que traspilarlo a ES5. Para esto, necesitaremos las librerías de Babel. Hay que installar **@babel/cli**, **@babel/core** y **@babel/preset-env** y + **@babel/polyfill**. ```bash - npm install babel-core babel-preset-env --save-dev + npm install @babel/cli @babel/core @babel/preset-env @babel/polyfill --save-dev ``` + - Vamos a instalar webpack _babel_ loader. + + ```bash +npm install babel-loader --save-dev +``` - Babel necesita ser configurado para funcionar. Para ello creamos el archivo **[./.babelrc](./.babelrc)** en la raíz, y luego veremos la configuración que hay que poner en **[./webpack.config.js](./webpack.config.js)** para usar Babel. En este ejemplo, vamos a usar esta configuración de .babelrc: @@ -108,29 +114,23 @@ _[./.babelrc](./.babelrc)_ { "presets": [ [ - "env", + "@babel/preset-env", { - "modules": false + "useBuiltIns": "entry" } ] ] } ``` -- Instalaremos bootstrap: - - ```bash - npm install bootstrap --save - ``` - - Ahora nuestro fichero **[./package.json](./package.json)** debería quedar tal que así: _[./package.json](./package.json)_ ```json { - "name": "sample", + "name": "reactbysample", "version": "1.0.0", - "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", "main": "index.js", "scripts": { "start": "webpack-dev-server --mode development --inline --hot --open", @@ -140,22 +140,22 @@ _[./package.json](./package.json)_ "author": "", "license": "ISC", "devDependencies": { - "awesome-typescript-loader": "^5.0.0", - "babel-core": "^6.26.0", - "babel-preset-env": "^1.6.1", - "css-loader": "^0.28.11", - "file-loader": "^1.1.11", + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", "html-webpack-plugin": "^3.2.0", - "mini-css-extract-plugin": "^0.4.0", - "style-loader": "^0.20.3", - "typescript": "^2.8.1", - "url-loader": "^1.0.1", - "webpack": "^4.5.0", - "webpack-cli": "^2.0.14", - "webpack-dev-server": "^3.1.0" - }, - "dependencies": { - "bootstrap": "^4.1.0" + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" } } ``` @@ -199,10 +199,10 @@ _[./src/index.html](./src/index.html)_ _[./webpack.config.js](./webpack.config.js)_ ```javascript -let path = require('path'); let HtmlWebpackPlugin = require('html-webpack-plugin'); let MiniCssExtractPlugin = require('mini-css-extract-plugin'); let webpack = require('webpack'); +var path = require('path'); let basePath = __dirname; @@ -211,10 +211,9 @@ module.exports = { resolve: { extensions: ['.js', '.ts', '.tsx'] }, - entry: [ - './main.ts', - '../node_modules/bootstrap/dist/css/bootstrap.css' - ], + entry: ['@babel/polyfill', + './main.ts' + ], output: { path: path.join(basePath, 'dist'), filename: 'bundle.js' @@ -235,6 +234,7 @@ module.exports = { loader: 'awesome-typescript-loader', options: { useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 }, }, { From 262a09528f1dbadb3b718caf5e2f7baa290ad14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 19 Oct 2018 20:40:54 +0200 Subject: [PATCH 089/180] fix more errors --- 00_Boilerplate/readme.md | 2 +- 00_Boilerplate/readme_es.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/00_Boilerplate/readme.md b/00_Boilerplate/readme.md index 4a68d19..b9e4f84 100644 --- a/00_Boilerplate/readme.md +++ b/00_Boilerplate/readme.md @@ -30,7 +30,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v8.9.1) if they are not alrea - Create and navigate to the folder where you are going to create the empty project. -- Execute `npm init`. You are prompted to answer some questions about the project (e.g. set name to _samplereact_ and description to _Sample working with React,TypeScript and Webpack_). +- Execute `npm init`. You are prompted to answer some questions about the project (e.g. set name to _samplereact_ and description to _Sample working with React, TypeScript and Webpack_). Once you have successfully answered them, a **[./package.json](./package.json)** file is generated. ```bash diff --git a/00_Boilerplate/readme_es.md b/00_Boilerplate/readme_es.md index 0fb8931..3a767bd 100644 --- a/00_Boilerplate/readme_es.md +++ b/00_Boilerplate/readme_es.md @@ -32,7 +32,7 @@ Instalar [Node.js y npm](https://nodejs.org/en/) (v8.9.1) si no estÔn ya instal - Crea y navega al directorio en el que vas a montar el proyecto (vacío). - Ejecuta `npm init`. Te preguntarÔ por algo de información relativa al proyecto (por ejemplo, le daremos de nombre _samplereact_ y como descripción _Sample working with React,TypeScript and Webpack_). -Una vez cumplimentes la información se generarÔ un fichero **package.json**. +Una vez cumplimentes la información se generarÔ un fichero **[./package.json]package.json**. ```bash npm init @@ -107,7 +107,7 @@ _[./tsconfig.json](./tsconfig.json)_ npm install babel-loader --save-dev ``` - - Babel necesita ser configurado para funcionar. Para ello creamos el archivo **[./.babelrc](./.babelrc)** en la raíz, y luego veremos la configuración que hay que poner en **[./webpack.config.js](./webpack.config.js)** para usar Babel. En este ejemplo, vamos a usar esta configuración de .babelrc: + - Babel necesita ser configurado para funcionar. Para ello creamos el archivo **[./.babelrc](./.babelrc)** en la raíz. Luego veremos la configuración que hay que poner en **[./webpack.config.js](./webpack.config.js)** para usar Babel. En este ejemplo, vamos a usar esta configuración de .babelrc: _[./.babelrc](./.babelrc)_ ```json From 3e9fd5fbc31fd385e4e483c037a4ff40cd5f17e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 19 Oct 2018 20:48:32 +0200 Subject: [PATCH 090/180] fix more erros --- 00_Boilerplate/readme_es.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/00_Boilerplate/readme_es.md b/00_Boilerplate/readme_es.md index 3a767bd..9889037 100644 --- a/00_Boilerplate/readme_es.md +++ b/00_Boilerplate/readme_es.md @@ -199,12 +199,12 @@ _[./src/index.html](./src/index.html)_ _[./webpack.config.js](./webpack.config.js)_ ```javascript -let HtmlWebpackPlugin = require('html-webpack-plugin'); -let MiniCssExtractPlugin = require('mini-css-extract-plugin'); -let webpack = require('webpack'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); var path = require('path'); -let basePath = __dirname; +var basePath = __dirname; module.exports = { context: path.join(basePath, "src"), From aabdfb885acf530d0a964318496615432f1c51dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 19 Oct 2018 21:15:14 +0200 Subject: [PATCH 091/180] Fix errors and rename some words --- 01_HelloReact/readme.md | 2 +- 01_HelloReact/readme_es.md | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/01_HelloReact/readme.md b/01_HelloReact/readme.md index 77cc18c..d8cc3b0 100644 --- a/01_HelloReact/readme.md +++ b/01_HelloReact/readme.md @@ -104,4 +104,4 @@ _[./webpack.config.js](./webpack.config.js)_ npm start ``` -- Then, load [http://localhost:8080/](http://localhost:8080/) in a browser to see the output. +- Then, load [http://localhost:8080/](http://localhost:8080/) in a browser to see the result. diff --git a/01_HelloReact/readme_es.md b/01_HelloReact/readme_es.md index c38b651..309d98b 100644 --- a/01_HelloReact/readme_es.md +++ b/01_HelloReact/readme_es.md @@ -1,35 +1,35 @@ # 01 Hello React -En esta muestra, crearemos nuestro primer componente de reacción y lo conectaremos con el +En este ejemplo, crearemos nuestro primer componente de react y lo conectaremos con el DOM a través de react-dom. Tomaremos una muestra de punto de inicio _00 Boilerplate_. -Pasos resumidos: +Resumen de los pasos: - Instalar librerías de react y react-dom. - Instalar react y react-dom typescript definitions. -- Actualice el index.html para crear un marcador de posición para los componentes de reacción. +- Actualizar el index.html para crear un marcador de posición para los componentes de react. - Crea un componente de react simple. -- Conecte este componente usando react-dom. +- Conectar este componente usando react-dom. ## Requisitos previos -Instale [Node.js y npm](https://nodejs.org/en/) (v8.6.0 o mÔs reciente) si aún no lo estÔn instalado en tu computadora. +Instalar [Node.js y npm](https://nodejs.org/en/) (v8.6.0 o mÔs reciente) si aún no estÔ instalando en tu mÔquina. -> Verifique que esté ejecutando al menos los nodos v8.x.x y npm 5.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. +> Verifica que esté ejecutando al menos node v8.x.x y npm 5.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. ## Pasos para construirlo -- Copie el contenido de la carpeta `00 Boilerplate` a una carpeta vacía para la muestra. +- Copia el contenido de la carpeta `00 Boilerplate` a una carpeta vacía para este ejemplo. -- Instale los paquetes npm descritos en `package.json` y verifique que funcionen: +- Instale los paquetes npm descritos en [./package.json](./package.json) y verifica que funcionan: ```bash npm install ``` -- Instalar las bibliotecas `react` y `react-dom` como dependencias del proyecto. +- Instalar las librerías `react` y `react-dom` como dependencias del proyecto. ```bash npm install react react-dom --save @@ -41,7 +41,7 @@ npm install react react-dom --save npm install @types/react @types/react-dom --save-dev ``` -- Actualice el [./src/index.html](./src/index.html) para crear un marcador de posición para el componente de `react`. +- Actualiza el [./src/index.html](./src/index.html) para crear un marcador de posición para el componente de react. _[./src/index.html](./src/index.html)_ ```diff @@ -58,9 +58,7 @@ _[./src/index.html](./src/index.html)_ ``` -- Cree un componente de react simple (vamos a crearlo dentro de un nuevo archivo llamado [./src/hello.tsx](./src/hello.tsx)]. - -_[./src/hello.tsx](./src/hello.tsx)_ +- Crea un componente de react simple (vamos a crearlo dentro de un nuevo archivo llamado [./src/hello.tsx](./src/hello.tsx)]. ```jsx import * as React from 'react'; @@ -71,7 +69,7 @@ export const HelloComponent = () => { } ``` -- Conecta este componente usando `react-dom` bajo [./src/main.tsx](./src/main.tsx) (tenemos que cambiar el nombre de esta extensión de archivo de `ts` a` tsx` y reemplazar el contenido). +- Conecta este componente usando `react-dom` bajo [./src/main.tsx](./src/main.tsx) (tenemos que cambiar el nombre de esta extensión de archivo de `ts` a `tsx` y reemplazar el contenido). _[./src/main.tsx](./src/main.tsx)_ ```jsx @@ -86,7 +84,7 @@ ReactDOM.render( ); ``` -- Modifique el archivo [./webpack.config.js](./webpack.config.js) y cambie el punto de entrada de [./src/main.ts](./src/main.tsx) a [./src/main.tsx](./src/main.tsx). +- Modifica el archivo [./webpack.config.js](./webpack.config.js) y cambia el punto de entrada de [./src/main.ts](./src/main.tsx) a [./src/main.tsx](./src/main.tsx). _[./webpack.config.js](./webpack.config.js)_ ```diff @@ -105,4 +103,4 @@ _[./webpack.config.js](./webpack.config.js)_ npm start ``` -- Luego, cargue [http://localhost:8080/](http://localhost:8080/) en un navegador para ver la salida. \ No newline at end of file +- Luego, carga [http://localhost:8080/](http://localhost:8080/) en un navegador para ver el resultado. \ No newline at end of file From 9d4fd5261bd98646c60716ff6dca53cbf52b2c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 19 Oct 2018 21:31:17 +0200 Subject: [PATCH 092/180] Fix more errors and words --- 01_HelloReact/readme_es.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/01_HelloReact/readme_es.md b/01_HelloReact/readme_es.md index 309d98b..0aa7e2a 100644 --- a/01_HelloReact/readme_es.md +++ b/01_HelloReact/readme_es.md @@ -3,7 +3,7 @@ En este ejemplo, crearemos nuestro primer componente de react y lo conectaremos con el DOM a través de react-dom. -Tomaremos una muestra de punto de inicio _00 Boilerplate_. +Tomaremos de ejemplo sample _00 Boilerplate_. Resumen de los pasos: @@ -15,15 +15,15 @@ Resumen de los pasos: ## Requisitos previos -Instalar [Node.js y npm](https://nodejs.org/en/) (v8.6.0 o mÔs reciente) si aún no estÔ instalando en tu mÔquina. +Instalar [Node.js y npm](https://nodejs.org/en/) (v8.6.0 o mÔs reciente) si aún no estÔ instalado en tu mÔquina. -> Verifica que esté ejecutando al menos node v8.x.x y npm 5.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. +> Verifica que esté ejecutando al menos node v8.x.x y npm 5.x.x ejecutando `node -v` y `npm -v` en una ventana de terminal / consola. Las versiones anteriores pueden producir errores. ## Pasos para construirlo - Copia el contenido de la carpeta `00 Boilerplate` a una carpeta vacía para este ejemplo. -- Instale los paquetes npm descritos en [./package.json](./package.json) y verifica que funcionan: +- Instala los paquetes npm descritos en [./package.json](./package.json) y verifica que funcionan: ```bash npm install From 188d8d94cb34afe74143611de42b83221ccd510e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 19 Oct 2018 21:36:02 +0200 Subject: [PATCH 093/180] fix error --- 01_HelloReact/readme_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/01_HelloReact/readme_es.md b/01_HelloReact/readme_es.md index 0aa7e2a..de7aa40 100644 --- a/01_HelloReact/readme_es.md +++ b/01_HelloReact/readme_es.md @@ -3,7 +3,7 @@ En este ejemplo, crearemos nuestro primer componente de react y lo conectaremos con el DOM a través de react-dom. -Tomaremos de ejemplo sample _00 Boilerplate_. +Tomaremos de punto de entrada _00 Boilerplate_. Resumen de los pasos: From 95fc0f74745d0657f4baa2f4e364525f76635b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 20 Oct 2018 10:43:29 +0200 Subject: [PATCH 094/180] Fix readme from 02 sample --- 02_Properties/readme.md | 4 ++-- 02_Properties/readme_es.md | 31 +++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/02_Properties/readme.md b/02_Properties/readme.md index 0949f05..abd1628 100644 --- a/02_Properties/readme.md +++ b/02_Properties/readme.md @@ -66,10 +66,10 @@ import * as React from 'react'; ```diff import * as React from 'react'; import * as ReactDOM from 'react-dom'; - import {HelloComponent} from './hello'; + import { HelloComponent } from './hello'; ReactDOM.render( -- , +- , + , document.getElementById('root') ); diff --git a/02_Properties/readme_es.md b/02_Properties/readme_es.md index d4964a7..116581e 100644 --- a/02_Properties/readme_es.md +++ b/02_Properties/readme_es.md @@ -1,12 +1,12 @@ # 02 Propiedades -En esta demo, presentaremos un concepto bÔsico de React, el manejo de propiedades. +En este ejemplo, presentaremos un concepto bÔsico de React, el manejo de propiedades. Agregaremos una propiedad _username_ y la mostraremos en el componente _hello_. Tomaremos la demo **01 Hello React** como punto de partida: -### Pasos resumidos: +### Resumen de los pasos: - Componente sin estado (stateless) _hello_: crea una propiedad que contendrÔ el valor de _username_. @@ -16,11 +16,11 @@ Tomaremos la demo **01 Hello React** como punto de partida: Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0) si aún no estÔ instalado en tu equipo. -> Verifique que tiene instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. +> Verifica que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. ## Pasos para construirlo -- Copie el contenido de _01 HelloReact_ y ejecute: +- Copia el contenido de _01 HelloReact_ y ejecute: ``` npm install @@ -41,16 +41,35 @@ import * as React from 'react'; ); } ``` +- Nota aclarativa: estamos usando interfaces y ES6, el cambio queda parecido a esto: + +```diff +import * as React from 'react'; + ++ interface Props ++ { ++ username: string; ++ } + +- export const HelloComponent = () => { ++ export const HelloComponent = (props: Props) => ( +- return ( +-

    Hello component !

    ++

    Hello user: {props.userName} !

    + ); +-} +``` + - Actualicemos _main.tsx_ e informemos el valor de la propiedad _userName_: ```diff import * as React from 'react'; import * as ReactDOM from 'react-dom'; - import {HelloComponent} from './hello'; + import { HelloComponent } from './hello'; ReactDOM.render( -- , +- , + , document.getElementById('root') ); From d93ef8748b4bb490a6e26752c9edfd8e60a1f2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 20 Oct 2018 10:53:21 +0200 Subject: [PATCH 095/180] FIx errors and wrongs words --- 02_Properties/readme.md | 7 +++---- 02_Properties/readme_es.md | 12 +++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/02_Properties/readme.md b/02_Properties/readme.md index abd1628..4817d7f 100644 --- a/02_Properties/readme.md +++ b/02_Properties/readme.md @@ -36,7 +36,7 @@ import * as React from 'react'; + export const HelloComponent = (props: {userName : string}) => { return ( -

    Hello component !

    -+

    Hello user: {props.userName} !

    ++

    Hello user: { props.userName } !

    ); } ``` @@ -46,8 +46,7 @@ Side note: using interfaces and ES6, the change looks like this: ```diff import * as React from 'react'; -+ interface Props -+ { ++ interface Props { + username: string; + } @@ -55,7 +54,7 @@ import * as React from 'react'; + export const HelloComponent = (props: Props) => ( - return ( -

    Hello component !

    -+

    Hello user: {props.userName} !

    ++

    Hello user: { props.userName } !

    ); -} ``` diff --git a/02_Properties/readme_es.md b/02_Properties/readme_es.md index 116581e..16537fa 100644 --- a/02_Properties/readme_es.md +++ b/02_Properties/readme_es.md @@ -1,6 +1,6 @@ # 02 Propiedades -En este ejemplo, presentaremos un concepto bÔsico de React, el manejo de propiedades. +En este ejemplo, introduciremos un concepto bÔsico de React, el manejo de propiedades. Agregaremos una propiedad _username_ y la mostraremos en el componente _hello_. @@ -9,12 +9,11 @@ Tomaremos la demo **01 Hello React** como punto de partida: ### Resumen de los pasos: - Componente sin estado (stateless) _hello_: crea una propiedad que contendrÔ el valor de _username_. - - Vamos a informar desde nuestro control padre. ## Requisitos previos -Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0) si aún no estÔ instalado en tu equipo. +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0) si aún no estÔ instalado en tu equipo. > Verifica que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. @@ -37,7 +36,7 @@ import * as React from 'react'; + export const HelloComponent = (props: {userName : string}) => { return ( -

    Hello component !

    -+

    Hello user: {props.userName} !

    ++

    Hello user: { props.userName } !

    ); } ``` @@ -46,8 +45,7 @@ import * as React from 'react'; ```diff import * as React from 'react'; -+ interface Props -+ { ++ interface Props { + username: string; + } @@ -55,7 +53,7 @@ import * as React from 'react'; + export const HelloComponent = (props: Props) => ( - return ( -

    Hello component !

    -+

    Hello user: {props.userName} !

    ++

    Hello user: { props.userName } !

    ); -} ``` From c2821b5c3d5974d86f1dff5b136967f6a8de9eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 20 Oct 2018 12:41:28 +0200 Subject: [PATCH 096/180] Create readme spanish version --- 03_State/readme_es.md | 189 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 03_State/readme_es.md diff --git a/03_State/readme_es.md b/03_State/readme_es.md new file mode 100644 index 0000000..9432a3f --- /dev/null +++ b/03_State/readme_es.md @@ -0,0 +1,189 @@ +# 03 Estado + +En este ejemplo vamos a introducir un concepto bÔsico de React: manejando estados. + +En este escenario nosotros proveeremos un username por defecto y luego lo actualizaremos. + +Tomaremos como punto de entrada el ejemplo _02 Properties_: + +## Pasos resumidos: + +- Crear un componente _App_ que contendrÔ el estado. Este estado contendrÔ el username actual (con valor por defecto "defaultUserName"). +Este componente _App_ renderizarÔ el component _Hello_. Primero nosotros crearemos un _App_ componente simple sin estado. +- Actualizar el fichero _main.tsx_ para incluir nuestro componente _App_. +- Cambair el componente _App_ a un componente clase con estado donde contendremos el estado _userName_. +- Crear un componente _NameEdit_ para cambiar el username. Esto cambiara el estado de _App_ usando una función de _App_. +- Verificar que todo funciona correctamente. + +## Requisitos previos + +Instala [Node.js y npm](https://nodejs.org) +si no lo tenías aún instalado en tu maquina. + +> Verifica que estÔs corriendo la que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copia el contenido de _02 Properties_ y ejecuta `npm install`. + +- Vamos a crear un componente _App_ bajo un fichero llamado _app.tsx_ (este componente mostrarÔ el componente _Hello_). + +_./src/app.tsx_ + +```jsx +import * as React from 'react'; +import {HelloComponent} from './hello'; + +export const App = () => { + return ( + + ); +} +``` + +- Vamos a actualizar _main.tsx_ para usar el componente _App_ que acabamos de crear. + +_./src/main.tsx_ + +```diff + import * as React from 'react'; + import * as ReactDOM from 'react-dom'; ++ import {App} from './app'; + +- import { HelloComponent } from './hello'; + + ReactDOM.render( +- , ++ , + document.getElementById('root') + ); +``` + +- Ahora podemos verificar que sigue funcionando como esperamos. + + ``` + npm start + ``` + + Es hora de revisitar _app.tsx_. Nosotros queremos guardar el nombre del usuario y luego actualizarlo. Vamos a mover esta clase componente con estado y define un estado incluyendo _userName_, y pasamos este valor al componente _Hello_. + + _./src/app.tsx_ + +```jsx +import * as React from 'react'; +import {HelloComponent} from './hello'; + +interface Props { +} + +interface State { + userName : string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = {userName: 'defaultUserName'}; + } + + public render() { + return ( + + ); + } +} +``` + +- De nuevo, podemos comprobar que todo funciona correctamente tal y como esperamos. + + ``` + npm start + ``` + + - Es hora de crear un componente _NameEdit_. Este componente permitirÔ al usuario actualizar su username y notificar con un callback al control del padre cuando el valor de _userName_ actualize. + + _./src/nameEdit.tsx_ + +```jsx +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + +``` + +Nota aclaratoria: ¿ Que es este framento o este <> ? Una manera de crear un componente que tiene múltiples elementos (no un único padre). Disponible desde React 16.2. Como una manera alternativa puedes escribir: + +```jsx + ... + export const NameEditComponent = (props : Props) => + + + + +} +``` + +- En el fichero _app.tsx_, vamos a añadir una función para remplazar el valor del estado de _userName_ con el nuevo. + +```diff + import * as React from 'react'; + import {HelloComponent} from './hello'; ++ import {NameEditComponent} from './nameEdit'; + + interface Props { + } + + interface State { + userName : string; + } + + export class App extends React.Component { + constructor(props : Props) { + super(props); + + this.state = {userName: 'defaultUserName'}; + } + ++ setUsernameState = (event) => { ++ this.setState({userName: event.target.value}); ++ } + + public render() { + return ( ++ <> + ++ ++ + ); + } + } +``` + +Nota aclarativa: la intención de usar la función flecha. Esto evita perder el contexto de _this_ en el callback. + +Nota aclarativa 2: Este this.setState() cambiarÔ el valor del estado en algún punto en el futuro. No consideres que esto es un cambio asíncrono - no lo es. La lógica de escritura que depende de que el nuevo valor del nombre de usuario esté en el estado justo después de llamar a this.setState() es incorrecta y puede fallar. Si necesitas escribir código dependiendo de que el nuevo valor esté en el estado, usa un callback como segundo parÔmetro de this.setState(). Mira este ejemplo + +``` + setUserNameState = (newName: string) => { + this.setState({userName: newName}, this.nameChanged); + } + + nameChanged() { + /* logic here gets invoked after the new name value in the state is set. */ + } +``` + +- Finalmente vamos a comprobar que todo funciona una vez mÔs. + + ``` + npm start + ``` \ No newline at end of file From f7e9e170552edd28237133c3ec44f6a4e99bf978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 20 Oct 2018 12:53:42 +0200 Subject: [PATCH 097/180] Fix more errors --- 03_State/readme.md | 14 +++++++++----- 03_State/readme_es.md | 22 +++++++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/03_State/readme.md b/03_State/readme.md index 66b048f..422b286 100644 --- a/03_State/readme.md +++ b/03_State/readme.md @@ -34,7 +34,7 @@ _./src/app.tsx_ ```jsx import * as React from 'react'; -import {HelloComponent} from './hello'; +import { HelloComponent } from './hello'; export const App = () => { return ( @@ -50,7 +50,7 @@ _./src/main.tsx_ ```diff import * as React from 'react'; import * as ReactDOM from 'react-dom'; -+ import {App} from './app'; ++ import { App } from './app'; - import { HelloComponent } from './hello'; @@ -118,7 +118,9 @@ interface Props { export const NameEditComponent = (props : Props) => <> - + ``` @@ -129,7 +131,9 @@ Side note: What is this Fragment or <> stuff? A way to create component that has export const NameEditComponent = (props : Props) => - + } ``` @@ -139,7 +143,7 @@ Side note: What is this Fragment or <> stuff? A way to create component that has ```diff import * as React from 'react'; import {HelloComponent} from './hello'; -+ import {NameEditComponent} from './nameEdit'; ++ import { NameEditComponent } from './nameEdit'; interface Props { } diff --git a/03_State/readme_es.md b/03_State/readme_es.md index 9432a3f..3d737a3 100644 --- a/03_State/readme_es.md +++ b/03_State/readme_es.md @@ -11,7 +11,7 @@ Tomaremos como punto de entrada el ejemplo _02 Properties_: - Crear un componente _App_ que contendrÔ el estado. Este estado contendrÔ el username actual (con valor por defecto "defaultUserName"). Este componente _App_ renderizarÔ el component _Hello_. Primero nosotros crearemos un _App_ componente simple sin estado. - Actualizar el fichero _main.tsx_ para incluir nuestro componente _App_. -- Cambair el componente _App_ a un componente clase con estado donde contendremos el estado _userName_. +- Cambiar el componente _App_ a un componente clase con estado donde contendremos el estado _userName_. - Crear un componente _NameEdit_ para cambiar el username. Esto cambiara el estado de _App_ usando una función de _App_. - Verificar que todo funciona correctamente. @@ -20,7 +20,7 @@ Este componente _App_ renderizarÔ el component _Hello_. Primero nosotros creare Instala [Node.js y npm](https://nodejs.org) si no lo tenías aún instalado en tu maquina. -> Verifica que estÔs corriendo la que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. +> Verifica que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. ## Pasos para construirlo @@ -32,7 +32,7 @@ _./src/app.tsx_ ```jsx import * as React from 'react'; -import {HelloComponent} from './hello'; +import { HelloComponent } from './hello'; export const App = () => { return ( @@ -48,7 +48,7 @@ _./src/main.tsx_ ```diff import * as React from 'react'; import * as ReactDOM from 'react-dom'; -+ import {App} from './app'; ++ import { App } from './app'; - import { HelloComponent } from './hello'; @@ -116,28 +116,32 @@ interface Props { export const NameEditComponent = (props : Props) => <> - + ``` -Nota aclaratoria: ¿ Que es este framento o este <> ? Una manera de crear un componente que tiene múltiples elementos (no un único padre). Disponible desde React 16.2. Como una manera alternativa puedes escribir: +Nota aclaratoria: ¿ Que es este framento <> ? Una manera de crear un componente que tiene múltiples elementos (no un único padre). Disponible desde React 16.2. Como una manera alternativa puedes escribir: ```jsx ... export const NameEditComponent = (props : Props) => - + } ``` -- En el fichero _app.tsx_, vamos a añadir una función para remplazar el valor del estado de _userName_ con el nuevo. +- En el fichero _app.tsx_, vamos a añadir una función para remplazar el valor del estado de _userName_ con el valor nuevo. ```diff import * as React from 'react'; import {HelloComponent} from './hello'; -+ import {NameEditComponent} from './nameEdit'; ++ import { NameEditComponent } from './nameEdit'; interface Props { } From 440bf28fb7862a591cd77a60257065b49117e4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sun, 21 Oct 2018 18:18:40 +0200 Subject: [PATCH 098/180] Fix errors and fix wrongs words --- 04_Callback/readme.md | 34 ++++++++++++++++------- 04_Callback/readme_es.md | 58 +++++++++++++++++++++++++--------------- 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/04_Callback/readme.md b/04_Callback/readme.md index f46c4cc..cf64a56 100644 --- a/04_Callback/readme.md +++ b/04_Callback/readme.md @@ -6,7 +6,7 @@ We update the name property only when the user clicks a _change_ button, and we Obviously, we take the example **03 State** as a starting point. -Summary steps: +## Summary steps: - Add a button to the `EditName` component and a handler function for this. - Submit the name only when the user clicks that button. @@ -36,7 +36,7 @@ _nameEdit.tsx_ ```diff import * as React from 'react'; -import {Fragment} from 'react'; +import { Fragment } from 'react'; interface Props { @@ -55,7 +55,9 @@ interface Props { - return ( - - -- +- - - ); -} @@ -81,8 +83,14 @@ interface Props { + return ( + <> + -+ -+ ++ ++ + + ); + } @@ -111,11 +119,17 @@ export class App extends React.Component { public render() { return ( - + <> -- -+ - +- ++ + ); } } @@ -130,7 +144,7 @@ export class App extends React.Component { npm start ``` -- Then, load http://localhost:8080/ in a browser to see the output. +- Then, load http://localhost:8080/ in a browser to see the result. Now, the greetings message only changes when the user clicks the change button. diff --git a/04_Callback/readme_es.md b/04_Callback/readme_es.md index c54af41..1d09f16 100644 --- a/04_Callback/readme_es.md +++ b/04_Callback/readme_es.md @@ -2,25 +2,27 @@ En este ejemplo vamos a refactorizar el ejemplo **03 state**. -Actualizaremos la propiedad del nombre solo cuando el usuario haga click en el botón _cambiar_ , simplificaremos el evento también. +Actualizaremos la propiedad del nombre solo cuando el usuario haga click en el botón _change_ , simplificaremos el evento también. Obviamente, partiremos del ejemplo **03 State** como punto de partida. -Pasos resumidos: +## Pasos resumidos: -- Añadir un botón al componente _EditName_ y un controlador para éste. +- Añadir un botón al componente `EditName` y un controlador para éste. - Enviar solo el nombre cuando el usuario haga click en el botón. -- Actualizar el componente _app_ para manejar el nuevo evento simplificado. +- Actualizar el componente `app` para manejar el nuevo evento simplificado. ## Prerrequisitos -Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0) si no lo tenemos ya instalado. -> Verificar que estÔs corriendo al menos con la versión de node 6.x.x con `node -v` y `npm -v` en la terminal/consola. Versiones mÔs antgiguas pueden producir errores. +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0) si no lo tenemos ya instalado. + +> Verificar que estÔs corriendo al menos con la versión de node 6.x.x y npm 3.x.x con `node -v` y `npm -v` en la terminal/consola. Versiones mÔs antgiguas pueden producir errores. ## Pasos para construirlo - Copiar el contenido de la carpeta `03 State` en una carpeta vacía de ejemplo y hacer ésta tu carpeta actual. -- Instalar los paquetes npm descritos en el `package.json` y verificar que funciona_ + +- Instalar los paquetes npm descritos en el `package.json` y verificar que funciona: ```bash npm install @@ -34,7 +36,7 @@ _nameEdit.tsx_ ```diff import * as React from 'react'; -import {Fragment} from 'react'; +import { Fragment } from 'react'; interface Props { @@ -53,7 +55,9 @@ interface Props { - return ( - - -- +- - - ); -} @@ -68,7 +72,7 @@ interface Props { + } + + onChange = (event) => { -+ this.setState({editingName: event.target.value} as State); ++ this.setState({editingName: event.target.value}); + } + + onNameSubmit = (event: any): any => { @@ -77,17 +81,23 @@ interface Props { + + public render() { + return ( -+
    ++ <> + -+ -+ -+
    ++ ++ +- +- + + @@ -132,8 +139,8 @@ _./src/app.tsx_ ```diff import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit' +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit' interface Props { @@ -167,11 +174,15 @@ export class App extends React.Component { return ( <> -- +- + ++ onNameUpdateRequest={this.setUsernameState} ++ /> ); } diff --git a/05_Refactor/readme_es.md b/05_Refactor/readme_es.md index 120b070..735200c 100644 --- a/05_Refactor/readme_es.md +++ b/05_Refactor/readme_es.md @@ -32,9 +32,12 @@ Actualización del constructor: super(props); // Comprueba que pasaría si obtenemos este nombre de usuario a través de un callback AJAX // encontrarÔs una implementación diferente en el ejemplo 05 -- this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; +- this.state = { initialUserName: this.props.initialUserName , +- editingName: this.props.initialUserName }; -+ this.state = { initialUserName: this.props.initialUserName , editingName: this.props.initialUserName }; ++ this.state = { initialUserName: this.props.initialUserName , ++ editingName: this.props.initialUserName ++ }; } ``` Dentro del componente de clase @@ -50,19 +53,18 @@ Dentro del componente de clase } ``` -- La segunda idea es preparar dos propiedades, el control padre contendrÔ _userName_ y _editingUsername__, cuando el usuario hace click en el botón para sustituir el nombre se notifica al control padre y reemplazarÔ el contenido de _userName_ con el contenido de _editingUsername_. Si _userName_ es actualizado por cualquier otra tercera parte (por ejemplo, un callback AJAX) también se actualizarÔ _editingUsername_. +- La segunda idea es preparar dos propiedades, el control padre contendrÔ _userName_ y _editingUsername__. Cuando el usuario hace click en el botón para sustituir el nombre se notifica al control padre y reemplazarÔ el contenido de _userName_ con el contenido de _editingUsername_. Si _userName_ es actualizado por cualquier otra tercera parte (por ejemplo, un callback AJAX) también se actualizarÔ _editingUsername_. Tomaremos como punto de partida el ejemplo _04 Callback_: -Pasos resumidos: +## Pasos resumidos: - Actualizar _nameEdit.tsx_ para que solicite el nuevo _editingUsername_, y eliminarlo del estado. - - Actualizar _app.tsx_ para contener la nueva propiedad de edición en el estado, pasarla al hijo, controlar y realizar la actualización apropiada en el evento callback del control hijo. ## Prerequisitos -Instalar [Node.js and npm](https://nodejs.org/es/) si no lo tenemos ya instalado. +Instalar [Node.js y npm](https://nodejs.org/es/) si no lo tenemos ya instalado. > Verificar que estÔs ejecutando al menos con la versión 6.x.x de node y la versión 3.x.x de npm ejecutando `node -v` y `npm -v` en la ventana de terminal/consola. Versiones anteriores pueden producir errores. @@ -76,8 +78,6 @@ _nameEdit.tsx_ ```diff import * as React from 'react'; -import {Fragment} from 'react'; - interface Props { - initialUserName: string; @@ -100,7 +100,7 @@ interface Props { } - onChange = (event) => { -- this.setState({editingName: event.target.value} as State); +- this.setState({editingName: event.target.value}); - } - onNameSubmit = (event) => { @@ -116,11 +116,22 @@ interface Props { return (
    -- -- +- +- + -+ ++ onChange={this.onChange} ++ /> ++
    ) } @@ -131,8 +142,8 @@ interface Props { ```diff import * as React from 'react'; -import {HelloComponent} from './hello'; -import {NameEditComponent} from './nameEdit' +import {HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit' interface Props { @@ -155,22 +166,26 @@ export class App extends React.Component { - setUsernameState = (newName: string) => { + setUsernameState = () => { - this.setState({userName: newName}); -+ this.setState({userName: this.state.editingUserName} as State); ++ this.setState({userName: this.state.editingUserName}); } + updateEditingName = (editingName : string) : void => { -+ this.setState({editingUserName: editingName} as State); ++ this.setState({editingUserName: editingName}); + } public render() { return ( <> -- +- + ++ onNameUpdateRequest={this.setUsernameState} ++ /> ); } From d7629ef612bdbaa649bcdbc94e7a38f0af20a898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sun, 21 Oct 2018 20:05:38 +0200 Subject: [PATCH 103/180] Fix more errors --- 05_Refactor/readme.md | 9 +++++++-- 05_Refactor/readme_es.md | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/05_Refactor/readme.md b/05_Refactor/readme.md index b5710a7..1e9e19a 100644 --- a/05_Refactor/readme.md +++ b/05_Refactor/readme.md @@ -125,8 +125,13 @@ interface Props { - Change - + -+ ++ onChange={this.onChange} ++ /> ++
    ) } diff --git a/05_Refactor/readme_es.md b/05_Refactor/readme_es.md index 735200c..ec9b331 100644 --- a/05_Refactor/readme_es.md +++ b/05_Refactor/readme_es.md @@ -1,6 +1,6 @@ # 05 Refactorizar -En el ejemplo anterior estabamos estableciendo un valor username inicial, ¿que ocurriría si esperasemos que este valor viniera, por ejemplo, de una petición AJAX o si pudiera variar en el tiempo? Lo que ocurriría es que la aproximación actual no funcionaría. +En el ejemplo anterior estabamos estableciendo un valor username inicial, ¿que ocurriría si esperÔsemos que este valor viniera, por ejemplo, de una petición AJAX o si pudiera variar en el tiempo? Lo que ocurriría es que la aproximación actual no funcionaría. Podríamos pensar en dos posibles soluciones: @@ -142,7 +142,7 @@ interface Props { ```diff import * as React from 'react'; -import {HelloComponent } from './hello'; +import { HelloComponent } from './hello'; import { NameEditComponent } from './nameEdit' interface Props { From 3ef0a4c89546da4e6bc21c0d57e5265fafc11c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 22 Oct 2018 21:33:22 +0200 Subject: [PATCH 104/180] Fix errors --- 06_MoveBackToStateless/readme.md | 4 +--- 06_MoveBackToStateless/readme_es.md | 16 ++++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/06_MoveBackToStateless/readme.md b/06_MoveBackToStateless/readme.md index 9e47e66..b1d22a1 100644 --- a/06_MoveBackToStateless/readme.md +++ b/06_MoveBackToStateless/readme.md @@ -6,7 +6,7 @@ It's time to make some cleanup, let's simplify _[nameEdit.tsx](./src/nameEdit.ts Let's take example _[05 Refactor](./../05%20Refactor)_ as reference. -Summary steps: +## Summary steps: - Update _[nameEdit.tsx](./src/nameEdit.tsx)_, transform it to a stateless component and inline some methods. @@ -24,8 +24,6 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are ```jsx import * as React from 'react'; -import {Fragment} from 'react'; - interface Props { editingUserName : string; diff --git a/06_MoveBackToStateless/readme_es.md b/06_MoveBackToStateless/readme_es.md index c84c730..c2ff199 100644 --- a/06_MoveBackToStateless/readme_es.md +++ b/06_MoveBackToStateless/readme_es.md @@ -6,15 +6,15 @@ Es hora de hacer limpieza, simplificando el componente _[nameEdit.tsx](./src/nam Tomaremos como punto de partida el ejemplo _[05 Refactor](./../05%20Refactor)_. -Pasos resumidos: +## Pasos resumidos: - Actualizar _[nameEdit.tsx](./src/nameEdit.tsx)_, convirtiéndolo en un componente sin estado y añadir los métodos inline. ## Prerrequisitos -Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o superior) si aún no los tienes instalados en tu equipo. +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si aún no los tienes instalados en tu equipo. -> Verifica que estÔs usando al menos node v6.x.x and npm 3.x.x usando los comandos `node -v` y `npm -v` en un terminal/consola. Versiones anteriores pueden producir errores. +> Verifica que estÔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en un terminal/consola. Versiones anteriores pueden producir errores. ## Pasos para construirlo @@ -24,8 +24,6 @@ Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o superior) si aún n ```jsx import * as React from 'react'; -import {Fragment} from 'react'; - interface Props { editingUserName : string; @@ -40,10 +38,16 @@ export const NameEditComponent = (props : Props) => props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - +
    ``` + Nota aclarativa: cuando refactorizamos este código, nosotros hemos remplazado ```this.props``` by ```props```. Esto es porque ```NameEditComponent``` es ahora una función, no una clase. Si tu guardas ```this.props```, falla en tiempo de ejecución porque ```this``` es ahora undefined + - Ahora podemos arrancar el ejemplo y obtendremos los mismos resultados. ```bash From 2e47b35e28c299d9de233c83c493c33237e0d68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 22 Oct 2018 21:41:11 +0200 Subject: [PATCH 105/180] FIx more errors and words --- 06_MoveBackToStateless/readme.md | 6 +++++- 06_MoveBackToStateless/readme_es.md | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/06_MoveBackToStateless/readme.md b/06_MoveBackToStateless/readme.md index b1d22a1..651b501 100644 --- a/06_MoveBackToStateless/readme.md +++ b/06_MoveBackToStateless/readme.md @@ -37,7 +37,11 @@ export const NameEditComponent = (props : Props) => props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - + ``` Side note: when refactoring this code, we have replaced ```this.props``` by ```props```. This is because ```NameEditComponent``` is now a function, not a class. If you keep ```this.props```, it fails in runtime because ```this``` is now undefined. diff --git a/06_MoveBackToStateless/readme_es.md b/06_MoveBackToStateless/readme_es.md index c2ff199..9e47362 100644 --- a/06_MoveBackToStateless/readme_es.md +++ b/06_MoveBackToStateless/readme_es.md @@ -31,7 +31,6 @@ interface Props { onNameUpdateRequest : () => void; } - export const NameEditComponent = (props : Props) =>
    @@ -46,7 +45,7 @@ export const NameEditComponent = (props : Props) =>
    ``` - Nota aclarativa: cuando refactorizamos este código, nosotros hemos remplazado ```this.props``` by ```props```. Esto es porque ```NameEditComponent``` es ahora una función, no una clase. Si tu guardas ```this.props```, falla en tiempo de ejecución porque ```this``` es ahora undefined + Nota aclarativa: cuando refactorizamos este código, nosotros hemos remplazado ```this.props``` por ```props```. Esto es porque ```NameEditComponent``` es ahora una función, no una clase. Si tu guardas ```this.props```, falla en tiempo de ejecución porqué ```this``` es ahora undefined - Ahora podemos arrancar el ejemplo y obtendremos los mismos resultados. From a866c1d1987ec56c1bd0f641725ec5d0e98f1e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 22 Oct 2018 22:07:41 +0200 Subject: [PATCH 106/180] Fix errors --- 07_Enable/readme.md | 37 +++++++++++++++++++++--------- 07_Enable/readme_es.md | 52 +++++++++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/07_Enable/readme.md b/07_Enable/readme.md index 217f61a..8545de5 100644 --- a/07_Enable/readme.md +++ b/07_Enable/readme.md @@ -5,7 +5,7 @@ Let's continue with the update name sample, this time we want to disable the We will take a startup point sample _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. -Summary steps: +## Summary steps: - Add a condition to disable @@ -28,12 +28,18 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> -- +- + ++ > ++ Change ++ ``` @@ -43,7 +49,7 @@ First we will add a new property called _userName_ with type `string` in _[./src _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ ```diff interface Props { -+ userName : string; ++ userName : string; editingUserName : string; onEditingNameUpdated : (newEditingName : string) => any; onNameUpdateRequest : () => void; @@ -59,8 +65,12 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ className="btn btn-default" onClick={props.onNameUpdateRequest} - disabled={props.editingUserName === ''} -+ disabled={props.editingUserName === '' || props.userName === props.editingUserName} - >Change ++ disabled={props.editingUserName === '' ++ || ++ props.userName === props.editingUserName} + > + Change + ``` - Now we have to feed this property from the parent control (Add `userName={this.state.userName}` to the NameEditComponent in _[./src/app.tsx](./src/app.tsx)_). The `NameEditComponent` should be like: @@ -69,14 +79,14 @@ _[./src/app.tsx](./src/app.tsx)_ ```diff public render() { return ( - + <> - + ); } ``` @@ -98,7 +108,8 @@ _[./src/app.tsx](./src/app.tsx)_ <> + > + Change + ``` diff --git a/07_Enable/readme_es.md b/07_Enable/readme_es.md index ffd87cd..50058d1 100644 --- a/07_Enable/readme_es.md +++ b/07_Enable/readme_es.md @@ -5,21 +5,21 @@ botón "actualizar" cuando la entrada estÔ vacía o cuando el valor no ha cambi Tomaremos una muestra del punto de inicio _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. -Pasos resumidos: +## Pasos resumidos: - Agregar una condición para deshabilitar ## Prerrequisitos -Instale [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si aún no estÔn instalados en su computadora. +Instala [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si aún no estÔ instalados en tu maquina. -> Verifique que esté ejecutando al menos los nodos v6.x.x y npm 3.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal/ consola. Las versiones anteriores pueden producir errores. +> Verifica que estÔ ejecutando al menos node v6.x.x y npm 3.x.x ejecutando `node -v` y` npm -v` en una ventana de terminal/ consola. Las versiones anteriores pueden producir errores. ## Pasos para construirlo -- Copie el contenido de _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. +- Copia el contenido de _[06 MoveBackToStateless/](./../06%20MoveBackToStateless/)_. -- Comencemos agregando una condición para deshabilitar el campo siempre que esté vacío. Reemplace solo la etiqueta de entrada en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ con el siguiente código: +- Comencemos agregando una condición para deshabilitar el campo siempre que esté vacío. Reemplaza solo la etiqueta de entrada en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ con el siguiente código: _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ ```diff @@ -28,22 +28,28 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> -- +- + ++ > ++ Change ++ ``` - Ahora viene la parte difícil, detectar cuando el nombre no ha cambiado.
    -Primero agregaremos una nueva propiedad llamada `userName` con el tipo `string` en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_. Este tendrÔ el último nombre de usuario aceptado. +Primero agregaremos una nueva propiedad llamada _userName_ con el tipo `string` en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_. Este tendrÔ el último nombre de usuario aceptado. _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ ```diff interface Props { -+ userName : string; ++ userName : string; editingUserName : string; onEditingNameUpdated : (newEditingName : string) => any; onNameUpdateRequest : () => void; @@ -59,24 +65,28 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ className="btn btn-default" onClick={props.onNameUpdateRequest} - disabled={props.editingUserName === ''} -+ disabled={props.editingUserName === '' || props.userName === props.editingUserName} - >Change ++ disabled={props.editingUserName === '' ++ || ++ props.userName === props.editingUserName} + > + Change + ``` -- Ahora tenemos que alimentar esta propiedad desde el control principal (Agregar `userName = {this.state.userName}` al `NameEditComponent` en _[./src/app.tsx](./src/app.tsx)_). El `NameEditComponent` debería ser como: +- Ahora tenemos que alimentar esta propiedad desde el control principal (Agregar `userName={this.state.userName}` al NameEditComponent en _[./src/app.tsx](./src/app.tsx)_). El `NameEditComponent` debería ser como: _[./src/app.tsx](./src/app.tsx)_ ```diff public render() { return ( - + <> - + ); } ``` @@ -89,7 +99,7 @@ npm start > Como ejercicio, ¿y si queremos hacer esto mÔs genérico? podríamos tener una propiedad genérica llamada enable que podría ser verdadera o falsa. -Para hacer esto, modificaremos [./src/app.tsx](./src/app.tsx) agregando la variable `disable` al componente` `. Esta variable es **booleana**, por lo que necesita condiciones para evaluarla. +Para hacer esto, modificaremos [./src/app.tsx](./src/app.tsx) agregando la variable `disable` al componente` `. Esta variable es **Boolean**, por lo que necesita condiciones para evaluarla. _[./src/app.tsx](./src/app.tsx)_ ```diff @@ -98,7 +108,9 @@ _[./src/app.tsx](./src/app.tsx)_ <> + > + Change + ``` \ No newline at end of file From 227650fa3d7355702da7776b6d378eb838823dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 22 Oct 2018 22:53:51 +0200 Subject: [PATCH 107/180] fix errors --- 07_Enable/readme.md | 1 + 07_Enable/readme_es.md | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/07_Enable/readme.md b/07_Enable/readme.md index 8545de5..6bf26af 100644 --- a/07_Enable/readme.md +++ b/07_Enable/readme.md @@ -47,6 +47,7 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ First we will add a new property called _userName_ with type `string` in _[./src/nameEdit.tsx](./src/nameEdit.tsx)_. This one will hold the last accepted userName. _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ + ```diff interface Props { + userName : string; diff --git a/07_Enable/readme_es.md b/07_Enable/readme_es.md index 50058d1..ab43f4a 100644 --- a/07_Enable/readme_es.md +++ b/07_Enable/readme_es.md @@ -47,6 +47,7 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ Primero agregaremos una nueva propiedad llamada _userName_ con el tipo `string` en _[./src/nameEdit.tsx](./src/nameEdit.tsx)_. Este tendrÔ el último nombre de usuario aceptado. _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ + ```diff interface Props { + userName : string; @@ -73,7 +74,7 @@ _[./src/nameEdit.tsx](./src/nameEdit.tsx)_ ``` -- Ahora tenemos que alimentar esta propiedad desde el control principal (Agregar `userName={this.state.userName}` al NameEditComponent en _[./src/app.tsx](./src/app.tsx)_). El `NameEditComponent` debería ser como: +- Ahora tenemos que alimentar esta propiedad desde el control principal (Añade `userName={this.state.userName}` al NameEditComponent en _[./src/app.tsx](./src/app.tsx)_). El `NameEditComponent` debería ser como: _[./src/app.tsx](./src/app.tsx)_ ```diff @@ -99,7 +100,7 @@ npm start > Como ejercicio, ¿y si queremos hacer esto mÔs genérico? podríamos tener una propiedad genérica llamada enable que podría ser verdadera o falsa. -Para hacer esto, modificaremos [./src/app.tsx](./src/app.tsx) agregando la variable `disable` al componente` `. Esta variable es **Boolean**, por lo que necesita condiciones para evaluarla. +Para hacer esto, modificaremos [./src/app.tsx](./src/app.tsx) agregando la variable `disable` al componente` `. Esta variable es **Booleana**, por lo que necesita condiciones para evaluarla. _[./src/app.tsx](./src/app.tsx)_ ```diff From 046e55a13c55cb774a06231e2a535b7e22e74541 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Tue, 23 Oct 2018 11:25:38 +0200 Subject: [PATCH 108/180] Update readme.md --- 10_Sidebar/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/10_Sidebar/readme.md b/10_Sidebar/readme.md index 4ed6e6e..f3d781e 100644 --- a/10_Sidebar/readme.md +++ b/10_Sidebar/readme.md @@ -249,7 +249,6 @@ export class App extends React.Component<{}, State> { +
    + From a2287c97da1d6afad92736ea3fd1e8293c6c1e8c Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Tue, 23 Oct 2018 12:17:14 +0200 Subject: [PATCH 109/180] Update readme.md --- 11_TableMock/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/11_TableMock/readme.md b/11_TableMock/readme.md index 01701ec..ddd1de2 100644 --- a/11_TableMock/readme.md +++ b/11_TableMock/readme.md @@ -137,7 +137,7 @@ interface Props { // We define members as a state (the compoment holding this will be a container // component) interface State { - members : Array + members : MemberEntity[] } // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX From 4ad9fc45cf03b9069a1025f25fd95ec5c51d2bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Tue, 23 Oct 2018 22:05:49 +0200 Subject: [PATCH 110/180] Fix errors --- 08_Colorpicker/readme.md | 44 +++++++++++++++---------- 08_Colorpicker/readme_es.md | 64 +++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/08_Colorpicker/readme.md b/08_Colorpicker/readme.md index 3c49148..8c8d4f7 100644 --- a/08_Colorpicker/readme.md +++ b/08_Colorpicker/readme.md @@ -2,9 +2,9 @@ We take _01 HelloReact_ as reference. ->This example is based on the following [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), but adding some variations. +> This example is based on the following [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), but adding some variations. -Summary steps: +## Summary steps: - Rename _hello.tsx_ file to _colorpicker.tsx_. - Define properties and state. @@ -53,8 +53,8 @@ _./src/app.tsx_ ```jsx import * as React from 'react'; -import {Color} from './color'; -import {ColorPicker} from './colorpicker'; +import { Color } from './color'; +import { ColorPicker } from './colorpicker'; interface State { color : Color; @@ -89,11 +89,11 @@ _./src/main.tsx_ import * as React from 'react'; import * as ReactDOM from 'react-dom'; - import { HelloComponent } from './hello'; -+ import {App} from './app'; ++ import { App } from './app'; ReactDOM.render( -- -+ , +- ++ , document.getElementById('root')); ``` @@ -103,7 +103,7 @@ _./src/colorpicker.tsx_ ```diff import * as React from 'react'; -+ import {Color} from './color' ++ import { Color } from './color' + interface Props { + color : Color; @@ -132,7 +132,8 @@ _./src/colorpicker.tsx_ + max="255" + value={props.color.red} + onChange={(event) => props.onColorUpdated( -+ {red: +event.target.value, green: props.color.green, blue: props.color.blue} ++ {red: +event.target.value, green: ++ props.color.green, blue: props.color.blue} + )} + /> + {props.color.red} @@ -147,8 +148,8 @@ _./src/app.tsx_ ```diff import * as React from 'react'; - import {Color} from './color'; - import {ColorPicker} from './colorpicker'; + import { Color } from './color'; + import { ColorPicker } from './colorpicker'; interface State { color : Color; @@ -168,9 +169,18 @@ _./src/app.tsx_ public render() { return (
    -+ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] ++ ++ Color: [ ++ red: {this.state.color.red}, ++ green: {this.state.color.green}, ++ blue: {this.state.color.blue} ++ ] ++ - -+ ++
    ); } @@ -247,7 +257,7 @@ _./src/colordisplayer.tsx_ ```jsx import * as React from 'react'; - import {Color} from './color' + import { Color } from './color' interface Props { color : Color; @@ -271,9 +281,9 @@ _./src/colordisplayer.tsx_ ```diff import * as React from 'react'; -import {Color} from './color'; -import {ColorPicker} from './colorpicker'; -+ import {ColorDisplayer} from './colordisplayer'; +import { Color } from './color'; +import { ColorPicker } from './colorpicker'; ++ import { ColorDisplayer } from './colordisplayer'; interface State { color : Color; diff --git a/08_Colorpicker/readme_es.md b/08_Colorpicker/readme_es.md index 0d99c36..76d9825 100644 --- a/08_Colorpicker/readme_es.md +++ b/08_Colorpicker/readme_es.md @@ -2,9 +2,9 @@ Tomaremos como punto de partida el ejemplo _01 HelloReact_: ->Este ejemplo estÔ basado en el siguiente: [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), pero añade algunas variaciones +> Este ejemplo estÔ basado en el siguiente: [egghead jsbin](https://jsbin.com/qiwoxax/4/edit?html,js,output), pero añade algunas variaciones -Resumen de pasos: +## Pasos resumidos: - Renombrar el archivo _hello.tsx_ a _colorpicker.tsx_. - Definir las propiedades y el estado. @@ -13,7 +13,7 @@ Resumen de pasos: ## Prerrequisitos -Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 ó + nueva) si aún no estÔn instaladas. +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 ó mÔs reciente) si aún no estÔ instalada en la mÔquina. > Verificar que tienes node al menos v6.x.x y npm 3.x.x ejecutando `node -v` y `npm -v` en un terminal ó console de windows. Versiones viejas pueden provocar errores. @@ -21,9 +21,9 @@ Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 ó + nueva) si aún n - Copiar el contenido de _01 HelloReact_ y ejecutar `npm install`. -- Vamos a definir una estructura apropiada (crea un archivo _color.tsx_). +- Vamos a definir una estructura apropiada (crea un archivo _color.ts_). -_./src/color.tsx_ +_./src/color.ts_ ```javascript export interface Color { @@ -53,8 +53,8 @@ _./src/app.tsx_ ```jsx import * as React from 'react'; -import {Color} from './color'; -import {ColorPicker} from './colorpicker'; +import { Color } from './color'; +import { ColorPicker } from './colorpicker'; interface State { color : Color; @@ -89,11 +89,11 @@ _./src/main.tsx_ import * as React from 'react'; import * as ReactDOM from 'react-dom'; - import { HelloComponent } from './hello'; -+ import {App} from './app'; ++ import { App } from './app'; ReactDOM.render( -- -+ , +- ++ , document.getElementById('root')); ``` @@ -104,7 +104,7 @@ _./src/colorpicker.tsx_ ```diff import * as React from 'react'; -+ import {Color} from './color' ++ import { Color } from './color' + interface Props { + color : Color; @@ -133,7 +133,8 @@ _./src/colorpicker.tsx_ + max="255" + value={props.color.red} + onChange={(event : any) => props.onColorUpdated( -+ {red: event.target.value, green: props.color.green, blue: props.color.blue} ++ {red: event.target.value, ++ green: props.color.green, blue: props.color.blue} + )} + /> + {props.color.red} @@ -148,8 +149,8 @@ _./src/app.tsx_ ```diff import * as React from 'react'; - import {Color} from './color'; - import {ColorPicker} from './colorpicker'; + import { Color } from './color'; + import { ColorPicker } from './colorpicker'; interface State { color : Color; @@ -162,16 +163,25 @@ _./src/app.tsx_ this.state = {color: {red: 90, green: 50, blue: 70}}; } - setColorState(newColor : Color) { - this.setState({color: newColor}); - } ++ setColorState = (newColor : Color) => { ++ this.setState({color: newColor}); ++ } public render() { return (
    -+ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] ++ ++ Color: [ ++ red: {this.state.color.red}, ++ green: {this.state.color.green}, ++ blue: {this.state.color.blue} ++ ] ++ - -+ ++
    ); } @@ -216,7 +226,7 @@ _./src/colopicker.tsx_ + onChange={(event : any) => props.onColorUpdated( + { + red: props.color.red, -+ green: event.target.value, ++ green: +event.target.value, + blue: props.color.blue + } + )} @@ -231,7 +241,7 @@ _./src/colopicker.tsx_ + { + red: props.color.red, + green: props.color.green, -+ blue: event.target.value ++ blue: +event.target.value + } + )} + /> @@ -242,20 +252,20 @@ _./src/colopicker.tsx_ } ``` -- Haremos esto un poco mƔs atractivo visualmente, serƭa una buena idea mostrar un rectƔngulo relleno con el color selccionado. Crearemos un componente ColorDisplayer (_colordisplayer.tsx_). +- Haremos esto un poco mƔs atractivo visualmente, serƭa una buena idea mostrar un rectƔngulo relleno con el color seleccionado. Crearemos un componente ColorDisplayer (_colordisplayer.tsx_). _./src/colordisplayer.tsx_ ```jsx import * as React from 'react'; - import {Color} from './color' + import { Color } from './color' interface Props { color : Color; } export const ColorDisplayer = (props : Props) => { - const divStyle = { + const divStyle : React.CSSProperties = { // React.CSSProperties gives editing-time visual feedback on the CSS you are typing. width: '11rem', height: '7rem', backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})` @@ -273,8 +283,8 @@ _./src/colordisplayer.tsx_ ```diff import * as React from 'react'; import {Color} from './color'; -import {ColorPicker} from './colorpicker'; -+ import {ColorDisplayer} from './colordisplayer'; +import { ColorPicker } from './colorpicker'; ++ import { ColorDisplayer } from './colordisplayer'; interface State { color : Color; @@ -295,7 +305,7 @@ export class App extends React.Component<{}, State> { return (
    + -+ Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}]
    ); From 5227e87d7f005cde3f1de5571aee0fd8c52e46e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Tue, 23 Oct 2018 22:16:59 +0200 Subject: [PATCH 111/180] Refactor readme --- 08_Colorpicker/readme.md | 10 +++++++--- 08_Colorpicker/readme_es.md | 15 +++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/08_Colorpicker/readme.md b/08_Colorpicker/readme.md index 8c8d4f7..b7ff652 100644 --- a/08_Colorpicker/readme.md +++ b/08_Colorpicker/readme.md @@ -303,9 +303,13 @@ export class App extends React.Component<{}, State> { public render() { return (
    -+ - Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] - ++ + + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + +
    ); } diff --git a/08_Colorpicker/readme_es.md b/08_Colorpicker/readme_es.md index 76d9825..76c3cae 100644 --- a/08_Colorpicker/readme_es.md +++ b/08_Colorpicker/readme_es.md @@ -97,8 +97,7 @@ _./src/main.tsx_ document.getElementById('root')); ``` -- Vamos a cambiar tambiƩn el contenido del fichero, definimos un color y un - callback como propiedad para establecer el color (_colorpicker.tsx_). +- Vamos a cambiar tambiƩn el contenido del fichero, definimos un color y un callback como propiedad para establecer el color (_colorpicker.tsx_). _./src/colorpicker.tsx_ @@ -133,7 +132,7 @@ _./src/colorpicker.tsx_ + max="255" + value={props.color.red} + onChange={(event : any) => props.onColorUpdated( -+ {red: event.target.value, ++ {red: +event.target.value, + green: props.color.green, blue: props.color.blue} + )} + /> @@ -304,9 +303,13 @@ export class App extends React.Component<{}, State> { public render() { return (
    -+ - Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] - ++ + + Color: [red: {this.state.color.red}, green: {this.state.color.green}, blue: {this.state.color.blue}] + +
    ); } From be38f2f31a9c3fc1eb887c6d2431d891dd97f22d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Wed, 24 Oct 2018 21:36:32 +0200 Subject: [PATCH 112/180] Fix errors from readme --- 09_ColorpRefactor/readme.md | 10 +++++----- 09_ColorpRefactor/readme_es.md | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/09_ColorpRefactor/readme.md b/09_ColorpRefactor/readme.md index e0cbdec..d3b709b 100644 --- a/09_ColorpRefactor/readme.md +++ b/09_ColorpRefactor/readme.md @@ -4,7 +4,7 @@ In this example we are going to review the colorpicker component we have created We take _08 Colorpicker_ as reference. -Summary steps: +## Summary steps: - Create a simple color slider component. - Replace the color slider inputs with the new slider. @@ -26,7 +26,7 @@ _./src/colorslider.tsx_ ```jsx import * as React from 'react'; -import {Color} from './color'; +import { Color } from './color'; interface Props { value : number; @@ -53,8 +53,8 @@ export const ColorSliderComponent = (props : Props) => { ```diff import * as React from 'react'; -import {Color} from './color'; -+ import {ColorSliderComponent} from './colorslider'; +import { Color } from './color'; ++ import { ColorSliderComponent } from './colorslider'; interface Props { color : Color; @@ -154,7 +154,7 @@ _./src/colorpicker.tsx_ ```diff import * as React from 'react'; import { Color } from './color' -import {ColorSliderComponent} from './colorslider'; +import { ColorSliderComponent } from './colorslider'; interface Props { color: Color; diff --git a/09_ColorpRefactor/readme_es.md b/09_ColorpRefactor/readme_es.md index 2d09921..583e399 100644 --- a/09_ColorpRefactor/readme_es.md +++ b/09_ColorpRefactor/readme_es.md @@ -5,7 +5,7 @@ Haremos un escenario mÔs orientado al componente. Tomaremos como punto de partida el ejemplo _08 Colorpicker_: -Resumen de pasos: +## Pasos resumidos: - Crear un componente deslizable para un color simple. - Remplazar los slides inputs actuales con el nuevo componente creado. @@ -14,7 +14,7 @@ Resumen de pasos: ## Prerrequisitos -Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o posterior) si no estÔn ya instalados. +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si no estÔn ya instalados. > Verificar que estÔs ejecutando al menos la v6.x.x de node y npm 3.x.x ejecutando el siguiente comando `node -v` y `npm -v` en una terminal/console de window. Versiones viejas pueden producir errores. @@ -28,7 +28,7 @@ _./src/colorslider.tsx_ ```jsx import * as React from 'react'; -import {Color} from './color'; +import { Color } from './color'; interface Props { value : number; @@ -55,8 +55,8 @@ export const ColorSliderComponent = (props : Props) => { ```diff import * as React from 'react'; -import {Color} from './color'; -+ import {ColorSliderComponent} from './colorslider'; +import { Color } from './color'; ++ import { ColorSliderComponent } from './colorslider'; interface Props { color : Color; @@ -149,22 +149,24 @@ export const ColorPicker = (props : Props) => { npm start ``` -- Todavía tenemos mejoras que hacer, porque no tener un solo manejador para todos los colores? Si currificamos el colorupdated handler, podemos! +- Todavía tenemos mejoras que hacer, ¿porque no tener un solo manejador para todos los colores? Si currificamos el colorupdated handler, podemos! + +_./src/colorpicker.tsx_ ```diff import * as React from 'react'; import { Color } from './color' -import {ColorSliderComponent} from './colorslider'; +import { ColorSliderComponent } from './colorslider'; interface Props { color: Color; onColorUpdated: (color: Color) => void; } -+ const updateColor = (props : Props, colorId : keyof Color) => (value) => { ++ const updateColor = (props : Props, colorId : keyof Color) => (value) => { // keyof Color ensures only 'red', 'blue' or 'green' can be passed in. + props.onColorUpdated({ -+ ...props.color, -+ [colorId]: value ++ ...props.color, // this creates a clone of the current props.color object... ++ [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value. + }); + }; @@ -212,7 +214,7 @@ export const ColorPicker = (props: Props) => { } ``` -- Démosle una oprtunidad el ejemplo: +- Démosle una oprtunidad al ejemplo: ``` npm start From cf3d95caba650397c81ec37f05598c56d58d59f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Wed, 24 Oct 2018 22:01:52 +0200 Subject: [PATCH 113/180] Fix errors --- 09_ColorpRefactor/readme_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/09_ColorpRefactor/readme_es.md b/09_ColorpRefactor/readme_es.md index 583e399..b385d9d 100644 --- a/09_ColorpRefactor/readme_es.md +++ b/09_ColorpRefactor/readme_es.md @@ -16,7 +16,7 @@ Tomaremos como punto de partida el ejemplo _08 Colorpicker_: Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs reciente) si no estÔn ya instalados. -> Verificar que estÔs ejecutando al menos la v6.x.x de node y npm 3.x.x ejecutando el siguiente comando `node -v` y `npm -v` en una terminal/console de window. Versiones viejas pueden producir errores. +> Verificar que estÔs ejecutando al menos la v6.x.x de node y npm 3.x.x ejecutando el siguiente comando `node -v` y `npm -v` en una terminal/console de window. Versiones antiguas pueden producir errores. ## Pasos a seguir: From 46c0cb772b6f3dafdb0425725282bb7ea9d8f86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Thu, 25 Oct 2018 22:01:24 +0200 Subject: [PATCH 114/180] Fix errors from readme --- 10_Sidebar/readme.md | 24 +++++++++++------ 10_Sidebar/readme_es.md | 59 +++++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/10_Sidebar/readme.md b/10_Sidebar/readme.md index f3d781e..6b97ccf 100644 --- a/10_Sidebar/readme.md +++ b/10_Sidebar/readme.md @@ -4,7 +4,7 @@ In this sample we are going to implement a single sidebar. We will take a startup point sample _03 State_: -Summary steps: +## Summary steps: - Add some styles. - Include the new css into the webpack loop. @@ -142,7 +142,7 @@ _./src/app.tsx_ import * as React from 'react'; import { HelloComponent } from './hello'; import { NameEditComponent } from './nameEdit'; -+ import {SidebarComponent} from './sidebar'; ++ import { SidebarComponent } from './sidebar'; ``` _./src/app.tsx_ @@ -152,7 +152,9 @@ _./src/app.tsx_ <> + - + ); ``` @@ -204,7 +206,9 @@ interface Props { export const SidebarComponent = (props: Props) => -
    -+
    ++
    Basic sidebar, first steps
    ``` @@ -225,7 +229,10 @@ export class App extends React.Component<{}, State> { super(props); - this.state = {userName: 'defaultUserName'}; -+ this.state = {userName: "defaultUserName", isSidebarVisible: false}; ++ this.state = { ++ userName: "defaultUserName", ++ isSidebarVisible: false ++ }; } setUsernameState(event) { @@ -246,7 +253,8 @@ export class App extends React.Component<{}, State> { - + - + +
    + @@ -247,7 +264,7 @@ export class App extends React.Component<{}, State> { - Llegados a este punto necesitaremos parar y volver a arrancar la aplicación para ver los cambios funcionando: -```cmd +``` npm start ``` @@ -255,12 +272,20 @@ npm start - Hasta aqui todo bien, pero ¿que pasa si queremos hacer que nuestra barra lateral sea un componente reusable? Podriamos simplemente mostrar nuestra caja pero el contenido debería ser dinÔmico. -- Comencemos añadiendo algo de contenido cuando nuestra barra lateral es instanciada. +- Comencemos añadiendo algo de contenido cuando nuestra barra lateral es instanciada (_app.tsx_). + +_./src/app.tsx_ ```diff - -+

    Test content

    -
    +- ++ ++

    Cool Scfi movies

    ++ ++
    ``` - Ahora vamos a volcar algo de contenido en _sidebar.tsx_ usando {this.props.children} @@ -291,6 +316,6 @@ const divStyle = (props: React.CSSProperties) => ({ - Probemos el ejemplo -```cmd +``` npm start ``` From 4736400f0c1f003057e00b2456125c9d6bea221b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Thu, 25 Oct 2018 22:29:31 +0200 Subject: [PATCH 115/180] Fix more errors from readme --- 10_Sidebar/readme.md | 8 +++----- 10_Sidebar/readme_es.md | 6 +++++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/10_Sidebar/readme.md b/10_Sidebar/readme.md index 6b97ccf..bebad6b 100644 --- a/10_Sidebar/readme.md +++ b/10_Sidebar/readme.md @@ -186,8 +186,7 @@ const classNames = require('./sidebar.css');
    ``` -- Now let's add some logic to show / hide the sidebar in case the flag gets -updated +- Now let's add some logic to show / hide the sidebar in case the flag gets updated _./src/sidebar.tsx_ @@ -253,7 +252,7 @@ export class App extends React.Component<{}, State> { - + - +
    +
    + + + + +``` + +> No podemos usar max-widh en el parametro style. Tenemos que escribir 'maxWidth' en los componentes de react. + +- Luego vamos a implementar un componente que mostrarĆ” una lista de miembros (y usaremos las filas), _membersTable.tsx_: + +_./src/membersTable.tsx_ + +```javascript +import * as React from 'react'; +import {MemberEntity} from './model/member'; +import {memberAPI} from './api/memberAPI'; +import {MemberRow} from './memberRow'; + +interface Props { +} + +// We define members as a state (the compoment holding this will be a container +// component) +interface State { + members : MemberEntity[] +} + +// Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX +export class MembersTableComponent extends React.Component { + + constructor(props : Props){ + super(props); + // set initial state + this.state = {members: []}; + } + + // Standard react lifecycle function: + // https://facebook.github.io/react/docs/component-specs.html + public componentDidMount() { + this.setState({members: memberAPI.getAllMembers()}) + } + + public render() { + + return ( +
    +

    Members Page

    +
    + + + {props.member.id} + + {props.member.login} +
    + + + + + + + + + { + this.state.members.map((member : MemberEntity) => + + ) + } + +
    + Avatar + + Id + + Name +
    +
    + ); + } +} +``` + +- Vamos a actualizar app.tsx + +```diff +import * as React from 'react'; +- import { HelloComponent } from './hello'; +- import { NameEditComponent } from './nameEdit'; ++ import {MembersTableComponent} from './membersTable'; + +interface State { + userName : string; +} + +export class App extends React.Component<{}, State> { + public render() { + return ( + <> ++ +- +- + + + ); + } +} + +``` + +Ejecutemos la muestra + +``` +npm start +``` From efd9d7628795be962a605301d5a6e1d9df6a034c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 29 Oct 2018 22:35:37 +0100 Subject: [PATCH 117/180] Fix wrongs errors --- 12_TableHttp/readme.md | 6 +++--- 12_TableHttp/readme_es.md | 31 ++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/12_TableHttp/readme.md b/12_TableHttp/readme.md index 7463b6e..643057e 100644 --- a/12_TableHttp/readme.md +++ b/12_TableHttp/readme.md @@ -5,7 +5,7 @@ mock data by real one. We will take a startup point sample _11 TableMock_: -Summary steps: +## Summary steps: - Configure transpilation and add extra transpile step babel >> es5. - Update API in order to work with promises and fetch data from Github API. @@ -39,7 +39,7 @@ npm install whatwg-fetch --save-dev _./src/api/memberAPI.ts_ ```javascript -import {MemberEntity} from '../model/member'; +import { MemberEntity } from '../model/member'; import {} from 'whatwg-fetch'; // Sync mock data API, inspired from: @@ -117,7 +117,7 @@ _./src/membersTable.tsx_ ```diff -+ import {MemberHead} from './memberHead'; ++ import { MemberHead } from './memberHead'; ``` diff --git a/12_TableHttp/readme_es.md b/12_TableHttp/readme_es.md index 73b3656..b883e04 100644 --- a/12_TableHttp/readme_es.md +++ b/12_TableHttp/readme_es.md @@ -4,7 +4,7 @@ Sigamos con nuestro ejemplo de la tabla, vamos a cambiar datos falsos por unos r Cogeremos como punto inicial el ejemplo _11 TableMock_: -Pasos resumidos: +## Pasos resumidos: - Configurar transpilación y añadir una transpilación extra babel >> es5. - Actualizar la API para trabajar con promesas y obtener datos de la API de Github. @@ -13,11 +13,11 @@ Pasos resumidos: ## Prerrequisitos -Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no estÔn ya instalados. +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no estÔn ya instalados. > Verificar que tienes al menos corriendo la versión de node v6.x.x y npm 3.x.x ejecutando `node -v` y `npm -v` en la terminal de Windows. Versiones mÔs antiguas pueden producir errores. -## Pasos +## Pasos para construirlo - Copiar el contenido de _11 TableMock_ y ejecutar: @@ -27,13 +27,18 @@ Instalar [Node.js and npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no e - Vamos a eliminar el fichero _mermberMockData.ts_ del directorio _src/api_ . +- Para tener soporte en navegadores antiguos vamos a instalar la siguiente librería: + +```javascript +npm install whatwg-fetch --save-dev +``` + - Vamos a reemplazar _memberAPI_ cargando los miembros con promesas : _./src/api/memberAPI.ts_ ```javascript -import {MemberEntity} from '../model/member'; -import {} from 'core-js'; +import { MemberEntity } from '../model/member'; import {} from 'whatwg-fetch'; // Sync mock data API, inspired from: @@ -79,8 +84,11 @@ class MemberAPI { } export const memberAPI = new MemberAPI(); + ``` -- Añadimos un nuevo componente _memberHead_ para crear la cabecera de la tabla : +- Añadimos un nuevo componente _memberHead_ para crear la cabecera de la tabla: + +_./src/memberHead.tsx_ ```javascript import * as React from 'react'; @@ -104,11 +112,16 @@ export const MemberHead = () => _./src/memberTable.tsx_ -- Importamos el nuevo componente : +- Importamos el nuevo componente: + ```diff -+ import {MemberHead} from './memberHead'; + ++ import { MemberHead } from './memberHead'; + ``` -- Modificamos la funcion render : + +- Modificamos la funcion render: + ```diff - - From 81e5e131f423b61510365f65d10c88ef43091c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 29 Oct 2018 22:43:02 +0100 Subject: [PATCH 118/180] fix more errors --- 12_TableHttp/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/12_TableHttp/readme.md b/12_TableHttp/readme.md index 643057e..d1dec19 100644 --- a/12_TableHttp/readme.md +++ b/12_TableHttp/readme.md @@ -113,7 +113,7 @@ export const MemberHead = () => _./src/membersTable.tsx_ -- Import the new component : +- Import the new component: ```diff @@ -121,7 +121,7 @@ _./src/membersTable.tsx_ ``` -- Modify the render function : +- Modify the render function: ```diff - From 553e8575d11c01365fd5d3709b83bce7abc79a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Tue, 30 Oct 2018 22:30:00 +0100 Subject: [PATCH 119/180] fix name property --- 02_Properties/readme.md | 2 +- 02_Properties/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/02_Properties/readme.md b/02_Properties/readme.md index 4817d7f..2eb6711 100644 --- a/02_Properties/readme.md +++ b/02_Properties/readme.md @@ -47,7 +47,7 @@ Side note: using interfaces and ES6, the change looks like this: import * as React from 'react'; + interface Props { -+ username: string; ++ userName: string; + } - export const HelloComponent = () => { diff --git a/02_Properties/readme_es.md b/02_Properties/readme_es.md index 16537fa..fee2acd 100644 --- a/02_Properties/readme_es.md +++ b/02_Properties/readme_es.md @@ -46,7 +46,7 @@ import * as React from 'react'; import * as React from 'react'; + interface Props { -+ username: string; ++ userName: string; + } - export const HelloComponent = () => { From c4cc00adf4ed40fff82ab4634b941eec6d36221b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Tue, 30 Oct 2018 23:23:59 +0100 Subject: [PATCH 120/180] Remove bootstrap class --- 06_MoveBackToStateless/readme.md | 2 +- 06_MoveBackToStateless/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/06_MoveBackToStateless/readme.md b/06_MoveBackToStateless/readme.md index 651b501..28a4335 100644 --- a/06_MoveBackToStateless/readme.md +++ b/06_MoveBackToStateless/readme.md @@ -37,7 +37,7 @@ export const NameEditComponent = (props : Props) => props.onEditingNameUpdated((e.target as HTMLInputElement).value)} /> - +
    + + + ) +} + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); +``` + +- Esto puede estÔr ok, pero si analizamos este componente mÔs a fondo, prodíamos dividirlo en dos, una es la tarjeta en sí, la otra el cuadro del diÔlogo, por lo que finalmente dibería verse como + +** Propósito ** + +```javascript + + + + + + +``` + +- Creamos el loginformcomponent: + +_./src/pages/login/loginForm.tsx_ + +```javascript +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; + +export const LoginForm = (props) => { + return ( +
    + + + +
    + ) +} +``` + +- Y actualizamos _loginPage.tsx_ + +_./src/pages/loginPage.tsx_ + +```diff +import * as React from "react" +import { Link } from 'react-router-dom'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { FormHelperText } from "@material-ui/core"; ++ import { LoginForm } from './loginForm'; + +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +interface Props extends RouteComponentProps, WithStyles { +} + +const LoginPageInner = (props : Props) => { + const { classes } = props; + + return ( + + + ++ +-
    +- +- +- +-
    +
    +
    + ) +} + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); +``` + +- Vamos a intentar y verificar como se estÔ viendo. + +```bash +npm start +``` + +- Añadamos una navegación en el botón, hagÔmoslo en dos pasos. + +- Primero exponemos un método para hacer esto en loginPage. + +_./src/pages/login/loginPage.tsx_ + +```diff +// ... + +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +interface Props extends RouteComponentProps, WithStyles { +} + +const LoginPageInner = (props) => { + const { classes } = props; + ++ const onLogin = () => { ++ props.history.push('/pageB'); ++ } + + return ( + + + +- ++ + + + ) +} + +- export const LoginPage = withStyles(styles)(LoginPageInner); ++ export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); +``` + +- Añadamos la navegación en el botón (mas tarde verificaremos el usuario y la contraseña) _form.tsx_. +En order de hacer esto usaremos react-router 4 "withRouter" HoC +(High order component). + +_./src/pages/login/form.tsx_ + +```diff +import * as React from "react" + +interface Props { ++ onLogin : () => void; +} + ++export const LoginForm = (props : Props) => { +- export const LoginForm = () => { ++ const { onLogin } = this.props; + + return ( +
    + +
    +
    + +
    +
    + +
    +- +- +
    + +
    + ); +- } ++}) +``` + +- Demos un vistazo rĆ”pido. + +```bash +npm start +``` + +Ok, podemos navegar cuando nosotros clicamos en la pĆ”gina de login. + +- Guardemos el progreso, ahora es momento de recoger la información username y password, y verificaremos si es vĆ”lida la contraseƱa. + +- Definamos una entidad para loginInfo crearemos la seguiente ruta y el fichero + +_src/model/login.ts_ + +```javascript +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); +``` + +- AƱadamos a la restApi falsa y aƱadamos la validación de login: crearemos una carpeta _src/api_. y un fichero llamado _login.ts_ + +_./src/api/login.ts_ + +```javascript +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); +``` + +- How it should look + +``` +. +└── src/ + │ + ā”œā”€ā”€ api/ + │ └── login.ts + ā”œā”€ā”€ model/ + │ └── login.ts + └── pages/ + ā”œā”€ā”€ login/ + │ ā”œā”€ā”€ form.tsx + │ ā”œā”€ā”€ header.tsx + │ ā”œā”€ā”€ index.ts + │ └── loginPage.tsx + └── b/ + ā”œā”€ā”€ index.ts + └── pageB.tsx +``` + +- AƱadamos una integración a _api_, mĆ”s una navegación en el existo de login. + +- Primero crearemos un estado de login y aƱade la integración de la api. + +_./src/pages/login/loginPage.tsx_ + +```diff +import * as React from "react" +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter } from 'react-router-dom'; +import {History} from 'history'; ++ import { LoginEntity, createEmptyLogin } from '../../model/login'; ++ import { isValidLogin } from '../../api/login'; + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + ++ interface State { ++ loginInfo: LoginEntity; ++ } + +interface Props extends RouteComponentProps, WithStyles { +} + +- const LoginPageInner = (props) => { ++ class LoginPageInner extends React.Component { +- const { classes, history } = props; + ++ constructor(props) { ++ super(props); ++ ++ this.state = { loginInfo: createEmptyLogin() } ++ } + +- const onLogin = () => { ++ onLogin = () => { ++ if (isValidLogin(this.state.loginInfo)) { ++ this.props.history.push('/pageB'); +- history.push('/pageB'); ++ } + } + ++ public render() { ++ const { classes } = this.props; + + return ( + + + +- ++ + + + ) ++ } +} + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); +``` + +- Ahora leamos un dato del componente textfields (usuario y contraseƱa). + +_./src/pages/login/loginPage.tsx_ + +```diff +class LoginPageInner extends React.Component { + + onLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } + } + ++ onUpdateLoginField = (name, value) => { ++ this.setState({loginInfo: { ++ ...this.state.loginInfo, ++ [name]: value, ++ }}) ++ } + + render() { + const { classes } = this.props; + + return ( + + + + + + + ) + + } +} +``` + +_./src/pages/login/loginForm.tsx_ + +```diff +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; ++ import { LoginEntity } from "../../model/login"; + +interface Props { + onLogin : () => void; ++ onUpdateField: (string, any) => void; ++ loginInfo : LoginEntity; +} + +export const LoginForm = (props : Props) => { +- const { onLogin } = props; ++ const { onLogin, onUpdateField, loginInfo } = props; + ++ // TODO: Enhacement move this outside the stateless component discuss why is a good idea ++ const onTexFieldChange = (fieldId) => (e) => { ++ onUpdateField(fieldId, e.target.value); ++ } + + return ( +
    + + + +
    + ) +} +``` + +- Mostremos una notificación cuando la validación de login falle. + +- Primero crearemos un componente de notificación simple, basado en _react material ui_ _snackbar_ + +_./src/common/notification.tsx_ + +```javascript +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles } from "@material-ui/core"; + +interface Props { + classes?: any; + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => ({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const {classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); +``` + +- Vamos a exponer este componente común via un fichero _index_. + +_./src/common/index.tsx_ + +```javascript +export * from './notification'; +``` + +- Ahora lo instanciamos en nuestro _loginPage_ + +_./src/pages/login/loginPage.tsx_ + +```diff +import * as React from "react" +import { withStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { History } from 'history'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; ++ import { NotificationComponent } from '../../common' + +const styles = theme => ({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + +interface State { + loginInfo: LoginEntity; ++ showLoginFailedMsg: boolean; +} + + +interface Props extends RouteComponentProps { + classes?: any; +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { loginInfo: createEmptyLogin(), ++ showLoginFailedMsg : false, + } + } + + onLogin = () => { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); +- } ++ } else { ++ this.setState({showLoginFailedMsg: true}); ++ } + } + + onUpdateLoginField = (name: string, value) => { + this.setState({ + loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }) + } + + render() { + const { classes } = this.props; + + return ( ++ <> + + + + + + ++ this.setState({showLoginFailedMsg: false})} ++ /> ++ + ) + + } +} + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); +``` + +- Estamos recibiendo algunas advertencias debido a la nueva versión de la tipografía, agreguemos un tema para solucionarlo. + +Cuidado con la nueva tipografía y la snackbar: +https://github.com/mui-org/material-ui/issues/13144 + +_./src/main.tsx_ + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; ++ import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; +import {LoginPage} from './pages/login'; +import {PageB} from './pages/b'; + ++ const theme = createMuiTheme({ ++ typography: { ++ useNextVariants: true, ++ }, ++ }); + + +ReactDOM.render( ++ + + + + + + ++ + ,document.getElementById('root') +); +``` + +- Estamos recibiendo algunas advertencias en la snackbar, pu puedes arreglarlo aquí: + +https://github.com/mui-org/material-ui/issues/13144 + +https://codesandbox.io/s/zz6wnqklzm + +> ¿ Y la validación del formulario ? Hay muchas librerías disponibles, una que nosotros hemos creado en lemoncode es lc-form-validation crearemos una muestra incluyendo esta librería (campos requeridos) \ No newline at end of file From 1268f2897d227aea4f2d9fcd7ce0d48ebe1be294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 3 Nov 2018 21:13:57 +0100 Subject: [PATCH 130/180] fix readme --- 15_LoginForm/readme.md | 2 +- 15_LoginForm/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/15_LoginForm/readme.md b/15_LoginForm/readme.md index 008ee1a..51ddab6 100644 --- a/15_LoginForm/readme.md +++ b/15_LoginForm/readme.md @@ -391,7 +391,7 @@ const LoginPageInner = (props) => { - Let's add the navigation on button clicked (later on we will check for user and pwd) _form.tsx_. In order to do this we will use react-router 4 "withRouter" HoC (High order component). -_./src/pages/login/form.tsx_ +_./src/pages/login/LoginForm.tsx_ ```diff import * as React from "react" diff --git a/15_LoginForm/readme_es.md b/15_LoginForm/readme_es.md index 91695b3..ae3f8a6 100644 --- a/15_LoginForm/readme_es.md +++ b/15_LoginForm/readme_es.md @@ -382,7 +382,7 @@ const LoginPageInner = (props) => { En order de hacer esto usaremos react-router 4 "withRouter" HoC (High order component). -_./src/pages/login/form.tsx_ +_./src/pages/login/LoginForm.tsx_ ```diff import * as React from "react" From 5f831e4c6dc31dcd599b7f22f35613f2fd934c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sun, 4 Nov 2018 19:08:50 +0100 Subject: [PATCH 131/180] Create readme spanish version and fix errors --- 17 Context/Readme.md | 36 ++---- 17 Context/readme_es.md | 259 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+), 23 deletions(-) create mode 100644 17 Context/readme_es.md diff --git a/17 Context/Readme.md b/17 Context/Readme.md index e9bd1c6..10fe407 100644 --- a/17 Context/Readme.md +++ b/17 Context/Readme.md @@ -1,15 +1,12 @@ -# Intro +## Intro In this sample we are going to learn how React 16 context api works. -This will allow us to share information between components without having -to go through props drilldown or having to add redux support to our project. +This will allow us to share information between components without having to go through props drilldown or having to add redux support to our project. -# Steps +## Steps to build it -- We want to store just the _login_ field once the user logs in and display it -in the page B (or in wathever page or component we need it), let's add a -default value ('no user'). +- We want to store just the _login_ field once the user logs in and display it in the page B (or in wathever page or component we need it), let's add a default value ('no user'). - Let's start by creating a context, we will call it _sessionContext_, and add the proper typing @@ -28,11 +25,8 @@ export const createDefaultUser = () : SessionContextProps => ({ export const SessionContext = React.createContext(createDefaultUser()); ``` -- This session context will expose a _provider_ (will serve us to set the login name in the context), -and a _consumer_ (will let us consume the login name from the context from any point of the application). -We will create a component (we will name it _SessionProvider) that on one hand will store in the state -the login name and bind it to the _SessionContext_ and in the other hand it act as a wrapper (usually -it will sit on top of the application and wrap the application). +- This session context will expose a _provider_ (will serve us to set the login name in the context), and a _consumer_ (will let us consume the login name from the context from any point of the application). +We will create a component (we will name it _SessionProvider) that on one hand will store in the state the login name and bind it to the _SessionContext_ and in the other hand it act as a wrapper (usually it will sit on top of the application and wrap the application). _./src/common/sessionContext.tsx_ @@ -84,7 +78,7 @@ import { HashRouter, Switch, Route } from 'react-router-dom'; import { LoginPage } from './pages/login'; import { PageB } from './pages/b'; import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; -+ import {SessionProvider} from './common' ++ import { SessionProvider } from './common' const theme = createMuiTheme({ typography: { @@ -114,7 +108,7 @@ _./src/pages/b/pageB.tsx_ ```diff import * as React from "react" import { Link } from 'react-router-dom'; -+ import {SessionContext} from '../../common/' ++ import { SessionContext } from '../../common/' export const PageB = () =>
    @@ -134,15 +128,13 @@ export const PageB = () =>
    ``` -- If we ran the sample we can navigate to page B and see the default login name being displayed +- If we ran the sample we can navigate to page B and see the default login name being displayed. ```bash npm start ``` -- Showing a default name is not a bad thing, but we need to display the real login name -entered by the user, to do this we will expose a function into our context that -will let any consumer update the value. +- Showing a default name is not a bad thing, but we need to display the real login name entered by the user, to do this we will expose a function into our context that will let any consumer update the value. _./src/common/sessionContext.tsx_ @@ -169,7 +161,6 @@ export const createDefaultUser = () : SessionContextProps => ({ - Let's configure this in the provider state. - ```diff export class SessionProvider extends React.Component<{}, State> { @@ -203,7 +194,7 @@ export class SessionProvider extends React.Component<{}, State> { _./src/pages/login/loginPage.tsx_ ```diff -+ import {SessionContext} from '../../common'; ++ import { SessionContext } from '../../common'; ``` - Let's update our login component property to accept the setLogin method. @@ -215,6 +206,7 @@ interface Props extends RouteComponentProps, WithStyles { + updateLogin: (value) => void } ``` + - We will create an intermediate component (in our next sample we will port it to a generic Hoc). _./src/pages/login/loginPage.tsx_ @@ -264,6 +256,4 @@ _./src/pages/login/loginPage.tsx_ npm start ``` -> If have to nest many render props, you can end up having a heavy nested -component, in that case checkout react-composer micro library (https://github.com/jamesplease/react-composer) - +> If have to nest many render props, you can end up having a heavy nested component, in that case checkout react-composer micro library (https://github.com/jamesplease/react-composer) \ No newline at end of file diff --git a/17 Context/readme_es.md b/17 Context/readme_es.md new file mode 100644 index 0000000..d5f300a --- /dev/null +++ b/17 Context/readme_es.md @@ -0,0 +1,259 @@ +# Intro + +En esta muestra vamos a aprender como la api de context en React 16 trabaja. + +Esto nos permitirÔ buscar información entre componentes sin tener que ir profundizando traves de las propiedades o tener que añadir la ayuda de redux a nuestro proyecto. + +## Pasos para construirlo + +- Queremos guardar el campo de _login_ una vez el usuario se logue y muestre la pÔgina B (o en culaquier pÔgina o componente que lo necesite), vamos a añadir un valor por defecto ('no user'). + +- Empezaremos por crear un contexto, lo llamaremos _sessionContext_, y añade los tipos apropiados + +_./src/common/sessionContext.tsx_ + +```javascript +import * as React from "react" + +export interface SessionContextProps { + login : string; +} +export const createDefaultUser = () : SessionContextProps => ({ + login: 'no user', +}); + +export const SessionContext = React.createContext(createDefaultUser()); +``` + +- Esta contexto de sesión expondrÔ un _provider_ (Nos servirÔ para establecer el nombre de inicio de sesión en el contexto), y un _consumer_ (Nos permitirÔ consumir el nombre de inicio de sesión del contexto desde cualquier punto de la aplicación). +Creamos un componente (lo nombraremos _SessionProvider) que en una mano guardarÔ en el estado el nombre del login y lo atarÔ a _SessionContext_ y en la otra acturÔ como una envoltura (normalmente estÔ al principio de la aplicación y envuelve la aplicación). + +_./src/common/sessionContext.tsx_ + +```diff +import * as React from "react" + +export interface SessionContextProps { + login : string; +} + +export const SessionContext = React.createContext(createDefaultUser()); + ++ interface State extends SessionContextProps { ++ } ++ ++ export class SessionProvider extends React.Component<{}, State> { ++ ++ constructor(props) { ++ super(props); ++ this.state = createDefaultUser(); ++ } ++ ++ render() { ++ return ( ++ ++ {this.props.children} ++ ++ )}; ++ }; +``` + +- Añade esto a un _index_ barrel común. + +_./src/common/index.tsx_ + +```diff +export * from './notification'; ++ export * from './sessionContext'; +``` + +- Es momento de exponer este provider al principio de nuestra aplicación. + +_./src/main.tsx_ + +```diff +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; ++ import { SessionProvider } from './common' + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); + +ReactDOM.render( + ++ + + + + + + ++ + + , document.getElementById('root') +); +``` + +- En la pageB consumiremos el nombre de usuario de sessionContext. + +_./src/pages/b/pageB.tsx_ + +```diff +import * as React from "react" +import { Link } from 'react-router-dom'; ++ import { SessionContext } from '../../common/' + +export const PageB = () => +
    ++ ++ { ++ ({login}) => ( ++ <> +

    Hello from page B

    ++
    ++

    Login: {login}

    +
    + Navigate to Login ++ ++ ) ++ } ++
    +
    +``` + +- Si ejecutamos la muestra podemos navegar a la pÔgina B y ver mostrado el nombre por defecto atado + +```bash +npm start +``` + +- Mostrar un nombre por defecto no es una cosa mala, pero necesitamos mostrar el nombre de login real que ingresado por el usuario, para hacer esto expondremos una función dentro de nuestro contexto que permitirÔ a cualquier consumer actualizar el valor. + +_./src/common/sessionContext.tsx_ + +```diff +``` + +- Ahora aplicaremos esto en el loginPage. + +Primero añade un métodoo de actualización de login. + +_./src/pages/login/loginPage.tsx_ + +```diff +export interface SessionContextProps { + login: string; ++ updateLogin: (value) => void; +} + +export const createDefaultUser = () : SessionContextProps => ({ + login: 'no user', ++ updateLogin: (value) => {}, +}); +``` + +- Configuraremos esto en el provider del estado. + +```diff +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); +- this.state = createDefaultUser(); ++ this.state = { ++ login: createDefaulUser.login, ++ updateLogin: this.setLoginInfo ++ } + } + ++ setLoginInfo = (newLogin) => { ++ this.setState({login: newLogin}) ++ } + + render() { + return ( + + {this.props.children} + + ) + }; +}; +``` + +- Momento de configurar este valor cuando hacemos clic en el botón de inicio de sesión. + +- Añade un import a nuestro context. + +_./src/pages/login/loginPage.tsx_ + +```diff ++ import { SessionContext } from '../../common'; +``` + +- Actualiza nuestra propiedad del componente login para aceptar el método setLogin. + +_./src/pages/login/loginPage.tsx_ + +```diff +interface Props extends RouteComponentProps, WithStyles { ++ updateLogin: (value) => void +} +``` + +- Crearemos un componente intermédio (en nuestra siguiente muestra lo portaremos como un Hoc genérico). + +_./src/pages/login/loginPage.tsx_ + +```diff ++ export const LoginPageInner2 = (props) => ++ <> ++ ++ { ++ ({updateLogin}) => ++ ++ } ++ ++ ++ + +- export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); ++ export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); + +``` + +- Llamemos al setLogin una vez el usuario pulse. + +_./src/pages/login/loginPage.tsx_ + +```diff + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { ++ this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } +``` + +- Si ejecutamos la app podremos verificar que ahora nosotros tenemos el resultado correcto + +```bash +npm start +``` + +> Si tenemos que anidar muchas render props, podemos terminar teniendo un componente anidado pesado, en ese caso revisa la micro librería react-composer (https://github.com/jamesplease/react-composer). \ No newline at end of file From 057c5d3da4bc83c762b11982cb76be80ede9b228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sun, 4 Nov 2018 21:58:32 +0100 Subject: [PATCH 132/180] Fix errors on spanish and english version --- 17 Context/Readme.md | 193 +++++++++++++++++++------------------ 17 Context/readme_es.md | 209 ++++++++++++++++++++-------------------- 2 files changed, 204 insertions(+), 198 deletions(-) diff --git a/17 Context/Readme.md b/17 Context/Readme.md index 10fe407..714ff8e 100644 --- a/17 Context/Readme.md +++ b/17 Context/Readme.md @@ -4,6 +4,14 @@ In this sample we are going to learn how React 16 context api works. This will allow us to share information between components without having to go through props drilldown or having to add redux support to our project. +We will take a startup point sample _16 Validation_: + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + ## Steps to build it - We want to store just the _login_ field once the user logs in and display it in the page B (or in wathever page or component we need it), let's add a default value ('no user'). @@ -13,29 +21,34 @@ This will allow us to share information between components without having to go _./src/common/sessionContext.tsx_ ```javascript -import * as React from "react" +import * as React from "react"; export interface SessionContextProps { - login : string; -} -export const createDefaultUser = () : SessionContextProps => ({ + login: string; +} + +const createDefaultUser = (): SessionContextProps => ({ login: 'no user', }); export const SessionContext = React.createContext(createDefaultUser()); ``` -- This session context will expose a _provider_ (will serve us to set the login name in the context), and a _consumer_ (will let us consume the login name from the context from any point of the application). -We will create a component (we will name it _SessionProvider) that on one hand will store in the state the login name and bind it to the _SessionContext_ and in the other hand it act as a wrapper (usually it will sit on top of the application and wrap the application). +- This session context will expose a _provider_ (it will serve us to set the login name in the context), and a _consumer_ (that will let us consume the login name from the context at any point of the application). +We will create a component (we will name it _SessionProvider_) that on one hand will store in the state the login name and bind it to the _SessionContext_ and on the other hand it will act as a wrapper (usually it will sit on top of the application and wrap the application). _./src/common/sessionContext.tsx_ ```diff -import * as React from "react" +import * as React from "react"; export interface SessionContextProps { - login : string; -} + login: string; +} + +const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', +}); export const SessionContext = React.createContext(createDefaultUser()); @@ -44,18 +57,19 @@ export const SessionContext = React.createContext(createDef + + export class SessionProvider extends React.Component<{}, State> { + -+ constructor(props) { -+ super(props); -+ this.state = createDefaultUser(); -+ } ++ constructor(props) { ++ super(props); ++ this.state = createDefaultUser(); ++ } + -+ render() { -+ return ( -+ -+ {this.props.children} -+ -+ )}; -+ }; ++ render() { ++ return ( ++ ++ {this.props.children} ++ ++ ); ++ } ++ } ``` - Let's add this to the common _index_ barrel. @@ -78,7 +92,7 @@ import { HashRouter, Switch, Route } from 'react-router-dom'; import { LoginPage } from './pages/login'; import { PageB } from './pages/b'; import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; -+ import { SessionProvider } from './common' ++ import { SessionProvider } from './common'; const theme = createMuiTheme({ typography: { @@ -96,64 +110,57 @@ ReactDOM.render( + - - , document.getElementById('root') + , + document.getElementById('root') ); ``` -- On pageB let's consume the sessionContext user name. +- On pageB let's consume the SessionContext login field. _./src/pages/b/pageB.tsx_ ```diff import * as React from "react" import { Link } from 'react-router-dom'; -+ import { SessionContext } from '../../common/' ++ import { SessionContext } from '../../common'; export const PageB = () => -
    -+ -+ { -+ ({login}) => ( -+ <> -

    Hello from page B

    -+
    -+

    Login: {login}

    -
    - Navigate to Login -+ -+ ) -+ } -+
    -
    +
    ++ ++ { ++ ({login}) => ( ++ <> +

    Hello from page B

    ++
    ++

    Login: {login}

    +
    + Navigate to Login ++ ++ ) ++ } ++
    +
    ``` -- If we ran the sample we can navigate to page B and see the default login name being displayed. +- If we run the sample we can navigate to page B and see the default login name being displayed. ```bash npm start ``` -- Showing a default name is not a bad thing, but we need to display the real login name entered by the user, to do this we will expose a function into our context that will let any consumer update the value. - -_./src/common/sessionContext.tsx_ - -```diff -``` - -- Now let's apply this on the loginPage. +- Showing a default name is not a bad thing, but we need to display the real login name entered by the user, to do this we will expose a function into our context that will let any consumer update the value. -First let's add an update login method. +- First let's add an update login method. -_./src/pages/login/loginPage.tsx_ +_./src/common/sessionContext.tsx_ ```diff export interface SessionContextProps { login: string; -+ updateLogin: (value) => void; ++ updateLogin: (value) => void; } -export const createDefaultUser = () : SessionContextProps => ({ +export const createDefaultUser = (): SessionContextProps => ({ login: 'no user', + updateLogin: (value) => {}, }); @@ -166,30 +173,30 @@ export class SessionProvider extends React.Component<{}, State> { constructor(props) { super(props); -- this.state = createDefaultUser(); -+ this.state = { -+ login: createDefaulUser.login, -+ updateLogin: this.setLoginInfo -+ } +- this.state = createDefaultUser(); ++ this.state = { ++ login: createDefaulUser.login, ++ updateLogin: this.setLoginInfo, ++ } } -+ setLoginInfo = (newLogin) => { -+ this.setState({login: newLogin}) -+ } ++ setLoginInfo = (newLogin) => { ++ this.setState({login: newLogin}); ++ } render() { return ( {this.props.children} - ) - }; -}; + ); + } +} ``` -- Time to setup this value when we click on the login button. +- Time to set up this value when we click on the login button. -- Let's add an import to our context. +- Let's add an import to our login page. _./src/pages/login/loginPage.tsx_ @@ -197,57 +204,53 @@ _./src/pages/login/loginPage.tsx_ + import { SessionContext } from '../../common'; ``` -- Let's update our login component property to accept the setLogin method. +- Let's update our login component props to accept the updateLogin method. _./src/pages/login/loginPage.tsx_ ```diff interface Props extends RouteComponentProps, WithStyles { -+ updateLogin: (value) => void ++ updateLogin: (value) => void; } ``` -- We will create an intermediate component (in our next sample we will port it to a generic Hoc). +- We will create an intermediate component (in our next sample we will port it to a generic HoC). _./src/pages/login/loginPage.tsx_ ```diff -+ export const LoginPageInner2 = (props) => -+ <> -+ -+ { -+ ({updateLogin}) => -+ -+ } -+ -+ -+ ++ export const LoginPageInner2 = (props) => ++ ++ { ++ ({updateLogin}) => ++ ++ } ++ - export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); + export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); - ``` -- Let's call the setLogin once the user clicks. +- Let's call the setLogin when the user clicks the button. _./src/pages/login/loginPage.tsx_ ```diff - onLogin = () => { - loginFormValidation.validateForm(this.state.loginInfo) - .then((formValidatinResult) => { - if(formValidatinResult.succeeded) { - if (isValidLogin(this.state.loginInfo)) { -+ this.props.updateLogin(this.state.loginInfo.login); - this.props.history.push('/pageB'); - } else { - this.setState({ showLoginFailedMsg: true }); - } +onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { ++ this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); } else { - alert('error, review the fields'); + this.setState({ showLoginFailedMsg: true }); } - }) - } + } else { + alert('error, review the fields'); + } + }); +} ``` - If we run the app we can check that now we get the right result. @@ -256,4 +259,4 @@ _./src/pages/login/loginPage.tsx_ npm start ``` -> If have to nest many render props, you can end up having a heavy nested component, in that case checkout react-composer micro library (https://github.com/jamesplease/react-composer) \ No newline at end of file +> If you have to nest many render props, you can end up having a heavy nested component, in that case checkout react-composer micro library (https://github.com/jamesplease/react-composer) \ No newline at end of file diff --git a/17 Context/readme_es.md b/17 Context/readme_es.md index d5f300a..0f460e3 100644 --- a/17 Context/readme_es.md +++ b/17 Context/readme_es.md @@ -1,41 +1,54 @@ # Intro -En esta muestra vamos a aprender como la api de context en React 16 trabaja. +En este ejemplo vamos a aprender cómo funciona la api de context en React 16. -Esto nos permitirÔ buscar información entre componentes sin tener que ir profundizando traves de las propiedades o tener que añadir la ayuda de redux a nuestro proyecto. +Esto nos permitirÔ compartir información entre componentes sin tener que ir añadiendo propiedades por todo el Ôrbol o tener que añadir redux a nuestro proyecto. + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estÔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en una terminal o consola. Las versiones anteriores pueden producir errores. + +Vamos a tomar como punto de partida el ejemplo _16 Validation_: ## Pasos para construirlo -- Queremos guardar el campo de _login_ una vez el usuario se logue y muestre la pÔgina B (o en culaquier pÔgina o componente que lo necesite), vamos a añadir un valor por defecto ('no user'). +- Queremos guardar el campo de _login_ una vez el usuario inicie sesión y mostrarlo en la pÔgina B (o en cualquier pÔgina o componente que lo necesite), vamos a añadir un valor por defecto ('no user'). -- Empezaremos por crear un contexto, lo llamaremos _sessionContext_, y añade los tipos apropiados +- Empezaremos por crear un contexto, lo llamaremos _sessionContext_, y añadiremos los tipos apropiados _./src/common/sessionContext.tsx_ ```javascript -import * as React from "react" +import * as React from "react"; export interface SessionContextProps { - login : string; -} -export const createDefaultUser = () : SessionContextProps => ({ + login: string; +} + +const createDefaultUser = (): SessionContextProps => ({ login: 'no user', }); export const SessionContext = React.createContext(createDefaultUser()); ``` -- Esta contexto de sesión expondrÔ un _provider_ (Nos servirÔ para establecer el nombre de inicio de sesión en el contexto), y un _consumer_ (Nos permitirÔ consumir el nombre de inicio de sesión del contexto desde cualquier punto de la aplicación). -Creamos un componente (lo nombraremos _SessionProvider) que en una mano guardarÔ en el estado el nombre del login y lo atarÔ a _SessionContext_ y en la otra acturÔ como una envoltura (normalmente estÔ al principio de la aplicación y envuelve la aplicación). +- Este contexto de sesión expondrÔ un _proveedor_ (que nos servirÔ para establecer el nombre de inicio de sesión en el contexto), y un _consumidor_ (que nos permitirÔ consumir el nombre de inicio de sesión del contexto en cualquier punto de la aplicación). +Crearemos un componente (lo llamaremos _SessionProvider_) que por un lado guardarÔ en el estado el nombre del login y lo atarÔ a _SessionContext_ y por otro lado acturÔ como un envoltorio (normalmente estÔ al principio de la aplicación y envuelve la aplicación). _./src/common/sessionContext.tsx_ ```diff -import * as React from "react" +import * as React from "react"; export interface SessionContextProps { - login : string; -} + login: string; +} + +const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', +}); export const SessionContext = React.createContext(createDefaultUser()); @@ -44,21 +57,22 @@ export const SessionContext = React.createContext(createDef + + export class SessionProvider extends React.Component<{}, State> { + -+ constructor(props) { -+ super(props); -+ this.state = createDefaultUser(); -+ } ++ constructor(props) { ++ super(props); ++ this.state = createDefaultUser(); ++ } + -+ render() { -+ return ( -+ -+ {this.props.children} -+ -+ )}; -+ }; ++ render() { ++ return ( ++ ++ {this.props.children} ++ ++ ); ++ } ++ } ``` -- Añade esto a un _index_ barrel común. +- Vamos a añadirlo a un _index_ barrel común. _./src/common/index.tsx_ @@ -67,7 +81,7 @@ export * from './notification'; + export * from './sessionContext'; ``` -- Es momento de exponer este provider al principio de nuestra aplicación. +- Es hora de exponer este proveedor al principio de nuestra aplicación. _./src/main.tsx_ @@ -78,7 +92,7 @@ import { HashRouter, Switch, Route } from 'react-router-dom'; import { LoginPage } from './pages/login'; import { PageB } from './pages/b'; import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; -+ import { SessionProvider } from './common' ++ import { SessionProvider } from './common'; const theme = createMuiTheme({ typography: { @@ -96,100 +110,93 @@ ReactDOM.render( + - - , document.getElementById('root') + , + document.getElementById('root') ); ``` -- En la pageB consumiremos el nombre de usuario de sessionContext. +- En la pÔgina B consumiremos el campo login de SessionContext. _./src/pages/b/pageB.tsx_ ```diff import * as React from "react" import { Link } from 'react-router-dom'; -+ import { SessionContext } from '../../common/' ++ import { SessionContext } from '../../common'; export const PageB = () => -
    -+ -+ { -+ ({login}) => ( -+ <> -

    Hello from page B

    -+
    -+

    Login: {login}

    -
    - Navigate to Login -+ -+ ) -+ } -+
    -
    +
    ++ ++ { ++ ({login}) => ( ++ <> +

    Hello from page B

    ++
    ++

    Login: {login}

    +
    + Navigate to Login ++ ++ ) ++ } ++
    +
    ``` -- Si ejecutamos la muestra podemos navegar a la pÔgina B y ver mostrado el nombre por defecto atado +- Si ejecutamos el ejemplo podemos navegar a la pÔgina B y ver el nombre por defecto. ```bash npm start ``` -- Mostrar un nombre por defecto no es una cosa mala, pero necesitamos mostrar el nombre de login real que ingresado por el usuario, para hacer esto expondremos una función dentro de nuestro contexto que permitirÔ a cualquier consumer actualizar el valor. - -_./src/common/sessionContext.tsx_ - -```diff -``` - -- Ahora aplicaremos esto en el loginPage. +- No es malo mostrar un nombre por defecto, pero necesitamos mostrar el nombre que introduzca el usuario, para hacer esto expondremos una función dentro de nuestro contexto que permitirÔ a cualquier consumidor actualizar el valor. -Primero añade un métodoo de actualización de login. +- Primero añadiremos un método para actualizar el login. -_./src/pages/login/loginPage.tsx_ +_./src/common/sessionContext.tsx_ ```diff export interface SessionContextProps { login: string; -+ updateLogin: (value) => void; ++ updateLogin: (value) => void; } -export const createDefaultUser = () : SessionContextProps => ({ +export const createDefaultUser = (): SessionContextProps => ({ login: 'no user', + updateLogin: (value) => {}, }); ``` -- Configuraremos esto en el provider del estado. +- Vamos a configurar esto en el estado del proveedor. ```diff export class SessionProvider extends React.Component<{}, State> { constructor(props) { super(props); -- this.state = createDefaultUser(); -+ this.state = { -+ login: createDefaulUser.login, -+ updateLogin: this.setLoginInfo -+ } +- this.state = createDefaultUser(); ++ this.state = { ++ login: createDefaulUser.login, ++ updateLogin: this.setLoginInfo, ++ } } -+ setLoginInfo = (newLogin) => { -+ this.setState({login: newLogin}) -+ } ++ setLoginInfo = (newLogin) => { ++ this.setState({login: newLogin}); ++ } render() { return ( {this.props.children} - ) - }; -}; + ); + } +} ``` -- Momento de configurar este valor cuando hacemos clic en el botón de inicio de sesión. +- Es hora de configurar este valor cuando hacemos clic en el botón de inicio de sesión. -- Añade un import a nuestro context. +- Vamos a añadir un import a nuestra pÔgina de inicio de sesión. _./src/pages/login/loginPage.tsx_ @@ -197,63 +204,59 @@ _./src/pages/login/loginPage.tsx_ + import { SessionContext } from '../../common'; ``` -- Actualiza nuestra propiedad del componente login para aceptar el método setLogin. +- Vamos a actualizar las propiedades del componente login para aceptar el método updateLogin. _./src/pages/login/loginPage.tsx_ ```diff interface Props extends RouteComponentProps, WithStyles { -+ updateLogin: (value) => void ++ updateLogin: (value) => void; } ``` -- Crearemos un componente intermédio (en nuestra siguiente muestra lo portaremos como un Hoc genérico). +- Vamos a crear un componente intermedio (en el siguiente ejemplo lo cambiaremos para que sea un HoC genérico). _./src/pages/login/loginPage.tsx_ ```diff -+ export const LoginPageInner2 = (props) => -+ <> -+ -+ { -+ ({updateLogin}) => -+ -+ } -+ -+ -+ ++ export const LoginPageInner2 = (props) => ++ ++ { ++ ({updateLogin}) => ++ ++ } ++ - export const LoginPage = withStyles(styles)(withRouter((LoginPageInner))); + export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); - ``` -- Llamemos al setLogin una vez el usuario pulse. +- Vamos a llamar al método setLogin cuando el usuario pulse el botón. _./src/pages/login/loginPage.tsx_ ```diff - onLogin = () => { - loginFormValidation.validateForm(this.state.loginInfo) - .then((formValidatinResult) => { - if(formValidatinResult.succeeded) { - if (isValidLogin(this.state.loginInfo)) { -+ this.props.updateLogin(this.state.loginInfo.login); - this.props.history.push('/pageB'); - } else { - this.setState({ showLoginFailedMsg: true }); - } +onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { ++ this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); } else { - alert('error, review the fields'); + this.setState({ showLoginFailedMsg: true }); } - }) - } + } else { + alert('error, review the fields'); + } + }); +} ``` -- Si ejecutamos la app podremos verificar que ahora nosotros tenemos el resultado correcto +- Si ejecutamos la aplicación podremos veremos el resultado adecuado. ```bash npm start ``` -> Si tenemos que anidar muchas render props, podemos terminar teniendo un componente anidado pesado, en ese caso revisa la micro librería react-composer (https://github.com/jamesplease/react-composer). \ No newline at end of file +> Si tenemos que anidar muchos render props, podemos acabar teniendo mucho anidamiento en un componente, en ese caso echa un vistazo a la librería react-composer (https://github.com/jamesplease/react-composer). \ No newline at end of file From 484f6eb6da323d9b58a393ca3bbdb58fd4d9540d Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 5 Nov 2018 08:27:04 +0100 Subject: [PATCH 133/180] Update readme.md --- 15_LoginForm/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/15_LoginForm/readme.md b/15_LoginForm/readme.md index 51ddab6..bfed34a 100644 --- a/15_LoginForm/readme.md +++ b/15_LoginForm/readme.md @@ -73,7 +73,7 @@ called _pages_ - In some cases this pages will contain more secondary files, let's create a simple _index.tsx_ file for each of this pages. -- Under _pages/login/index.ts. +- Under _pages/login/index.ts_. _./src/pages/login/index.ts_ @@ -842,4 +842,4 @@ https://github.com/mui-org/material-ui/issues/13144 https://codesandbox.io/s/zz6wnqklzm > And form validation? There are several libraries available, one that we had created in lemoncode is lc-form-validation we will create a sample including this lib to validate the login form -(required fields) \ No newline at end of file +(required fields) From 46af55df972a509cf62fd02314b6455e17f491b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 5 Nov 2018 21:23:28 +0100 Subject: [PATCH 134/180] remove as String --- 13_ShouldUpdate/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/13_ShouldUpdate/readme.md b/13_ShouldUpdate/readme.md index bbd285a..ec02ac5 100644 --- a/13_ShouldUpdate/readme.md +++ b/13_ShouldUpdate/readme.md @@ -228,7 +228,7 @@ export class App extends React.Component { + max="500" + value={this.state.satisfactionLevel} + onChange={(event : any) => this.setState( -+ {satisfactionLevel:event.target.value} as State)} ++ {satisfactionLevel:event.target.value})} + /> +
    + {this.state.satisfactionLevel} @@ -260,10 +260,10 @@ import * as React from 'react'; + } + const isRangeChange = (oldValue : number, newValue : number) => { -+ const oldValueClass = setSatisfactionClass(oldValue); ++ const oldValueClass = setSatisfactionClass(oldValue); + const newValueClass = setSatisfactionClass(newValue); + -+ return oldValueClass !== newValueClass; ++ return oldValueClass !== newValueClass; + } + export class FaceComponent extends React.Component { From 936ab30828993cbec57890df3facaf91ddfd6241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 5 Nov 2018 21:35:01 +0100 Subject: [PATCH 135/180] remove as state --- 13_ShouldUpdate/readme_es.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/13_ShouldUpdate/readme_es.md b/13_ShouldUpdate/readme_es.md index 5bebaed..7b3fbae 100644 --- a/13_ShouldUpdate/readme_es.md +++ b/13_ShouldUpdate/readme_es.md @@ -21,7 +21,7 @@ Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no est > Verificar que tienes al menos corriendo la versión de node v6.x.x y npm 3.x.x ejecutando `node -v` y `npm -v` en la terminal de Windows. Versiones mÔs antiguas pueden producir errores. -## Pasos para construirlo +## Pasos para construirlo - Copia el contenido de _03 State_ y ejecútalo: @@ -190,7 +190,7 @@ export const FaceComponent = (props : {level : number}) => { +
    ); } -``` +``` - En _app.tsx_ añadamos un estado que mantenga el grado de satisfacción actual mÔs un control deslizante para que el usuario lo actualize. @@ -222,7 +222,7 @@ export class App extends React.Component { + max="500" + value={this.state.satisfactionLevel} + onChange={(event : any) => this.setState( -+ {satisfactionLevel:event.target.value} as State)} ++ {satisfactionLevel:event.target.value})} + /> +
    + {this.state.satisfactionLevel} @@ -242,7 +242,7 @@ export class App extends React.Component { ``` - Añadamos una optimización de renderización, nosotros deberíamos solo lanzar el renderizado cuando el nivel de satisfacción cambie, necesitamos mover el componente a un componente estado: - + _./src/face.tsx_ ```diff @@ -253,10 +253,10 @@ import * as React from 'react'; + } + const isRangeChange = (oldValue : number, newValue : number) => { -+ const oldValueClass = setSatisfactionClass(oldValue); ++ const oldValueClass = setSatisfactionClass(oldValue); + const newValueClass = setSatisfactionClass(newValue); + -+ return oldValueClass !== newValueClass; ++ return oldValueClass !== newValueClass; + } + export class FaceComponent extends React.Component { From 5581747db69338e6e91bb2fd8336787773a170fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Mon, 5 Nov 2018 21:46:12 +0100 Subject: [PATCH 136/180] fix errors --- 14_ReactRouter/readme.md | 12 ++++++------ 14_ReactRouter/readme_es.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/14_ReactRouter/readme.md b/14_ReactRouter/readme.md index 2b9ad23..00b410f 100644 --- a/14_ReactRouter/readme.md +++ b/14_ReactRouter/readme.md @@ -33,7 +33,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Let's create a component called _PageA_ as _src/pageA.tsx_: -## ./src/pageA.tsx +_./src/pageA.tsx_ ```jsx import * as React from "react" @@ -46,7 +46,7 @@ export const PageA = () => - Let's create a component called _PageB_ as _src/pageB.tsx_: -### ./src/pageB.tsx +_./src/pageB.tsx_ ```jsx import * as React from "react" @@ -61,12 +61,12 @@ export const PageB = () => ```bash npm install react-router-dom --save -npm install @types/react-router-dom --save-dev +npm install @types/react-router-dom --save-dev ``` - Let's define the routing in _main.tsx_: -## ./src/main.tsx +_./src/main.tsx_ ```diff import * as React from 'react'; @@ -98,7 +98,7 @@ npm start - Let's define a navigation from _[PageA.tsx](./src/pageA.tsx)_ to _[PageB.tsx](./src/pageB.tsx)_. -## ./src/pageA.tsx +_./src/pageA.tsx_ ```diff import * as React from "react" @@ -114,7 +114,7 @@ export const PageA = () => - Let's define a navigation from _[PageB.tsx](./src/pageB.tsx)_ to _[PageA.tsx](./src/pageA.tsx)_ -## ./src/pageB.tsx +_./src/pageB.tsx_ ```diff import * as React from "react" diff --git a/14_ReactRouter/readme_es.md b/14_ReactRouter/readme_es.md index 630a36e..4725b7c 100644 --- a/14_ReactRouter/readme_es.md +++ b/14_ReactRouter/readme_es.md @@ -33,7 +33,7 @@ Copia el contenido de _[03 State](./../03%20State)_ y ejecuta: - Vamos a crear un componente llamado _PageA_ como _src/pageA.tsx_: -## ./src/pageA.tsx +_./src/pageA.tsx_ ```jsx import * as React from "react" @@ -46,7 +46,7 @@ export const PageA = () => - Vamos a crear un componente llamado _PageB_ como _src/pageB.tsx_: -### ./src/pageB.tsx +_./src/pageB.tsx_ ```jsx import * as React from "react" @@ -61,12 +61,12 @@ export const PageB = () => ```bash npm install react-router-dom --save -npm install @types/react-router-dom --save-dev +npm install @types/react-router-dom --save-dev ``` - Vamos a definir el enrutado en _main.tsx_: -## ./src/main.tsx +_./src/main.tsx_ ```diff import * as React from 'react'; @@ -97,7 +97,7 @@ npm start - Vamos a definir la navegación de _[PageA.tsx](./src/pageA.tsx)_ a _[PageB.tsx](./src/pageB.tsx)_. -## ./src/pageA.tsx +_./src/pageA.tsx_ ```diff import * as React from "react" @@ -113,7 +113,7 @@ export const PageA = () => - Vamos a definir la navegación de _[PageB.tsx](./src/pageB.tsx)_ a _[PageA.tsx](./src/pageA.tsx)_ -## ./src/pageB.tsx +_./src/pageB.tsx_ ```diff import * as React from "react" From 04e935e46f8f4bf09a6617bc777808ec75149c3e Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 7 Nov 2018 15:48:56 +0100 Subject: [PATCH 137/180] render props --- 18 Hoc/src/pages/b/pageB.tsx | 32 ++-- 19 RenderProps/.babelrc | 10 ++ 19 RenderProps/Readme.md | 150 ++++++++++++++++++ 19 RenderProps/package.json | 42 +++++ 19 RenderProps/src/api/login.ts | 5 + .../src/common/forms/textFieldForm.tsx | 42 +++++ 19 RenderProps/src/common/index.tsx | 2 + 19 RenderProps/src/common/notification.tsx | 53 +++++++ 19 RenderProps/src/common/sessionContext.tsx | 62 ++++++++ 19 RenderProps/src/hello.tsx | 7 + 19 RenderProps/src/index.html | 14 ++ 19 RenderProps/src/main.tsx | 27 ++++ 19 RenderProps/src/model/login.ts | 9 ++ 19 RenderProps/src/nameEdit.tsx | 12 ++ 19 RenderProps/src/pages/b/index.ts | 1 + 19 RenderProps/src/pages/b/pageB.tsx | 31 ++++ 19 RenderProps/src/pages/login/index.ts | 1 + 19 RenderProps/src/pages/login/loginForm.tsx | 43 +++++ 19 RenderProps/src/pages/login/loginPage.tsx | 120 ++++++++++++++ .../src/pages/login/loginValidations.ts | 16 ++ 19 RenderProps/src/pages/login/viewmodel.ts | 11 ++ 19 RenderProps/tsconfig.json | 17 ++ 19 RenderProps/webpack.config.js | 64 ++++++++ 23 files changed, 754 insertions(+), 17 deletions(-) create mode 100644 19 RenderProps/.babelrc create mode 100644 19 RenderProps/Readme.md create mode 100644 19 RenderProps/package.json create mode 100644 19 RenderProps/src/api/login.ts create mode 100644 19 RenderProps/src/common/forms/textFieldForm.tsx create mode 100644 19 RenderProps/src/common/index.tsx create mode 100644 19 RenderProps/src/common/notification.tsx create mode 100644 19 RenderProps/src/common/sessionContext.tsx create mode 100644 19 RenderProps/src/hello.tsx create mode 100644 19 RenderProps/src/index.html create mode 100644 19 RenderProps/src/main.tsx create mode 100644 19 RenderProps/src/model/login.ts create mode 100644 19 RenderProps/src/nameEdit.tsx create mode 100644 19 RenderProps/src/pages/b/index.ts create mode 100644 19 RenderProps/src/pages/b/pageB.tsx create mode 100644 19 RenderProps/src/pages/login/index.ts create mode 100644 19 RenderProps/src/pages/login/loginForm.tsx create mode 100644 19 RenderProps/src/pages/login/loginPage.tsx create mode 100644 19 RenderProps/src/pages/login/loginValidations.ts create mode 100644 19 RenderProps/src/pages/login/viewmodel.ts create mode 100644 19 RenderProps/tsconfig.json create mode 100644 19 RenderProps/webpack.config.js diff --git a/18 Hoc/src/pages/b/pageB.tsx b/18 Hoc/src/pages/b/pageB.tsx index 5606910..1c63c13 100644 --- a/18 Hoc/src/pages/b/pageB.tsx +++ b/18 Hoc/src/pages/b/pageB.tsx @@ -1,21 +1,19 @@ import * as React from "react" import { Link } from 'react-router-dom'; -import { SessionContext } from '../../common/' +import { SessionContext, withSessionContext } from '../../common/' -export const PageB = () => -
    - - { - ({ login }) => ( - <> -

    Hello from page B

    -
    -
    -

    Login: {login}

    +interface Props { + login : string; +} - Navigate to Login - - ) - } -
    -
    +const PageBInner = (props : Props) => + <> +

    Hello from page B

    +
    +
    +

    Login: {props.login}

    + + Navigate to Login + + +export const PageB = withSessionContext(PageBInner); \ No newline at end of file diff --git a/19 RenderProps/.babelrc b/19 RenderProps/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/19 RenderProps/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/19 RenderProps/Readme.md b/19 RenderProps/Readme.md new file mode 100644 index 0000000..ce756f3 --- /dev/null +++ b/19 RenderProps/Readme.md @@ -0,0 +1,150 @@ +# Intro + +In this sample we are going to learn how use render props in React. + +In this case we will implement a component that will inject the session +to other components via render props. + +The main advantage of using this approach instead of HOC is that the child component gets a clear contract of the props it receives. + +# Steps + +- Let's first add the component that will expose the render prop, we will append it to +the sessionContext file. + +_./src/common/sessionContext.tsx_ + +```diff +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + ++ interface Props { ++ render : (login : string) => React.ReactNode; ++ } ++ ++ export class Session extends React.Component { ++ constructor(props : Props) { ++ super(props); ++ } ++ ++ render() { ++ return ( ++ ++ { ++ ({ login, updateLogin }) => ++ <> ++ {this.props.render(login)} ++ ++ } ++ ++ ) ++ } ++ } +``` + +- Now in the _pageB.tsx_ we can invoke it like that (first approach): + +_./src/pages/b/pageB.tsx_ + +```jsx +import * as React from "react" +import { Link } from 'react-router-dom'; +import { Session } from '../../common/' +import { checkPropTypes } from "prop-types"; + +export const PageB = () => +
    + ( + <> +

    Hello from page B

    +
    +
    +

    Login: {login}

    + + Navigate to Login + + )} + > +
    +
    +``` + +- Let's add one refactor to make the code more readable: + +_./src/pages/b/pageB.tsx_ + +```jsx +import * as React from "react" +import { Link } from 'react-router-dom'; +import { Session } from '../../common/' +import { checkPropTypes } from "prop-types"; + + +interface Props { + login : string; +} + +const LoginComponent = (props: Props) => + <> +

    Hello from page B

    +
    +
    +

    Login: {props.login}

    + + Navigate to Login + + + +export const PageB = () => +
    + ( + + )} + > + +
    +``` + +> If you need to nest several render props, you can use _react-composer_: https://github.com/jamesplease/react-composer + diff --git a/19 RenderProps/package.json b/19 RenderProps/package.json new file mode 100644 index 0000000..be1e062 --- /dev/null +++ b/19 RenderProps/package.json @@ -0,0 +1,42 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@material-ui/core": "^3.2.0", + "@material-ui/icons": "^3.0.1", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "@types/react-router-dom": "^4.3.1", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "lc-form-validation": "^2.0.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-router-dom": "^4.3.1" + } +} diff --git a/19 RenderProps/src/api/login.ts b/19 RenderProps/src/api/login.ts new file mode 100644 index 0000000..1f7d4f3 --- /dev/null +++ b/19 RenderProps/src/api/login.ts @@ -0,0 +1,5 @@ +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); diff --git a/19 RenderProps/src/common/forms/textFieldForm.tsx b/19 RenderProps/src/common/forms/textFieldForm.tsx new file mode 100644 index 0000000..808091c --- /dev/null +++ b/19 RenderProps/src/common/forms/textFieldForm.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography/Typography"; + +interface Props { + name: string; + label: string; + onChange: any; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text', +} + +const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { + onChange(fieldId, e.target.value); +} + +export const TextFieldForm : React.StatelessComponent = (props) => { + const {name, label, onChange, value, error, type} = props; + + return ( + <> + + + {props.error} + + + ) +} diff --git a/19 RenderProps/src/common/index.tsx b/19 RenderProps/src/common/index.tsx new file mode 100644 index 0000000..8d8eeb0 --- /dev/null +++ b/19 RenderProps/src/common/index.tsx @@ -0,0 +1,2 @@ +export * from './notification'; +export * from './sessionContext'; \ No newline at end of file diff --git a/19 RenderProps/src/common/notification.tsx b/19 RenderProps/src/common/notification.tsx new file mode 100644 index 0000000..5396faf --- /dev/null +++ b/19 RenderProps/src/common/notification.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; + +interface Props extends WithStyles { + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => createStyles({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const { classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); diff --git a/19 RenderProps/src/common/sessionContext.tsx b/19 RenderProps/src/common/sessionContext.tsx new file mode 100644 index 0000000..f83ed61 --- /dev/null +++ b/19 RenderProps/src/common/sessionContext.tsx @@ -0,0 +1,62 @@ +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + +interface Props { + render : (login : string) => React.ReactNode; +} + +export class Session extends React.Component { + constructor(props : Props) { + super(props); + } + + render() { + return ( + + { + ({ login, updateLogin }) => + <> + {this.props.render(login)} + + } + + ) + } +} diff --git a/19 RenderProps/src/hello.tsx b/19 RenderProps/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/19 RenderProps/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/19 RenderProps/src/index.html b/19 RenderProps/src/index.html new file mode 100644 index 0000000..0fcc01e --- /dev/null +++ b/19 RenderProps/src/index.html @@ -0,0 +1,14 @@ + + + + + + + + + +
    +
    +
    + + diff --git a/19 RenderProps/src/main.tsx b/19 RenderProps/src/main.tsx new file mode 100644 index 0000000..247c55f --- /dev/null +++ b/19 RenderProps/src/main.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; +import { SessionProvider } from './common'; + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); + +ReactDOM.render( + + + + + + + + + + + , document.getElementById('root') +); diff --git a/19 RenderProps/src/model/login.ts b/19 RenderProps/src/model/login.ts new file mode 100644 index 0000000..081e6fb --- /dev/null +++ b/19 RenderProps/src/model/login.ts @@ -0,0 +1,9 @@ +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); diff --git a/19 RenderProps/src/nameEdit.tsx b/19 RenderProps/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/19 RenderProps/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/19 RenderProps/src/pages/b/index.ts b/19 RenderProps/src/pages/b/index.ts new file mode 100644 index 0000000..913631b --- /dev/null +++ b/19 RenderProps/src/pages/b/index.ts @@ -0,0 +1 @@ +export {PageB} from './pageB'; \ No newline at end of file diff --git a/19 RenderProps/src/pages/b/pageB.tsx b/19 RenderProps/src/pages/b/pageB.tsx new file mode 100644 index 0000000..1f2258d --- /dev/null +++ b/19 RenderProps/src/pages/b/pageB.tsx @@ -0,0 +1,31 @@ +import * as React from "react" +import { Link } from 'react-router-dom'; +import { Session } from '../../common/' +import { checkPropTypes } from "prop-types"; + + +interface Props { + login : string; +} + +const LoginComponent = (props: Props) => + <> +

    Hello from page B

    +
    +
    +

    Login: {props.login}

    + + Navigate to Login + + + +export const PageB = () => +
    + ( + + )} + > + +
    diff --git a/19 RenderProps/src/pages/login/index.ts b/19 RenderProps/src/pages/login/index.ts new file mode 100644 index 0000000..50d85bb --- /dev/null +++ b/19 RenderProps/src/pages/login/index.ts @@ -0,0 +1 @@ +export {LoginPage} from './loginPage'; \ No newline at end of file diff --git a/19 RenderProps/src/pages/login/loginForm.tsx b/19 RenderProps/src/pages/login/loginForm.tsx new file mode 100644 index 0000000..eda83bd --- /dev/null +++ b/19 RenderProps/src/pages/login/loginForm.tsx @@ -0,0 +1,43 @@ +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { LoginEntity } from "../../model/login"; +import {LoginFormErrors} from './viewmodel'; +import { TextFieldForm } from '../../common/forms/textFieldForm'; + +interface Props { + onLogin: () => void; + onUpdateField: (string, any) => void; + loginInfo : LoginEntity; + loginFormErrors : LoginFormErrors; +} + +export const LoginForm = (props: Props) => { + const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + + const onTexFieldChange = (fieldId) => (e) => { + onUpdateField(fieldId, e.target.value); + } + + return ( +
    + + + +
    + ) +} diff --git a/19 RenderProps/src/pages/login/loginPage.tsx b/19 RenderProps/src/pages/login/loginPage.tsx new file mode 100644 index 0000000..5742172 --- /dev/null +++ b/19 RenderProps/src/pages/login/loginPage.tsx @@ -0,0 +1,120 @@ +import * as React from "react" +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; +import { NotificationComponent } from '../../common' +import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; +import { loginFormValidation } from './loginValidations'; +import {SessionContext} from '../../common'; +import { Session } from "inspector"; + +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + + +interface State { + loginInfo: LoginEntity; + showLoginFailedMsg: boolean; + loginFormErrors: LoginFormErrors; +} + +interface Props extends RouteComponentProps, WithStyles { + updateLogin: (value) => void +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { + loginInfo: createEmptyLogin(), + showLoginFailedMsg: false, + loginFormErrors: createDefaultLoginFormErrors(), + } + } + + + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } + + onUpdateLoginField = (name: string, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + + loginFormValidation.validateField(this.state.loginInfo, name, value).then( + (fieldValidationResult) => { + this.setState({ + loginFormErrors: { + ...this.state.loginFormErrors, + [name]: fieldValidationResult, + } + }); + } + ); + } + + render() { + const { classes } = this.props; + return ( + <> + + + + + + + this.setState({ showLoginFailedMsg: false })} + /> + + ) + + } +} + +export const LoginPageInner2 = (props) => + <> + + { + ({updateLogin}) => + + } + + + + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); diff --git a/19 RenderProps/src/pages/login/loginValidations.ts b/19 RenderProps/src/pages/login/loginValidations.ts new file mode 100644 index 0000000..d0eee27 --- /dev/null +++ b/19 RenderProps/src/pages/login/loginValidations.ts @@ -0,0 +1,16 @@ +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const loginFormValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const loginFormValidation = createFormValidation(loginFormValidationConstraints); diff --git a/19 RenderProps/src/pages/login/viewmodel.ts b/19 RenderProps/src/pages/login/viewmodel.ts new file mode 100644 index 0000000..b374fa6 --- /dev/null +++ b/19 RenderProps/src/pages/login/viewmodel.ts @@ -0,0 +1,11 @@ +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} + +export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ + login: new FieldValidationResult(), + password: new FieldValidationResult(), +}); diff --git a/19 RenderProps/tsconfig.json b/19 RenderProps/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/19 RenderProps/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/19 RenderProps/webpack.config.js b/19 RenderProps/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/19 RenderProps/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From fbabff66d03826b5750fe8c0748ded3bc8173c51 Mon Sep 17 00:00:00 2001 From: Braulio Date: Wed, 7 Nov 2018 16:48:28 +0100 Subject: [PATCH 138/180] Error Boundary sample completed --- 20 ErrorBoundaries/.babelrc | 10 ++ 20 ErrorBoundaries/package.json | 37 ++++ 20 ErrorBoundaries/readme.md | 170 ++++++++++++++++++ 20 ErrorBoundaries/readme_es.md | 193 +++++++++++++++++++++ 20 ErrorBoundaries/src/app.tsx | 37 ++++ 20 ErrorBoundaries/src/errorBoundary.tsx | 29 ++++ 20 ErrorBoundaries/src/faultyComponent.tsx | 13 ++ 20 ErrorBoundaries/src/hello.tsx | 7 + 20 ErrorBoundaries/src/index.html | 13 ++ 20 ErrorBoundaries/src/main.tsx | 10 ++ 20 ErrorBoundaries/src/nameEdit.tsx | 12 ++ 20 ErrorBoundaries/tsconfig.json | 17 ++ 20 ErrorBoundaries/webpack.config.js | 64 +++++++ 13 files changed, 612 insertions(+) create mode 100644 20 ErrorBoundaries/.babelrc create mode 100644 20 ErrorBoundaries/package.json create mode 100644 20 ErrorBoundaries/readme.md create mode 100644 20 ErrorBoundaries/readme_es.md create mode 100644 20 ErrorBoundaries/src/app.tsx create mode 100644 20 ErrorBoundaries/src/errorBoundary.tsx create mode 100644 20 ErrorBoundaries/src/faultyComponent.tsx create mode 100644 20 ErrorBoundaries/src/hello.tsx create mode 100644 20 ErrorBoundaries/src/index.html create mode 100644 20 ErrorBoundaries/src/main.tsx create mode 100644 20 ErrorBoundaries/src/nameEdit.tsx create mode 100644 20 ErrorBoundaries/tsconfig.json create mode 100644 20 ErrorBoundaries/webpack.config.js diff --git a/20 ErrorBoundaries/.babelrc b/20 ErrorBoundaries/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/20 ErrorBoundaries/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/20 ErrorBoundaries/package.json b/20 ErrorBoundaries/package.json new file mode 100644 index 0000000..63b6302 --- /dev/null +++ b/20 ErrorBoundaries/package.json @@ -0,0 +1,37 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} diff --git a/20 ErrorBoundaries/readme.md b/20 ErrorBoundaries/readme.md new file mode 100644 index 0000000..c6dafd9 --- /dev/null +++ b/20 ErrorBoundaries/readme.md @@ -0,0 +1,170 @@ +# 20 Error Boundaries + +In this example we will play with the Error Boundary concept. + + +## Summary steps: + + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Copy the content from _03 State_ and execute `npm install`. + +- Let's create a faulty component: + +_./src/faultyComponent.tsx_ + +```jsx +import * as React from 'react'; + +export class FaultyComponent extends React.Component { + componentDidMount() { + throw "I'm the faulty component, generating a bad crash." + } + + render() { + return ( +

    Hello from Faulty Component

    + ) + } +} +``` + +- Let's instantiate this component in our _app.tsx_ + +_./src/app.tsx_ + +```diff +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; ++ import { FaultyComponent } from './faultyComponent'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + ++ + + ); + } +} +``` + +- Let's run the app. + +```bash +npm start +``` + +- If you open the console you will see a bad crash being reported, that's something that you wouldn't like to suffer when you are using plugins and other components that you may not trust, why not wrap any error in a safe area +and display a friendly component failed to load in case of an uncontroller error happen in that area (and keep the rest +of application working as expected). Let's create an Error Boundary. + +_./src/erroBoundary.tsx_ + +```jsx +import * as React from 'React'; + +export class ErrorBoundary extends React.Component { + state = { error: null, errorInfo: null }; + + componentDidCatch(error, errorInfo) { + this.setState({ + error: error, + errorInfo: errorInfo + }); + } + + render() { + if (this.state.errorInfo) { + return ( +
    +

    Plugin Failed to load, optional error info:

    +
    + {this.state.error && this.state.error.toString()} +
    + {this.state.errorInfo.componentStack} +
    +
    + ); + } + + return this.props.children; + } +} +``` + +- And let's wrap our faultyComponent inside this error boundary (we could wrap a set of components if needed). + +_./src/app.tsx_ + +```diff +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; +import { FaultyComponent } from './faultyComponent'; ++ import { ErrorBoundary } from './errorBoundary'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + ++ + ++ + + ); + } +} +``` + +> There's a nice generic wrapper for this ErrorBoundary: https://github.com/bvaughn/react-error-boundary + +> Error boundaries and event handlers: https://github.com/facebook/react/issues/11409 \ No newline at end of file diff --git a/20 ErrorBoundaries/readme_es.md b/20 ErrorBoundaries/readme_es.md new file mode 100644 index 0000000..3d737a3 --- /dev/null +++ b/20 ErrorBoundaries/readme_es.md @@ -0,0 +1,193 @@ +# 03 Estado + +En este ejemplo vamos a introducir un concepto bÔsico de React: manejando estados. + +En este escenario nosotros proveeremos un username por defecto y luego lo actualizaremos. + +Tomaremos como punto de entrada el ejemplo _02 Properties_: + +## Pasos resumidos: + +- Crear un componente _App_ que contendrÔ el estado. Este estado contendrÔ el username actual (con valor por defecto "defaultUserName"). +Este componente _App_ renderizarÔ el component _Hello_. Primero nosotros crearemos un _App_ componente simple sin estado. +- Actualizar el fichero _main.tsx_ para incluir nuestro componente _App_. +- Cambiar el componente _App_ a un componente clase con estado donde contendremos el estado _userName_. +- Crear un componente _NameEdit_ para cambiar el username. Esto cambiara el estado de _App_ usando una función de _App_. +- Verificar que todo funciona correctamente. + +## Requisitos previos + +Instala [Node.js y npm](https://nodejs.org) +si no lo tenías aún instalado en tu maquina. + +> Verifica que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copia el contenido de _02 Properties_ y ejecuta `npm install`. + +- Vamos a crear un componente _App_ bajo un fichero llamado _app.tsx_ (este componente mostrarÔ el componente _Hello_). + +_./src/app.tsx_ + +```jsx +import * as React from 'react'; +import { HelloComponent } from './hello'; + +export const App = () => { + return ( + + ); +} +``` + +- Vamos a actualizar _main.tsx_ para usar el componente _App_ que acabamos de crear. + +_./src/main.tsx_ + +```diff + import * as React from 'react'; + import * as ReactDOM from 'react-dom'; ++ import { App } from './app'; + +- import { HelloComponent } from './hello'; + + ReactDOM.render( +- , ++ , + document.getElementById('root') + ); +``` + +- Ahora podemos verificar que sigue funcionando como esperamos. + + ``` + npm start + ``` + + Es hora de revisitar _app.tsx_. Nosotros queremos guardar el nombre del usuario y luego actualizarlo. Vamos a mover esta clase componente con estado y define un estado incluyendo _userName_, y pasamos este valor al componente _Hello_. + + _./src/app.tsx_ + +```jsx +import * as React from 'react'; +import {HelloComponent} from './hello'; + +interface Props { +} + +interface State { + userName : string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = {userName: 'defaultUserName'}; + } + + public render() { + return ( + + ); + } +} +``` + +- De nuevo, podemos comprobar que todo funciona correctamente tal y como esperamos. + + ``` + npm start + ``` + + - Es hora de crear un componente _NameEdit_. Este componente permitirÔ al usuario actualizar su username y notificar con un callback al control del padre cuando el valor de _userName_ actualize. + + _./src/nameEdit.tsx_ + +```jsx +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + +``` + +Nota aclaratoria: ¿ Que es este framento <> ? Una manera de crear un componente que tiene múltiples elementos (no un único padre). Disponible desde React 16.2. Como una manera alternativa puedes escribir: + +```jsx + ... + export const NameEditComponent = (props : Props) => + + + + +} +``` + +- En el fichero _app.tsx_, vamos a añadir una función para remplazar el valor del estado de _userName_ con el valor nuevo. + +```diff + import * as React from 'react'; + import {HelloComponent} from './hello'; ++ import { NameEditComponent } from './nameEdit'; + + interface Props { + } + + interface State { + userName : string; + } + + export class App extends React.Component { + constructor(props : Props) { + super(props); + + this.state = {userName: 'defaultUserName'}; + } + ++ setUsernameState = (event) => { ++ this.setState({userName: event.target.value}); ++ } + + public render() { + return ( ++ <> + ++ ++ + ); + } + } +``` + +Nota aclarativa: la intención de usar la función flecha. Esto evita perder el contexto de _this_ en el callback. + +Nota aclarativa 2: Este this.setState() cambiarÔ el valor del estado en algún punto en el futuro. No consideres que esto es un cambio asíncrono - no lo es. La lógica de escritura que depende de que el nuevo valor del nombre de usuario esté en el estado justo después de llamar a this.setState() es incorrecta y puede fallar. Si necesitas escribir código dependiendo de que el nuevo valor esté en el estado, usa un callback como segundo parÔmetro de this.setState(). Mira este ejemplo + +``` + setUserNameState = (newName: string) => { + this.setState({userName: newName}, this.nameChanged); + } + + nameChanged() { + /* logic here gets invoked after the new name value in the state is set. */ + } +``` + +- Finalmente vamos a comprobar que todo funciona una vez mÔs. + + ``` + npm start + ``` \ No newline at end of file diff --git a/20 ErrorBoundaries/src/app.tsx b/20 ErrorBoundaries/src/app.tsx new file mode 100644 index 0000000..7f42f20 --- /dev/null +++ b/20 ErrorBoundaries/src/app.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; +import { FaultyComponent } from './faultyComponent'; +import { ErrorBoundary } from './errorBoundary'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + + + + + + ); + } +} \ No newline at end of file diff --git a/20 ErrorBoundaries/src/errorBoundary.tsx b/20 ErrorBoundaries/src/errorBoundary.tsx new file mode 100644 index 0000000..4658af0 --- /dev/null +++ b/20 ErrorBoundaries/src/errorBoundary.tsx @@ -0,0 +1,29 @@ +import * as React from 'React'; + +export class ErrorBoundary extends React.Component { + state = { error: null, errorInfo: null }; + + componentDidCatch(error, errorInfo) { + this.setState({ + error: error, + errorInfo: errorInfo + }); + } + + render() { + if (this.state.errorInfo) { + return ( +
    +

    Plugin Failed to load, optional error info:

    +
    + {this.state.error && this.state.error.toString()} +
    + {this.state.errorInfo.componentStack} +
    +
    + ); + } + + return this.props.children; + } +} \ No newline at end of file diff --git a/20 ErrorBoundaries/src/faultyComponent.tsx b/20 ErrorBoundaries/src/faultyComponent.tsx new file mode 100644 index 0000000..4d4f7ed --- /dev/null +++ b/20 ErrorBoundaries/src/faultyComponent.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; + +export class FaultyComponent extends React.Component { + componentDidMount() { + throw "I'm the faulty component, generating a bad crash." + } + + render() { + return ( +

    Hello from Faulty Component

    + ) + } +} \ No newline at end of file diff --git a/20 ErrorBoundaries/src/hello.tsx b/20 ErrorBoundaries/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/20 ErrorBoundaries/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/20 ErrorBoundaries/src/index.html b/20 ErrorBoundaries/src/index.html new file mode 100644 index 0000000..b0b7d25 --- /dev/null +++ b/20 ErrorBoundaries/src/index.html @@ -0,0 +1,13 @@ + + + + + + + +
    +

    Sample app

    +
    +
    + + diff --git a/20 ErrorBoundaries/src/main.tsx b/20 ErrorBoundaries/src/main.tsx new file mode 100644 index 0000000..be3985e --- /dev/null +++ b/20 ErrorBoundaries/src/main.tsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import {App} from './app'; + +import { HelloComponent } from './hello'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/20 ErrorBoundaries/src/nameEdit.tsx b/20 ErrorBoundaries/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/20 ErrorBoundaries/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/20 ErrorBoundaries/tsconfig.json b/20 ErrorBoundaries/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/20 ErrorBoundaries/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/20 ErrorBoundaries/webpack.config.js b/20 ErrorBoundaries/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/20 ErrorBoundaries/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From 0ee2d57bc685ecbcee5c7c639d390f1c73c4a945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Thu, 8 Nov 2018 21:43:36 +0100 Subject: [PATCH 139/180] Fix readme --- 17 Context/Readme.md | 2 +- 17 Context/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/17 Context/Readme.md b/17 Context/Readme.md index 714ff8e..995a295 100644 --- a/17 Context/Readme.md +++ b/17 Context/Readme.md @@ -175,7 +175,7 @@ export class SessionProvider extends React.Component<{}, State> { super(props); - this.state = createDefaultUser(); + this.state = { -+ login: createDefaulUser.login, ++ login: createDefaultUser().login, + updateLogin: this.setLoginInfo, + } } diff --git a/17 Context/readme_es.md b/17 Context/readme_es.md index 0f460e3..3daf082 100644 --- a/17 Context/readme_es.md +++ b/17 Context/readme_es.md @@ -175,7 +175,7 @@ export class SessionProvider extends React.Component<{}, State> { super(props); - this.state = createDefaultUser(); + this.state = { -+ login: createDefaulUser.login, ++ login: createDefaultUser().login, + updateLogin: this.setLoginInfo, + } } From 1f9cc532c03569c82d41026b651d6def9223ad70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 9 Nov 2018 19:37:12 +0100 Subject: [PATCH 140/180] Create spanish readme and fix errors to english version --- 18 Hoc/Readme.md | 28 +++++++++-------- 18 Hoc/readme_es.md | 74 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 18 Hoc/readme_es.md diff --git a/18 Hoc/Readme.md b/18 Hoc/Readme.md index 3119a9e..f2a7c65 100644 --- a/18 Hoc/Readme.md +++ b/18 Hoc/Readme.md @@ -1,17 +1,22 @@ -# Hoc +## Hoc -We are going to implement a High Order Component, this let us extract common functionallity and expose -it via composition. +We are going to implement a High Order Component,this let us extract common functionallity and expose it via composition. -# Steps +We will take a startup point sample _17 Context_: -In the previous sample we had to create an intermediate _LoginPageInner2_ component in order to -inject to the component the _loginInfo_ and _setLoginInfo_ fields from the Session context. +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +In the previous sample we had to create an intermediate _LoginPageInner2_ component in order to inject to the component the _loginInfo_ and _setLoginInfo_ fields from the Session context. This boilerplate is a bit ugly, and it would be worse if we want to access that info from other pages. -By implementing an Hoc we can just create a reusable function that will make easier to access the -SessionContext consumer. +By implementing an Hoc we can just create a reusable function that will make easier to access the SessionContext consumer. - We will start by creating our Hoc (let's add this at the bottom of the file). @@ -47,20 +52,19 @@ _./src/pages/login/loginPage.tsx_ _./src/pages/login/loginPage.tsx_ ```diff -- export const LoginPageInner2 = (props) => +- export const LoginPageInner2 = (props) => - <> - - { -- ({updateLogin}) => +- ({updateLogin}) => - - } -- +- - - - export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); + export const LoginPage = withSessionContext(withStyles(styles)(withRouter((LoginPageInner)))); - ``` - We can run and check that the sample is working. diff --git a/18 Hoc/readme_es.md b/18 Hoc/readme_es.md new file mode 100644 index 0000000..d815d33 --- /dev/null +++ b/18 Hoc/readme_es.md @@ -0,0 +1,74 @@ +## Hoc + +Vamos a implementar a High Order Component, esto permitirÔ extraer funcionalidad común y exponerla via composición. + +Vamos a tomar como punto de partida el ejemplo _17 Context_: + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estÔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en una terminal o consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +En el ejemplo anterior tuvimos que crear un componente _LoginPageInner2_ intermedio para inyectar al componente los campos _loginInfo_ y _setLoginInfo_ de la sesión de contexto. + +Este boilerplate es un poco feo, y sería peor si queremos acceder a esa información desde otras pÔginas. + +Al implementar un Hoc podemos crear una función reusable que facilitarÔ el acceso al consumidor SessionContext. + +- Empezaremospor crear nuestro Hoc (vamos a añadir esto a abajo del fichero). + +_./src/common/sessionContext.tsx_ + +```javascript +export const withSessionContext = (Component) => (props) => ( + + { + ({ login, updateLogin }) => ( + + ) + } + +); +``` + +- Ahora vamosm a importar esto en nuestro loginPage. + +_./src/pages/login/loginPage.tsx_ + +```diff +- import {SessionContext} from '../../common'; ++ import {SessionContext, withSessionContext} from '../../common'; +``` + +- Y vamos a borrar LoginPageInner2 y añadir nuestro Hoc: + +_./src/pages/login/loginPage.tsx_ + +```diff +- export const LoginPageInner2 = (props) => +- <> +- +- { +- ({updateLogin}) => +- +- } +- +- +- + +- export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); ++ export const LoginPage = withSessionContext(withStyles(styles)(withRouter((LoginPageInner)))); +``` + +- Podemos ejecutar y verificar que el ejemplo estÔ funcionando. + +> Como un ejercicio crea una pÔgina C y haz uso del Hoc. + +> Anidando HOCS podemos hacer código difícil de leer, nosotros podemos usar lodash flow para mitigar esto. \ No newline at end of file From 885ebe4d6daedcb1b772c3d5fd8a2ff8d280eb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 9 Nov 2018 19:39:03 +0100 Subject: [PATCH 141/180] fix more errors --- 18 Hoc/Readme.md | 4 ++-- 18 Hoc/readme_es.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/18 Hoc/Readme.md b/18 Hoc/Readme.md index f2a7c65..41c6af3 100644 --- a/18 Hoc/Readme.md +++ b/18 Hoc/Readme.md @@ -43,8 +43,8 @@ export const withSessionContext = (Component) => (props) => ( _./src/pages/login/loginPage.tsx_ ```diff -- import {SessionContext} from '../../common'; -+ import {SessionContext, withSessionContext} from '../../common'; +- import { SessionContext } from '../../common'; ++ import { SessionContext, withSessionContext } from '../../common'; ``` - And let's remove LoginPageInner2 and add our Hoc: diff --git a/18 Hoc/readme_es.md b/18 Hoc/readme_es.md index d815d33..f2c365c 100644 --- a/18 Hoc/readme_es.md +++ b/18 Hoc/readme_es.md @@ -43,8 +43,8 @@ export const withSessionContext = (Component) => (props) => ( _./src/pages/login/loginPage.tsx_ ```diff -- import {SessionContext} from '../../common'; -+ import {SessionContext, withSessionContext} from '../../common'; +- import { SessionContext } from '../../common'; ++ import { SessionContext, withSessionContext } from '../../common'; ``` - Y vamos a borrar LoginPageInner2 y añadir nuestro Hoc: From 3ef87d45d85783ed3bc333bb8706a6d1ac62678e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 10 Nov 2018 11:18:24 +0100 Subject: [PATCH 142/180] Create spanish readme and fix errors on english readme --- 19 RenderProps/Readme.md | 45 ++++++----- 19 RenderProps/readme_es.md | 155 ++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 19 deletions(-) create mode 100644 19 RenderProps/readme_es.md diff --git a/19 RenderProps/Readme.md b/19 RenderProps/Readme.md index ce756f3..b97aa92 100644 --- a/19 RenderProps/Readme.md +++ b/19 RenderProps/Readme.md @@ -1,16 +1,23 @@ -# Intro +## Intro In this sample we are going to learn how use render props in React. -In this case we will implement a component that will inject the session -to other components via render props. +In this case we will implement a component that will inject the session to other components via render props. The main advantage of using this approach instead of HOC is that the child component gets a clear contract of the props it receives. -# Steps +We will take a startup point sample _18 Hoc_: -- Let's first add the component that will expose the render prop, we will append it to -the sessionContext file. +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Let's first add the component that will expose the render prop, we will append it to +the _sessionContext_ file. _./src/common/sessionContext.tsx_ @@ -61,21 +68,21 @@ export class SessionProvider extends React.Component<{}, State> { + + export class Session extends React.Component { + constructor(props : Props) { -+ super(props); ++ super(props); + } -+ -+ render() { ++ ++ render() { + return ( -+ -+ { -+ ({ login, updateLogin }) => -+ <> -+ {this.props.render(login)} ++ ++ { ++ ({ login, updateLogin }) => ++ <> ++ {this.props.render(login)} + -+ } ++ } + -+ ) -+ } ++ ) ++ } + } ``` @@ -93,7 +100,7 @@ export const PageB = () =>
    ( + login => ( <>

    Hello from page B


    @@ -138,7 +145,7 @@ export const PageB = () =>
    ( + login => ( )} > diff --git a/19 RenderProps/readme_es.md b/19 RenderProps/readme_es.md new file mode 100644 index 0000000..12009d8 --- /dev/null +++ b/19 RenderProps/readme_es.md @@ -0,0 +1,155 @@ +## Introducción + +En este ejemplo vamos a aprender como usar render props en React. + +En este caso implementaremos un componente que inyectarÔ la sesión a otros componentes vía render props. + +El principal ventaja de usar este aproximación en lugar de HOC es que el componente hijo obtiene un contrato de las propiedades que recibe. + +Tomaremos como punto de entrada el ejemplo _18 Hoc_: + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estÔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en una terminal o consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Primero vamos a añadir el componente que expondrÔ el reder prop, lo añadiremos al fichero _sessionContext_. + +_./src/common/sessionContext.tsx_ + +```diff +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + ++ interface Props { ++ render : (login : string) => React.ReactNode; ++ } ++ ++ export class Session extends React.Component { ++ constructor(props : Props) { ++ super(props); ++ } ++ ++ render() { ++ return ( ++ ++ { ++ ({ login, updateLogin }) => ++ <> ++ {this.props.render(login)} ++ ++ } ++ ++ ) ++ } ++ } +``` + +- Ahora en _pageB.tsx_ podemos invocarlo como (primera aproximación): + +_./src/pages/b/pageB.tsx_ + +```jsx +import * as React from "react" +import { Link } from 'react-router-dom'; +import { Session } from '../../common/' +import { checkPropTypes } from "prop-types"; + +export const PageB = () => +
    + ( + <> +

    Hello from page B

    +
    +
    +

    Login: {login}

    + + Navigate to Login + + )} + > +
    +
    +``` + +- Vamos a añadir una refactorización para hacer el código mÔs legible: + +_./src/pages/b/pageB.tsx_ + +```jsx +import * as React from "react" +import { Link } from 'react-router-dom'; +import { Session } from '../../common/' +import { checkPropTypes } from "prop-types"; + + +interface Props { + login : string; +} + +const LoginComponent = (props: Props) => + <> +

    Hello from page B

    +
    +
    +

    Login: {props.login}

    + + Navigate to Login + + + +export const PageB = () => +
    + ( + + )} + > + +
    +``` + +- Si necesitas anidar muchas reder props, puedes usar _react-composer_: https://github.com/jamesplease/react-composer \ No newline at end of file From 6929a8a7ab68ab6a86fc9e9b0bb43d667834b47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 10 Nov 2018 11:39:09 +0100 Subject: [PATCH 143/180] fix more errors --- 19 RenderProps/Readme.md | 2 +- 19 RenderProps/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/19 RenderProps/Readme.md b/19 RenderProps/Readme.md index b97aa92..c65342f 100644 --- a/19 RenderProps/Readme.md +++ b/19 RenderProps/Readme.md @@ -147,7 +147,7 @@ export const PageB = () => render={ login => ( - )} + )} >
    diff --git a/19 RenderProps/readme_es.md b/19 RenderProps/readme_es.md index 12009d8..60caea4 100644 --- a/19 RenderProps/readme_es.md +++ b/19 RenderProps/readme_es.md @@ -146,7 +146,7 @@ export const PageB = () => render={ login => ( - )} + )} >
    From 68952c33bd52d6f65c2187005258409c2fd5950c Mon Sep 17 00:00:00 2001 From: Braulio Date: Mon, 12 Nov 2018 17:30:37 +0100 Subject: [PATCH 144/180] useEffects included --- 21 Hooks/.babelrc | 10 +++ 21 Hooks/package.json | 39 ++++++++++ 21 Hooks/readme.md | 108 +++++++++++++++++++++++++++ 21 Hooks/src/api/memberAPI.ts | 46 ++++++++++++ 21 Hooks/src/app.tsx | 30 ++++++++ 21 Hooks/src/hello.tsx | 7 ++ 21 Hooks/src/index.html | 13 ++++ 21 Hooks/src/main.tsx | 10 +++ 21 Hooks/src/memberHead.tsx | 15 ++++ 21 Hooks/src/memberRow.tsx | 15 ++++ 21 Hooks/src/membersTable.tsx | 41 ++++++++++ 21 Hooks/src/model/member.ts | 11 +++ 21 Hooks/src/model/memberMockData.ts | 17 +++++ 21 Hooks/src/nameEdit.tsx | 12 +++ 21 Hooks/tsconfig.json | 17 +++++ 21 Hooks/webpack.config.js | 64 ++++++++++++++++ 16 files changed, 455 insertions(+) create mode 100644 21 Hooks/.babelrc create mode 100644 21 Hooks/package.json create mode 100644 21 Hooks/readme.md create mode 100644 21 Hooks/src/api/memberAPI.ts create mode 100644 21 Hooks/src/app.tsx create mode 100644 21 Hooks/src/hello.tsx create mode 100644 21 Hooks/src/index.html create mode 100644 21 Hooks/src/main.tsx create mode 100644 21 Hooks/src/memberHead.tsx create mode 100644 21 Hooks/src/memberRow.tsx create mode 100644 21 Hooks/src/membersTable.tsx create mode 100644 21 Hooks/src/model/member.ts create mode 100644 21 Hooks/src/model/memberMockData.ts create mode 100644 21 Hooks/src/nameEdit.tsx create mode 100644 21 Hooks/tsconfig.json create mode 100644 21 Hooks/webpack.config.js diff --git a/21 Hooks/.babelrc b/21 Hooks/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/21 Hooks/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/21 Hooks/package.json b/21 Hooks/package.json new file mode 100644 index 0000000..24b318b --- /dev/null +++ b/21 Hooks/package.json @@ -0,0 +1,39 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "core-js": "^2.5.7", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9", + "whatwg-fetch": "^3.0.0" + }, + "dependencies": { + "react": "^16.7.0-alpha.0", + "react-dom": "^16.7.0-alpha.0" + } +} diff --git a/21 Hooks/readme.md b/21 Hooks/readme.md new file mode 100644 index 0000000..e1ca9f3 --- /dev/null +++ b/21 Hooks/readme.md @@ -0,0 +1,108 @@ +# Intro + +In this sample we will make use of hooks a cool concept introduced in React 16.7.0 + +# Steps + +- Let's copy the code from sample *12_TableHttp*. + +- Let's install the dependencies. + +```bash +npm install +``` + +- Now we are going to uninstall current version of react and react-dom: + +```bash +npm uninstall react react-dom --save +``` + +- And install the 16.7 alfa version: + +```bash +npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save +``` + +- We are going to move the current _membersTable.tsx_ class based component to an +stateless one (we will replace the current state using hooks). + +- Let's start by cleaning up code and adding a hook to hold the members list + +_./src/membersTable.tsx_ + +```diff +- interface Props { +- } + +- // We define members as a state (the compoment holding this will be a container +- // component) +- interface State { +- members: Array +-} + +- // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX +- export class MembersTableComponent extends React.Component { ++ export const MembersTableComponent = () => { +- constructor(props: Props) { +- super(props); +- // set initial state +- this.state = { members: [] }; +- } + ++ const [members, setMembers] = React.useState([]); + +- // Standard react lifecycle function: +- // https://facebook.github.io/react/docs/component-specs.html +- public componentDidMount() { ++ const loadMembers = () => { + memberAPI.getAllMembers().then((members) => +- this.setState({ members: members }) ++ setMembers(members) + ); + } + +- public render() { + + return ( +
    +

    Members Page

    + + + + + + { +- this.state.members.map((member: MemberEntity) => ++ members.map((member: MemberEntity) => + + ) + } + +
    +
    + ); +- } +} +``` + +- Now we have tetchy issue... _componentDidMount_ we don't have this on hooks component +how can we dod that? To do that we can make use of react hooks _useEffect_ + +_./src/membersTable.tsx_ + +```diff +export const MembersTableComponent = () => { + + const [members, setMembers] = React.useState([]); + + const loadMembers = () => { + memberAPI.getAllMembers().then((members) => + setMembers(members) + ); + } + ++ React.useEffect(() => { ++ loadMembers(); ++ }) +``` \ No newline at end of file diff --git a/21 Hooks/src/api/memberAPI.ts b/21 Hooks/src/api/memberAPI.ts new file mode 100644 index 0000000..6185ee0 --- /dev/null +++ b/21 Hooks/src/api/memberAPI.ts @@ -0,0 +1,46 @@ +import {MemberEntity} from '../model/member'; +import {fetch} from 'whatwg-fetch'; + +// Sync mock data API, inspired from: +// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 +class MemberAPI { + + // Just return a copy of the mock data + getAllMembers() : Promise { + const gitHubMembersUrl : string = 'https://api.github.com/orgs/lemoncode/members'; + + return fetch(gitHubMembersUrl) + .then((response) => this.checkStatus(response)) + .then((response) => this.parseJSON(response)) + .then((data) => this.resolveMembers(data)); + } + + private checkStatus(response : Response) : Promise { + if (response.status >= 200 && response.status < 300) { + return Promise.resolve(response); + } else { + let error = new Error(response.statusText); + throw error; + } + } + + private parseJSON(response : Response) : any { + return response.json(); + } + + private resolveMembers (data : any) : Promise { + const members = data.map((gitHubMember) => { + var member : MemberEntity = { + id: gitHubMember.id, + login: gitHubMember.login, + avatar_url: gitHubMember.avatar_url, + }; + + return member; + }); + + return Promise.resolve(members); + } +} + +export const memberAPI = new MemberAPI(); diff --git a/21 Hooks/src/app.tsx b/21 Hooks/src/app.tsx new file mode 100644 index 0000000..74a4736 --- /dev/null +++ b/21 Hooks/src/app.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import {MembersTableComponent} from './membersTable'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + + ); + } +} \ No newline at end of file diff --git a/21 Hooks/src/hello.tsx b/21 Hooks/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/21 Hooks/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/21 Hooks/src/index.html b/21 Hooks/src/index.html new file mode 100644 index 0000000..b0b7d25 --- /dev/null +++ b/21 Hooks/src/index.html @@ -0,0 +1,13 @@ + + + + + + + +
    +

    Sample app

    +
    +
    + + diff --git a/21 Hooks/src/main.tsx b/21 Hooks/src/main.tsx new file mode 100644 index 0000000..be3985e --- /dev/null +++ b/21 Hooks/src/main.tsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import {App} from './app'; + +import { HelloComponent } from './hello'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/21 Hooks/src/memberHead.tsx b/21 Hooks/src/memberHead.tsx new file mode 100644 index 0000000..1d8a947 --- /dev/null +++ b/21 Hooks/src/memberHead.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + Avatar + + + Id + + + Name + + diff --git a/21 Hooks/src/memberRow.tsx b/21 Hooks/src/memberRow.tsx new file mode 100644 index 0000000..9e56039 --- /dev/null +++ b/21 Hooks/src/memberRow.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import {MemberEntity} from './model/member'; + +export const MemberRow = (props: {member : MemberEntity}) => + + + + + + {props.member.id} + + + {props.member.login} + + diff --git a/21 Hooks/src/membersTable.tsx b/21 Hooks/src/membersTable.tsx new file mode 100644 index 0000000..5cfca80 --- /dev/null +++ b/21 Hooks/src/membersTable.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { MemberEntity } from './model/member'; +import { memberAPI } from './api/memberAPI'; +import { MemberRow } from './memberRow'; +import { MemberHead } from './memberHead'; +import {} from 'core-js'; + + +export const MembersTableComponent = () => { + + const [members, setMembers] = React.useState([]); + + const loadMembers = () => { + memberAPI.getAllMembers().then((members) => + setMembers(members) + ); + } + + React.useEffect(() => { + loadMembers(); + }); + + + return ( +
    +

    Members Page

    + + + + + + { + members.map((member: MemberEntity) => + + ) + } + +
    +
    + ); +} diff --git a/21 Hooks/src/model/member.ts b/21 Hooks/src/model/member.ts new file mode 100644 index 0000000..8977f30 --- /dev/null +++ b/21 Hooks/src/model/member.ts @@ -0,0 +1,11 @@ +export interface MemberEntity { + id: number; + login: string; + avatar_url: string; +} + +export const createEmptyMember = () : MemberEntity => ({ + id: -1, + login: "", + avatar_url: "" +}); diff --git a/21 Hooks/src/model/memberMockData.ts b/21 Hooks/src/model/memberMockData.ts new file mode 100644 index 0000000..8a2846a --- /dev/null +++ b/21 Hooks/src/model/memberMockData.ts @@ -0,0 +1,17 @@ +import {MemberEntity} from './member'; + +var MembersMockData : MemberEntity[] = + [ + { + id: 1457912, + login: "brauliodiez", + avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" + }, + { + id: 4374977, + login: "Nasdan", + avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" + } + ]; + +export default MembersMockData; diff --git a/21 Hooks/src/nameEdit.tsx b/21 Hooks/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/21 Hooks/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/21 Hooks/tsconfig.json b/21 Hooks/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/21 Hooks/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/21 Hooks/webpack.config.js b/21 Hooks/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/21 Hooks/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From 096c725c98b1433b026783d3619db407d6af933e Mon Sep 17 00:00:00 2001 From: Braulio Date: Mon, 12 Nov 2018 17:40:59 +0100 Subject: [PATCH 145/180] custom effect --- 21 Hooks/readme.md | 34 ++++++++++++++++++++++++++++++++++ 21 Hooks/src/membersTable.tsx | 15 +++++++++------ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/21 Hooks/readme.md b/21 Hooks/readme.md index e1ca9f3..29caf17 100644 --- a/21 Hooks/readme.md +++ b/21 Hooks/readme.md @@ -105,4 +105,38 @@ export const MembersTableComponent = () => { + React.useEffect(() => { + loadMembers(); + }) +``` + +- That was nice, but what if we could be interested in reusing this hook? we can extract it +into a function: + +_./src/membersTable.tsx_ + +```diff ++ function useMembers() { ++ const [members, setMembers] = React.useState([]); ++ ++ const loadMembers = () => { ++ memberAPI.getAllMembers().then((members) => ++ setMembers(members) ++ ); ++ } ++ ++ return {members, loadMembers }; ++} + +export const MembersTableComponent = () => { ++ const { members, loadMembers } = useMembers(); +- const [members, setMembers] = React.useState([]); + +- const loadMembers = () => { +- memberAPI.getAllMembers().then((members) => +- setMembers(members) +- ); +- } + + React.useEffect(() => { + loadMembers(); + }); + ``` \ No newline at end of file diff --git a/21 Hooks/src/membersTable.tsx b/21 Hooks/src/membersTable.tsx index 5cfca80..c702703 100644 --- a/21 Hooks/src/membersTable.tsx +++ b/21 Hooks/src/membersTable.tsx @@ -3,23 +3,26 @@ import { MemberEntity } from './model/member'; import { memberAPI } from './api/memberAPI'; import { MemberRow } from './memberRow'; import { MemberHead } from './memberHead'; -import {} from 'core-js'; - - -export const MembersTableComponent = () => { +import { } from 'core-js'; +function useMembers() { const [members, setMembers] = React.useState([]); - const loadMembers = () => { + const loadMembers = () => { memberAPI.getAllMembers().then((members) => setMembers(members) ); } + return { members, loadMembers}; +} + +export const MembersTableComponent = () => { + const { members, loadMembers } = useMembers(); + React.useEffect(() => { loadMembers(); }); - return (
    From 2de9462330410a7aeaec77107efd3514adcfcbbc Mon Sep 17 00:00:00 2001 From: Braulio Date: Mon, 12 Nov 2018 18:05:09 +0100 Subject: [PATCH 146/180] hooks completed --- 21 Hooks/readme.md | 14 +++++++++++++- 21 Hooks/src/membersTable.tsx | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/21 Hooks/readme.md b/21 Hooks/readme.md index 29caf17..d6c2ce9 100644 --- a/21 Hooks/readme.md +++ b/21 Hooks/readme.md @@ -138,5 +138,17 @@ export const MembersTableComponent = () => { React.useEffect(() => { loadMembers(); }); +``` + +- Now if we ran this it will get ran on every rerender, in order to limit this we can +pas an empty array as a second argument of _useEffect_, this tell React that you effect +doesn't depend on any values from props or state, is it nevers needs to re-rerun. -``` \ No newline at end of file +```diff + React.useEffect(() => { + loadMembers(); +- }); ++ },[]); + +``` +More info about _hooks-effect_: https://reactjs.org/docs/hooks-effect.html \ No newline at end of file diff --git a/21 Hooks/src/membersTable.tsx b/21 Hooks/src/membersTable.tsx index c702703..8dbee69 100644 --- a/21 Hooks/src/membersTable.tsx +++ b/21 Hooks/src/membersTable.tsx @@ -22,7 +22,7 @@ export const MembersTableComponent = () => { React.useEffect(() => { loadMembers(); - }); + }, []); return (
    From 44d245e3bde3d4ee76f705502d59dc2e3b178479 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 12 Nov 2018 18:43:28 +0100 Subject: [PATCH 147/180] Delete readme_es.md --- 20 ErrorBoundaries/readme_es.md | 193 -------------------------------- 1 file changed, 193 deletions(-) delete mode 100644 20 ErrorBoundaries/readme_es.md diff --git a/20 ErrorBoundaries/readme_es.md b/20 ErrorBoundaries/readme_es.md deleted file mode 100644 index 3d737a3..0000000 --- a/20 ErrorBoundaries/readme_es.md +++ /dev/null @@ -1,193 +0,0 @@ -# 03 Estado - -En este ejemplo vamos a introducir un concepto bÔsico de React: manejando estados. - -En este escenario nosotros proveeremos un username por defecto y luego lo actualizaremos. - -Tomaremos como punto de entrada el ejemplo _02 Properties_: - -## Pasos resumidos: - -- Crear un componente _App_ que contendrÔ el estado. Este estado contendrÔ el username actual (con valor por defecto "defaultUserName"). -Este componente _App_ renderizarÔ el component _Hello_. Primero nosotros crearemos un _App_ componente simple sin estado. -- Actualizar el fichero _main.tsx_ para incluir nuestro componente _App_. -- Cambiar el componente _App_ a un componente clase con estado donde contendremos el estado _userName_. -- Crear un componente _NameEdit_ para cambiar el username. Esto cambiara el estado de _App_ usando una función de _App_. -- Verificar que todo funciona correctamente. - -## Requisitos previos - -Instala [Node.js y npm](https://nodejs.org) -si no lo tenías aún instalado en tu maquina. - -> Verifica que tienes instalado al menos las versiones de node v6.x.x y npm 3.x.x, ejecutando en una ventana de terminal/consola `node -v` y `npm -v`. Las versiones anteriores pueden producir errores. - -## Pasos para construirlo - -- Copia el contenido de _02 Properties_ y ejecuta `npm install`. - -- Vamos a crear un componente _App_ bajo un fichero llamado _app.tsx_ (este componente mostrarÔ el componente _Hello_). - -_./src/app.tsx_ - -```jsx -import * as React from 'react'; -import { HelloComponent } from './hello'; - -export const App = () => { - return ( - - ); -} -``` - -- Vamos a actualizar _main.tsx_ para usar el componente _App_ que acabamos de crear. - -_./src/main.tsx_ - -```diff - import * as React from 'react'; - import * as ReactDOM from 'react-dom'; -+ import { App } from './app'; - -- import { HelloComponent } from './hello'; - - ReactDOM.render( -- , -+ , - document.getElementById('root') - ); -``` - -- Ahora podemos verificar que sigue funcionando como esperamos. - - ``` - npm start - ``` - - Es hora de revisitar _app.tsx_. Nosotros queremos guardar el nombre del usuario y luego actualizarlo. Vamos a mover esta clase componente con estado y define un estado incluyendo _userName_, y pasamos este valor al componente _Hello_. - - _./src/app.tsx_ - -```jsx -import * as React from 'react'; -import {HelloComponent} from './hello'; - -interface Props { -} - -interface State { - userName : string; -} - -export class App extends React.Component { - constructor(props: Props) { - super(props); - - this.state = {userName: 'defaultUserName'}; - } - - public render() { - return ( - - ); - } -} -``` - -- De nuevo, podemos comprobar que todo funciona correctamente tal y como esperamos. - - ``` - npm start - ``` - - - Es hora de crear un componente _NameEdit_. Este componente permitirÔ al usuario actualizar su username y notificar con un callback al control del padre cuando el valor de _userName_ actualize. - - _./src/nameEdit.tsx_ - -```jsx -import * as React from 'react'; - -interface Props { - userName : string; - onChange : (event) => void; -} - -export const NameEditComponent = (props : Props) => - <> - - - -``` - -Nota aclaratoria: ¿ Que es este framento <> ? Una manera de crear un componente que tiene múltiples elementos (no un único padre). Disponible desde React 16.2. Como una manera alternativa puedes escribir: - -```jsx - ... - export const NameEditComponent = (props : Props) => - - - - -} -``` - -- En el fichero _app.tsx_, vamos a añadir una función para remplazar el valor del estado de _userName_ con el valor nuevo. - -```diff - import * as React from 'react'; - import {HelloComponent} from './hello'; -+ import { NameEditComponent } from './nameEdit'; - - interface Props { - } - - interface State { - userName : string; - } - - export class App extends React.Component { - constructor(props : Props) { - super(props); - - this.state = {userName: 'defaultUserName'}; - } - -+ setUsernameState = (event) => { -+ this.setState({userName: event.target.value}); -+ } - - public render() { - return ( -+ <> - -+ -+ - ); - } - } -``` - -Nota aclarativa: la intención de usar la función flecha. Esto evita perder el contexto de _this_ en el callback. - -Nota aclarativa 2: Este this.setState() cambiarÔ el valor del estado en algún punto en el futuro. No consideres que esto es un cambio asíncrono - no lo es. La lógica de escritura que depende de que el nuevo valor del nombre de usuario esté en el estado justo después de llamar a this.setState() es incorrecta y puede fallar. Si necesitas escribir código dependiendo de que el nuevo valor esté en el estado, usa un callback como segundo parÔmetro de this.setState(). Mira este ejemplo - -``` - setUserNameState = (newName: string) => { - this.setState({userName: newName}, this.nameChanged); - } - - nameChanged() { - /* logic here gets invoked after the new name value in the state is set. */ - } -``` - -- Finalmente vamos a comprobar que todo funciona una vez mÔs. - - ``` - npm start - ``` \ No newline at end of file From 2bde4c9f8029f7f45f19f6872dc973e0c1038b1a Mon Sep 17 00:00:00 2001 From: Braulio Date: Thu, 15 Nov 2018 11:11:23 +0100 Subject: [PATCH 148/180] Sample completed --- 22_Hooks_UseContext/.babelrc | 10 ++ 22_Hooks_UseContext/Readme.md | 154 ++++++++++++++++++ 22_Hooks_UseContext/package.json | 42 +++++ 22_Hooks_UseContext/src/api/login.ts | 5 + .../src/common/forms/textFieldForm.tsx | 42 +++++ 22_Hooks_UseContext/src/common/index.tsx | 2 + .../src/common/notification.tsx | 53 ++++++ .../src/common/sessionContext.tsx | 62 +++++++ 22_Hooks_UseContext/src/hello.tsx | 7 + 22_Hooks_UseContext/src/index.html | 14 ++ 22_Hooks_UseContext/src/main.tsx | 27 +++ 22_Hooks_UseContext/src/model/login.ts | 9 + 22_Hooks_UseContext/src/nameEdit.tsx | 12 ++ 22_Hooks_UseContext/src/pages/b/index.ts | 1 + 22_Hooks_UseContext/src/pages/b/pageB.tsx | 29 ++++ 22_Hooks_UseContext/src/pages/login/index.ts | 1 + .../src/pages/login/loginForm.tsx | 43 +++++ .../src/pages/login/loginPage.tsx | 120 ++++++++++++++ .../src/pages/login/loginValidations.ts | 16 ++ .../src/pages/login/viewmodel.ts | 11 ++ 22_Hooks_UseContext/tsconfig.json | 17 ++ 22_Hooks_UseContext/webpack.config.js | 64 ++++++++ 22 files changed, 741 insertions(+) create mode 100644 22_Hooks_UseContext/.babelrc create mode 100644 22_Hooks_UseContext/Readme.md create mode 100644 22_Hooks_UseContext/package.json create mode 100644 22_Hooks_UseContext/src/api/login.ts create mode 100644 22_Hooks_UseContext/src/common/forms/textFieldForm.tsx create mode 100644 22_Hooks_UseContext/src/common/index.tsx create mode 100644 22_Hooks_UseContext/src/common/notification.tsx create mode 100644 22_Hooks_UseContext/src/common/sessionContext.tsx create mode 100644 22_Hooks_UseContext/src/hello.tsx create mode 100644 22_Hooks_UseContext/src/index.html create mode 100644 22_Hooks_UseContext/src/main.tsx create mode 100644 22_Hooks_UseContext/src/model/login.ts create mode 100644 22_Hooks_UseContext/src/nameEdit.tsx create mode 100644 22_Hooks_UseContext/src/pages/b/index.ts create mode 100644 22_Hooks_UseContext/src/pages/b/pageB.tsx create mode 100644 22_Hooks_UseContext/src/pages/login/index.ts create mode 100644 22_Hooks_UseContext/src/pages/login/loginForm.tsx create mode 100644 22_Hooks_UseContext/src/pages/login/loginPage.tsx create mode 100644 22_Hooks_UseContext/src/pages/login/loginValidations.ts create mode 100644 22_Hooks_UseContext/src/pages/login/viewmodel.ts create mode 100644 22_Hooks_UseContext/tsconfig.json create mode 100644 22_Hooks_UseContext/webpack.config.js diff --git a/22_Hooks_UseContext/.babelrc b/22_Hooks_UseContext/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/22_Hooks_UseContext/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/22_Hooks_UseContext/Readme.md b/22_Hooks_UseContext/Readme.md new file mode 100644 index 0000000..01d31b5 --- /dev/null +++ b/22_Hooks_UseContext/Readme.md @@ -0,0 +1,154 @@ +## Intro + +As we saw in sample _17 Context_ this is a powerfull feature. + +Getting data from a _Context.Consumer_ needs some plumbing, we have used so far +HOC (example 18) and Render Props (example 19) to wrap that into some reusable code, +that was nice but it needed to add some extra markup to our components, making +heavy use of HOC and RenderProps can lead you to the _markup hell_ (lot of nested +HOC / Render props). + +Let's see how this is solved using Hooks + Use Context. + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Let's install the dependencies. + +```bash +npm install +``` + +- Now we are going to uninstall current version of react and react-dom: + +```bash +npm uninstall react react-dom --save +``` + +- And install the 16.7 alfa version: + +```bash +npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save +``` + +- Let's replace the render props solution with a UseContext + +_./src/pages/pageB.tsx_ + +```diff +- import * as React from "react" ++ import * as React, {useContext} from "react" +import { Link } from 'react-router-dom'; +- import { Session } from '../../common/'; ++ import { SessionContext } from '../../common'; + +// ... + +- export const PageB = () => ++ export const PageB = () => { ++ const loginContext = React.useContext(SessionContext) ++ ++ return ( + +
    +- ( +- +- )} +- > ++ +
    ++} +``` + +- So now our _PageB_ component looks as simple as: + +_./src/pages/pageB.tsx_ + +```tsx +export const PageB = () => { + const loginContext = React.useContext(SessionContext); + + return ( +
    + +
    + ) +} +``` + +- Now we can get rid of the _renderProps_ helper we created. + +_./src/common/sessionContext.tsx_ + +```diff +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + +interface Props { + render : (login : string) => React.ReactNode; +} + +- export class Session extends React.Component { +- constructor(props : Props) { +- super(props); +- } +- +- render() { +- return ( +- +- { +- ({ login, updateLogin }) => +- <> +- {this.props.render(login)} +- +- } +- +- ) +- } +- } +``` + diff --git a/22_Hooks_UseContext/package.json b/22_Hooks_UseContext/package.json new file mode 100644 index 0000000..4726fa1 --- /dev/null +++ b/22_Hooks_UseContext/package.json @@ -0,0 +1,42 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@material-ui/core": "^3.2.0", + "@material-ui/icons": "^3.0.1", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "@types/react-router-dom": "^4.3.1", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "lc-form-validation": "^2.0.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "react": "^16.7.0-alpha.0", + "react-dom": "^16.7.0-alpha.0", + "react-router-dom": "^4.3.1" + } +} diff --git a/22_Hooks_UseContext/src/api/login.ts b/22_Hooks_UseContext/src/api/login.ts new file mode 100644 index 0000000..1f7d4f3 --- /dev/null +++ b/22_Hooks_UseContext/src/api/login.ts @@ -0,0 +1,5 @@ +import {LoginEntity} from '../model/login'; + +// Just a fake loginAPI +export const isValidLogin = (loginInfo : LoginEntity) : boolean => + (loginInfo.login === 'admin' && loginInfo.password === 'test'); diff --git a/22_Hooks_UseContext/src/common/forms/textFieldForm.tsx b/22_Hooks_UseContext/src/common/forms/textFieldForm.tsx new file mode 100644 index 0000000..808091c --- /dev/null +++ b/22_Hooks_UseContext/src/common/forms/textFieldForm.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography/Typography"; + +interface Props { + name: string; + label: string; + onChange: any; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text', +} + +const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { + onChange(fieldId, e.target.value); +} + +export const TextFieldForm : React.StatelessComponent = (props) => { + const {name, label, onChange, value, error, type} = props; + + return ( + <> + + + {props.error} + + + ) +} diff --git a/22_Hooks_UseContext/src/common/index.tsx b/22_Hooks_UseContext/src/common/index.tsx new file mode 100644 index 0000000..8d8eeb0 --- /dev/null +++ b/22_Hooks_UseContext/src/common/index.tsx @@ -0,0 +1,2 @@ +export * from './notification'; +export * from './sessionContext'; \ No newline at end of file diff --git a/22_Hooks_UseContext/src/common/notification.tsx b/22_Hooks_UseContext/src/common/notification.tsx new file mode 100644 index 0000000..5396faf --- /dev/null +++ b/22_Hooks_UseContext/src/common/notification.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import Button from '@material-ui/core/Button'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from '@material-ui/icons/Close'; +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; + +interface Props extends WithStyles { + message: string; + show: boolean; + onClose: () => void; +} + +const styles = theme => createStyles({ + close: { + padding: theme.spacing.unit / 2, + }, +}); + +const NotificationComponentInner = (props: Props) => { + const { classes, message, show, onClose } = props; + + return ( + {message}} + action={[ + + + , + ]} + + /> + ) +} + +export const NotificationComponent = withStyles(styles)(NotificationComponentInner); diff --git a/22_Hooks_UseContext/src/common/sessionContext.tsx b/22_Hooks_UseContext/src/common/sessionContext.tsx new file mode 100644 index 0000000..f83ed61 --- /dev/null +++ b/22_Hooks_UseContext/src/common/sessionContext.tsx @@ -0,0 +1,62 @@ +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + +interface Props { + render : (login : string) => React.ReactNode; +} + +export class Session extends React.Component { + constructor(props : Props) { + super(props); + } + + render() { + return ( + + { + ({ login, updateLogin }) => + <> + {this.props.render(login)} + + } + + ) + } +} diff --git a/22_Hooks_UseContext/src/hello.tsx b/22_Hooks_UseContext/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/22_Hooks_UseContext/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/22_Hooks_UseContext/src/index.html b/22_Hooks_UseContext/src/index.html new file mode 100644 index 0000000..0fcc01e --- /dev/null +++ b/22_Hooks_UseContext/src/index.html @@ -0,0 +1,14 @@ + + + + + + + + + +
    +
    +
    + + diff --git a/22_Hooks_UseContext/src/main.tsx b/22_Hooks_UseContext/src/main.tsx new file mode 100644 index 0000000..247c55f --- /dev/null +++ b/22_Hooks_UseContext/src/main.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { HashRouter, Switch, Route } from 'react-router-dom'; +import { LoginPage } from './pages/login'; +import { PageB } from './pages/b'; +import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; +import { SessionProvider } from './common'; + +const theme = createMuiTheme({ + typography: { + useNextVariants: true, + }, +}); + +ReactDOM.render( + + + + + + + + + + + , document.getElementById('root') +); diff --git a/22_Hooks_UseContext/src/model/login.ts b/22_Hooks_UseContext/src/model/login.ts new file mode 100644 index 0000000..081e6fb --- /dev/null +++ b/22_Hooks_UseContext/src/model/login.ts @@ -0,0 +1,9 @@ +export interface LoginEntity { + login : string; + password : string; +} + +export const createEmptyLogin = () : LoginEntity => ({ + login: '', + password: '', +}); diff --git a/22_Hooks_UseContext/src/nameEdit.tsx b/22_Hooks_UseContext/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/22_Hooks_UseContext/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/22_Hooks_UseContext/src/pages/b/index.ts b/22_Hooks_UseContext/src/pages/b/index.ts new file mode 100644 index 0000000..913631b --- /dev/null +++ b/22_Hooks_UseContext/src/pages/b/index.ts @@ -0,0 +1 @@ +export {PageB} from './pageB'; \ No newline at end of file diff --git a/22_Hooks_UseContext/src/pages/b/pageB.tsx b/22_Hooks_UseContext/src/pages/b/pageB.tsx new file mode 100644 index 0000000..f70c234 --- /dev/null +++ b/22_Hooks_UseContext/src/pages/b/pageB.tsx @@ -0,0 +1,29 @@ +import * as React from "react" +import { Link } from 'react-router-dom'; +import { Session } from '../../common/'; +import { SessionContext } from '../../common'; + +interface Props { + login : string; +} + +const LoginComponent = (props: Props) => + <> +

    Hello from page B

    +
    +
    +

    Login: {props.login}

    + + Navigate to Login + + + +export const PageB = () => { + const loginContext = React.useContext(SessionContext); + + return ( +
    + +
    + ) +} diff --git a/22_Hooks_UseContext/src/pages/login/index.ts b/22_Hooks_UseContext/src/pages/login/index.ts new file mode 100644 index 0000000..50d85bb --- /dev/null +++ b/22_Hooks_UseContext/src/pages/login/index.ts @@ -0,0 +1 @@ +export {LoginPage} from './loginPage'; \ No newline at end of file diff --git a/22_Hooks_UseContext/src/pages/login/loginForm.tsx b/22_Hooks_UseContext/src/pages/login/loginForm.tsx new file mode 100644 index 0000000..eda83bd --- /dev/null +++ b/22_Hooks_UseContext/src/pages/login/loginForm.tsx @@ -0,0 +1,43 @@ +import * as React from "react" +import TextField from "@material-ui/core/TextField"; +import Button from "@material-ui/core/Button"; +import { LoginEntity } from "../../model/login"; +import {LoginFormErrors} from './viewmodel'; +import { TextFieldForm } from '../../common/forms/textFieldForm'; + +interface Props { + onLogin: () => void; + onUpdateField: (string, any) => void; + loginInfo : LoginEntity; + loginFormErrors : LoginFormErrors; +} + +export const LoginForm = (props: Props) => { + const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + + const onTexFieldChange = (fieldId) => (e) => { + onUpdateField(fieldId, e.target.value); + } + + return ( +
    + + + +
    + ) +} diff --git a/22_Hooks_UseContext/src/pages/login/loginPage.tsx b/22_Hooks_UseContext/src/pages/login/loginPage.tsx new file mode 100644 index 0000000..5742172 --- /dev/null +++ b/22_Hooks_UseContext/src/pages/login/loginPage.tsx @@ -0,0 +1,120 @@ +import * as React from "react" +import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardHeader from '@material-ui/core/CardHeader'; +import CardContent from '@material-ui/core/CardContent'; +import { LoginForm } from './loginForm'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; +import { LoginEntity, createEmptyLogin } from '../../model/login'; +import { isValidLogin } from '../../api/login'; +import { NotificationComponent } from '../../common' +import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; +import { loginFormValidation } from './loginValidations'; +import {SessionContext} from '../../common'; +import { Session } from "inspector"; + +// https://material-ui.com/guides/typescript/ +const styles = theme => createStyles({ + card: { + maxWidth: 400, + margin: '0 auto', + }, +}); + + +interface State { + loginInfo: LoginEntity; + showLoginFailedMsg: boolean; + loginFormErrors: LoginFormErrors; +} + +interface Props extends RouteComponentProps, WithStyles { + updateLogin: (value) => void +} + +class LoginPageInner extends React.Component { + + constructor(props) { + super(props); + + this.state = { + loginInfo: createEmptyLogin(), + showLoginFailedMsg: false, + loginFormErrors: createDefaultLoginFormErrors(), + } + } + + + onLogin = () => { + loginFormValidation.validateForm(this.state.loginInfo) + .then((formValidatinResult) => { + if(formValidatinResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.updateLogin(this.state.loginInfo.login); + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } + } else { + alert('error, review the fields'); + } + }) + } + + onUpdateLoginField = (name: string, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + + loginFormValidation.validateField(this.state.loginInfo, name, value).then( + (fieldValidationResult) => { + this.setState({ + loginFormErrors: { + ...this.state.loginFormErrors, + [name]: fieldValidationResult, + } + }); + } + ); + } + + render() { + const { classes } = this.props; + return ( + <> + + + + + + + this.setState({ showLoginFailedMsg: false })} + /> + + ) + + } +} + +export const LoginPageInner2 = (props) => + <> + + { + ({updateLogin}) => + + } + + + + +export const LoginPage = withStyles(styles)(withRouter((LoginPageInner2))); diff --git a/22_Hooks_UseContext/src/pages/login/loginValidations.ts b/22_Hooks_UseContext/src/pages/login/loginValidations.ts new file mode 100644 index 0000000..d0eee27 --- /dev/null +++ b/22_Hooks_UseContext/src/pages/login/loginValidations.ts @@ -0,0 +1,16 @@ +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const loginFormValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const loginFormValidation = createFormValidation(loginFormValidationConstraints); diff --git a/22_Hooks_UseContext/src/pages/login/viewmodel.ts b/22_Hooks_UseContext/src/pages/login/viewmodel.ts new file mode 100644 index 0000000..b374fa6 --- /dev/null +++ b/22_Hooks_UseContext/src/pages/login/viewmodel.ts @@ -0,0 +1,11 @@ +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} + +export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ + login: new FieldValidationResult(), + password: new FieldValidationResult(), +}); diff --git a/22_Hooks_UseContext/tsconfig.json b/22_Hooks_UseContext/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/22_Hooks_UseContext/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/22_Hooks_UseContext/webpack.config.js b/22_Hooks_UseContext/webpack.config.js new file mode 100644 index 0000000..7c85f49 --- /dev/null +++ b/22_Hooks_UseContext/webpack.config.js @@ -0,0 +1,64 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: ['@babel/polyfill', + './main.tsx' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js' + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + "babelCore": "@babel/core", // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; From d6e02600743cac79766400bb935aa045e854cf48 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Fri, 16 Nov 2018 11:24:35 +0100 Subject: [PATCH 149/180] Update readme.md --- 10_Sidebar/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/10_Sidebar/readme.md b/10_Sidebar/readme.md index bebad6b..a5a985e 100644 --- a/10_Sidebar/readme.md +++ b/10_Sidebar/readme.md @@ -242,7 +242,7 @@ export class App extends React.Component<{}, State> { + toggleSidebarVisibility = () => { + const newVisibleState = !this.state.isSidebarVisible; + -+ this.setState({isSidebarVisible: newVisibleState} as State); ++ this.setState({isSidebarVisible: newVisibleState}); + } From 78c6376b9af6994eadeddb0dd39fd43609032a4d Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Fri, 16 Nov 2018 14:30:58 +0100 Subject: [PATCH 150/180] Update readme.md --- 13_ShouldUpdate/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/13_ShouldUpdate/readme.md b/13_ShouldUpdate/readme.md index ec02ac5..cb9572d 100644 --- a/13_ShouldUpdate/readme.md +++ b/13_ShouldUpdate/readme.md @@ -227,8 +227,8 @@ export class App extends React.Component { + min="0" + max="500" + value={this.state.satisfactionLevel} -+ onChange={(event : any) => this.setState( -+ {satisfactionLevel:event.target.value})} ++ onChange={(event) => this.setState( ++ {satisfactionLevel:+event.target.value})} + /> +
    + {this.state.satisfactionLevel} From e254add81c41672c88c3ffced46c560caf3fe5ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Fri, 16 Nov 2018 23:44:02 +0100 Subject: [PATCH 151/180] Fix errors and create readme spanish version --- 20 ErrorBoundaries/readme.md | 17 ++-- 20 ErrorBoundaries/readme_es.md | 168 ++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 20 ErrorBoundaries/readme_es.md diff --git a/20 ErrorBoundaries/readme.md b/20 ErrorBoundaries/readme.md index c6dafd9..4098d49 100644 --- a/20 ErrorBoundaries/readme.md +++ b/20 ErrorBoundaries/readme.md @@ -1,4 +1,4 @@ -# 20 Error Boundaries +## 20 Error Boundaries In this example we will play with the Error Boundary concept. @@ -34,9 +34,9 @@ export class FaultyComponent extends React.Component { ) } } -``` +``` -- Let's instantiate this component in our _app.tsx_ +- Let's instantiate this component in our _app.tsx_ _./src/app.tsx_ @@ -70,7 +70,7 @@ export class App extends React.Component { <> -+ ++ ); } @@ -83,9 +83,10 @@ export class App extends React.Component { npm start ``` -- If you open the console you will see a bad crash being reported, that's something that you wouldn't like to suffer when you are using plugins and other components that you may not trust, why not wrap any error in a safe area -and display a friendly component failed to load in case of an uncontroller error happen in that area (and keep the rest -of application working as expected). Let's create an Error Boundary. +- If you open the console you will see a bad crash being reported, that's something that you wouldn't like to suffer when you are using plugins and other components that you may not trust, why not wrap any error in a safe area ? +and display a friendly component failed to load in case of an uncontroller error happen in that area (and keep the rest of application working as expected). + +- Let's create an Error Boundary. _./src/erroBoundary.tsx_ @@ -156,7 +157,7 @@ export class App extends React.Component { <> -+ ++ + diff --git a/20 ErrorBoundaries/readme_es.md b/20 ErrorBoundaries/readme_es.md new file mode 100644 index 0000000..3338068 --- /dev/null +++ b/20 ErrorBoundaries/readme_es.md @@ -0,0 +1,168 @@ +## 20 Error Boundaries + +En este ejemplo jugaremos con el concepto de barrera de errores. + +## Resumen de pasos: + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estƔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en una terminal o consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copia el contenido de _03 State_ y ejecuta `npm install`. + +- Vamos a crear un componente defectuoso: + +_./src/faultyComponent.tsx_ + +```jsx +import * as React from 'react'; + +export class FaultyComponent extends React.Component { + componentDidMount() { + throw "I'm the faulty component, generating a bad crash." + } + + render() { + return ( +

    Hello from Faulty Component

    + ) + } +} +``` + +- Vamos a instanciar este componente en nuestro _app.tsx_ + +_./src/app.tsx_ + +```diff +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; ++ import { FaultyComponent } from './faultyComponent'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + ++ + + ); + } +} +``` + +- Vamos a ejecutar app + +```bash +npm start +``` + +- Si abres la consola verÔs que se informa de un fallo grave, eso es algo que no te gustaría sufrir cuando usas plugins y otros componentes en los que no confies, ¿por que no envolver cualquier error en un Ôrea segura?y mostrar un componente amigable que no se pudo cargar en caso de que ocurra un error de descontrolador en esa Ôrea (y mantener el resto de la aplicación funcionando como se esperaba). + +- Vamos a crear una barrera de error. + +_./src/erroBoundary.tsx_ + +```jsx +import * as React from 'React'; + +export class ErrorBoundary extends React.Component { + state = { error: null, errorInfo: null }; + + componentDidCatch(error, errorInfo) { + this.setState({ + error: error, + errorInfo: errorInfo + }); + } + + render() { + if (this.state.errorInfo) { + return ( +
    +

    Plugin Failed to load, optional error info:

    +
    + {this.state.error && this.state.error.toString()} +
    + {this.state.errorInfo.componentStack} +
    +
    + ); + } + + return this.props.children; + } +} +``` + +- Y envolvamos nuestro faultyComponent dentro de esta barrera de error (podriamos envolver un conjunto de componentes si es necesario). + +_./src/app.tsx_ + +```diff +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; +import { FaultyComponent } from './faultyComponent'; ++ import { ErrorBoundary } from './errorBoundary'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + ++ + ++ + + ); + } +} +``` + +> Hay un bonito envoltorio genƩrico para este ErrorBoundary: https://github.com/bvaughn/react-error-boundary + +> Barreras de error y manejador de enventos: https://github.com/facebook/react/issues/11409 \ No newline at end of file From 09efd1e1aed14c5f49b9864f42acd820173ac4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 12:21:44 +0100 Subject: [PATCH 152/180] fix more errors --- 20 ErrorBoundaries/readme.md | 2 +- 20 ErrorBoundaries/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/20 ErrorBoundaries/readme.md b/20 ErrorBoundaries/readme.md index 4098d49..8b3a3dc 100644 --- a/20 ErrorBoundaries/readme.md +++ b/20 ErrorBoundaries/readme.md @@ -159,7 +159,7 @@ export class App extends React.Component { + -+ ++
    ); } diff --git a/20 ErrorBoundaries/readme_es.md b/20 ErrorBoundaries/readme_es.md index 3338068..7c1cd8d 100644 --- a/20 ErrorBoundaries/readme_es.md +++ b/20 ErrorBoundaries/readme_es.md @@ -156,7 +156,7 @@ export class App extends React.Component { + -+ ++ ); } From c2781763b8e515aee5764687897370d2de1f8cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 16:44:41 +0100 Subject: [PATCH 153/180] Fix errors to english readme and create spanish readme --- 21 Hooks/readme.md | 39 +++++------ 21 Hooks/readme_es.md | 155 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 19 deletions(-) create mode 100644 21 Hooks/readme_es.md diff --git a/21 Hooks/readme.md b/21 Hooks/readme.md index d6c2ce9..6301103 100644 --- a/21 Hooks/readme.md +++ b/21 Hooks/readme.md @@ -1,10 +1,16 @@ -# Intro +## Intro In this sample we will make use of hooks a cool concept introduced in React 16.7.0 -# Steps +## Prerequisites -- Let's copy the code from sample *12_TableHttp*. +Install [Node.js and npm](https://nodejs.org) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Let's copy the code from sample _12_TableHttp_. - Let's install the dependencies. @@ -24,10 +30,9 @@ npm uninstall react react-dom --save npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save ``` -- We are going to move the current _membersTable.tsx_ class based component to an -stateless one (we will replace the current state using hooks). +- We are going to move the current _membersTable.tsx_ class based component to an stateless one (we will replace the current state using hooks). -- Let's start by cleaning up code and adding a hook to hold the members list +- Let's start by cleaning up code and adding a hook to hold the members list. _./src/membersTable.tsx_ @@ -55,7 +60,7 @@ _./src/membersTable.tsx_ - // Standard react lifecycle function: - // https://facebook.github.io/react/docs/component-specs.html - public componentDidMount() { -+ const loadMembers = () => { ++ const loadMembers = () => { memberAPI.getAllMembers().then((members) => - this.setState({ members: members }) + setMembers(members) @@ -86,8 +91,7 @@ _./src/membersTable.tsx_ } ``` -- Now we have tetchy issue... _componentDidMount_ we don't have this on hooks component -how can we dod that? To do that we can make use of react hooks _useEffect_ +- Now we have tetchy issue... _componentDidMount_ we don't have this on hooks component, how can we dod that? To do that we can make use of react hooks _useEffect_. _./src/membersTable.tsx_ @@ -96,7 +100,7 @@ export const MembersTableComponent = () => { const [members, setMembers] = React.useState([]); - const loadMembers = () => { + const loadMembers = () => { memberAPI.getAllMembers().then((members) => setMembers(members) ); @@ -107,8 +111,7 @@ export const MembersTableComponent = () => { + }) ``` -- That was nice, but what if we could be interested in reusing this hook? we can extract it -into a function: +- That was nice, but what if we could be interested in reusing this hook?. We can extract it into a function: _./src/membersTable.tsx_ @@ -116,7 +119,7 @@ _./src/membersTable.tsx_ + function useMembers() { + const [members, setMembers] = React.useState([]); + -+ const loadMembers = () => { ++ const loadMembers = () => { + memberAPI.getAllMembers().then((members) => + setMembers(members) + ); @@ -129,7 +132,7 @@ export const MembersTableComponent = () => { + const { members, loadMembers } = useMembers(); - const [members, setMembers] = React.useState([]); -- const loadMembers = () => { +- const loadMembers = () => { - memberAPI.getAllMembers().then((members) => - setMembers(members) - ); @@ -140,15 +143,13 @@ export const MembersTableComponent = () => { }); ``` -- Now if we ran this it will get ran on every rerender, in order to limit this we can -pas an empty array as a second argument of _useEffect_, this tell React that you effect -doesn't depend on any values from props or state, is it nevers needs to re-rerun. +- Now if we ran this it will get ran on every rerender, in order to limit this we can pass an empty array as a second argument of _useEffect_, this tell React that you effect doesn't depend on any values from props or state, is it nevers needs to re-rerun. ```diff React.useEffect(() => { loadMembers(); - }); + },[]); - ``` -More info about _hooks-effect_: https://reactjs.org/docs/hooks-effect.html \ No newline at end of file + +> More info about _hooks-effect_: https://reactjs.org/docs/hooks-effect.html \ No newline at end of file diff --git a/21 Hooks/readme_es.md b/21 Hooks/readme_es.md new file mode 100644 index 0000000..3ff4b70 --- /dev/null +++ b/21 Hooks/readme_es.md @@ -0,0 +1,155 @@ +## Intro + +En este ejemplo haremos uso de hooks un concepto chulo que se introdució en React 16.7.0 + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estÔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en una terminal o consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Vamos a copiar el código del ejemplo _12_TableHttp_. + +- Vamos a instalar las dependencias. + +```bash +npm install +``` + +- Ahora vamos a desinstalar la versión actual de react y react-dom + +```bash +npm uninstall react react-dom --save +``` + +- E instalamos la versión 16.7 alfa: + +```bash +npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save +``` + +- Vamos a mover el componente actual basado en la clase _membersTable.tsx_ a uno sin estado (reemplazaremos el estado actual usando hooks). + +- Vamos a empezar por limpiar el código y añadiendo un hook que mantenga la lista de mienbros. + +_./src/membersTable.tsx_ + +```diff +- interface Props { +- } + +- // We define members as a state (the compoment holding this will be a container +- // component) +- interface State { +- members: Array +-} + +- // Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX +- export class MembersTableComponent extends React.Component { ++ export const MembersTableComponent = () => { +- constructor(props: Props) { +- super(props); +- // set initial state +- this.state = { members: [] }; +- } + ++ const [members, setMembers] = React.useState([]); + +- // Standard react lifecycle function: +- // https://facebook.github.io/react/docs/component-specs.html +- public componentDidMount() { ++ const loadMembers = () => { + memberAPI.getAllMembers().then((members) => +- this.setState({ members: members }) ++ setMembers(members) + ); + } + +- public render() { + + return ( +
    +

    Members Page

    + + + + + + { +- this.state.members.map((member: MemberEntity) => ++ members.map((member: MemberEntity) => + + ) + } + +
    +
    + ); +- } +} +``` + +- Ahora tenemos un gran problema... _componentDidMount_ no tenemos esto en el componente hooks, ¿Como podemos hacer esto? Para ello podemos hacer uso de los hooks de react _useEffect_. + +_./src/membersTable.tsx_ + +```diff +export const MembersTableComponent = () => { + + const [members, setMembers] = React.useState([]); + + const loadMembers = () => { + memberAPI.getAllMembers().then((members) => + setMembers(members) + ); + } + ++ React.useEffect(() => { ++ loadMembers(); ++ }) +``` + +- Eso fué genial, pero ¿Y si pudiéramos estÔr interesandos en reutilizar esté hook? Podríamos extraer esto en una función: + +_./src/membersTable.tsx_ + +```diff ++ function useMembers() { ++ const [members, setMembers] = React.useState([]); ++ ++ const loadMembers = () => { ++ memberAPI.getAllMembers().then((members) => ++ setMembers(members) ++ ); ++ } ++ ++ return {members, loadMembers }; ++} + +export const MembersTableComponent = () => { ++ const { members, loadMembers } = useMembers(); +- const [members, setMembers] = React.useState([]); + +- const loadMembers = () => { +- memberAPI.getAllMembers().then((members) => +- setMembers(members) +- ); +- } + + React.useEffect(() => { + loadMembers(); + }); +``` + +- Ahora si lo ejecutamos, se ejecutarÔ en cada repetición, para limitar esto podemos pasar un array vació como un segundo argumento de _useEffect_, esto le dice a React que su efecto no dependen de ningún valor de props o del estado, esto nunca necesita volver a ejecutar. + +```diff + React.useEffect(() => { + loadMembers(); +- }); ++ },[]); +``` + +> Mas información sobre _hooks-effect_: https://reactjs.org/docs/hooks-effect.html \ No newline at end of file From f120c731d7de760277400c7ca6ac1e15e88579d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 18:44:51 +0100 Subject: [PATCH 154/180] fix errors --- 21 Hooks/readme_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/21 Hooks/readme_es.md b/21 Hooks/readme_es.md index 3ff4b70..3253948 100644 --- a/21 Hooks/readme_es.md +++ b/21 Hooks/readme_es.md @@ -152,4 +152,4 @@ export const MembersTableComponent = () => { + },[]); ``` -> Mas información sobre _hooks-effect_: https://reactjs.org/docs/hooks-effect.html \ No newline at end of file +> MÔs información sobre _hooks-effect_: https://reactjs.org/docs/hooks-effect.html \ No newline at end of file From 585f908cf91e3ffdc261b72bf82d68824992a602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 19:38:32 +0100 Subject: [PATCH 155/180] Create spanish readme and fix errors to english readme --- 22_Hooks_UseContext/Readme.md | 40 ++++---- 22_Hooks_UseContext/readme_es.md | 151 +++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 22_Hooks_UseContext/readme_es.md diff --git a/22_Hooks_UseContext/Readme.md b/22_Hooks_UseContext/Readme.md index 01d31b5..a2f2d29 100644 --- a/22_Hooks_UseContext/Readme.md +++ b/22_Hooks_UseContext/Readme.md @@ -2,11 +2,7 @@ As we saw in sample _17 Context_ this is a powerfull feature. -Getting data from a _Context.Consumer_ needs some plumbing, we have used so far -HOC (example 18) and Render Props (example 19) to wrap that into some reusable code, -that was nice but it needed to add some extra markup to our components, making -heavy use of HOC and RenderProps can lead you to the _markup hell_ (lot of nested -HOC / Render props). +Getting data from a _Context.Consumer_ needs some plumbing, we have used so far HOC (example 18) and Render Props (example 19) to wrap that into some reusable code, that was nice but it needed to add some extra markup to our components, making heavy use of HOC and RenderProps can lead you to the _markup hell_ (lot of nested HOC / Render props). Let's see how this is solved using Hooks + Use Context. @@ -18,6 +14,8 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not alrea ## Steps to build it +- Let's copy the code from sample _17 Context_. + - Let's install the dependencies. ```bash @@ -36,7 +34,7 @@ npm uninstall react react-dom --save npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save ``` -- Let's replace the render props solution with a UseContext +- Let's replace the render props solution with a _UseContext_. _./src/pages/pageB.tsx_ @@ -58,14 +56,14 @@ import { Link } from 'react-router-dom';
    - ( +- login => ( - - )} - > -+ ++
    -+} -``` ++} +``` - So now our _PageB_ component looks as simple as: @@ -77,7 +75,7 @@ export const PageB = () => { return (
    - +
    ) } @@ -134,21 +132,21 @@ interface Props { - export class Session extends React.Component { - constructor(props : Props) { -- super(props); +- super(props); - } - -- render() { +- render() { - return ( -- -- { -- ({ login, updateLogin }) => -- <> -- {this.props.render(login)} +- +- { +- ({ login, updateLogin }) => +- <> +- {this.props.render(login)} - -- } +- } - -- ) -- } +- ) +- } - } ``` diff --git a/22_Hooks_UseContext/readme_es.md b/22_Hooks_UseContext/readme_es.md new file mode 100644 index 0000000..f9c7d70 --- /dev/null +++ b/22_Hooks_UseContext/readme_es.md @@ -0,0 +1,151 @@ +## Intro + +Como vemos en el ejemplo _17 Context_ esto es una poderosa característica. + +Obtener datos de un _Context.Consumer_ necesita un poco de tubería, hasta ahora hemos utilizado HOC (ejemplo 18) y Render Props (ejemplo 19) para incluir eso en un código reutilizable, eso estuvo bien, pero necesitabas agregar un margen de beneficio adicional a nuestros componentes, hacer un uso intensivo de HOC y RenderProps puede llevarlo a _markup hell_ (muchos accesorios de HOC / Render props). + +Veamos como se resuelve esto utilizando Hooks + Use Context. + +## Prerequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estÔs usando al menos node v6.x.x y npm 3.x.x usando los comandos `node -v` y `npm -v` en una terminal o consola. Las versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Vamos a copiar el código del ejemplo _21 Hooks_. + +- Vamos a instalar las dependencias. + +```bash +npm install +``` + +- Ahora vamos a desistalar la versión actual de react y react-dom: + +```bash +npm uninstall react react-dom --save +``` + +- E instalamos la versión alfa 16.7: + +```bash +npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save +``` + +- Vamos a reemplazar la solución de render props con un _UseContext_ + +_./src/pages/pageB.tsx_ + +```diff +- import * as React from "react" ++ import * as React, {useContext} from "react" +import { Link } from 'react-router-dom'; +- import { Session } from '../../common/'; ++ import { SessionContext } from '../../common'; + +// ... + +- export const PageB = () => ++ export const PageB = () => { ++ const loginContext = React.useContext(SessionContext) ++ ++ return ( + +
    +- ( +- +- )} +- > ++ +
    ++} +``` + +- Asi que ahora nuestro componente _PageB_ se muestra tan simple como: + +_./src/pages/pageB.tsx_ + +```tsx +export const PageB = () => { + const loginContext = React.useContext(SessionContext); + + return ( +
    + +
    + ) +} +``` + +- Ahora podemos eliminar el helper de _renderProps_ que creamos. + +_./src/common/sessionContext.tsx_ + +```diff +import * as React from "react" + +export interface SessionContextProps { + login: string; + updateLogin: (value) => void; +} + +export const createDefaultUser = (): SessionContextProps => ({ + login: 'no user', + updateLogin: (value) => { }, +}); + +export const SessionContext = React.createContext(createDefaultUser()); + +interface State extends SessionContextProps { +} + +export class SessionProvider extends React.Component<{}, State> { + + constructor(props) { + super(props); + this.state = { + login: createDefaultUser().login, + updateLogin: this.setLoginInfo + } + } + + setLoginInfo = (newLogin) => { + this.setState({ login: newLogin }) + } + + render() { + return ( + + {this.props.children} + + ) + }; +}; + +interface Props { + render : (login : string) => React.ReactNode; +} + +- export class Session extends React.Component { +- constructor(props : Props) { +- super(props); +- } +- +- render() { +- return ( +- +- { +- ({ login, updateLogin }) => +- <> +- {this.props.render(login)} +- +- } +- +- ) +- } +- } +``` \ No newline at end of file From d6e798334d649042ac652e680cc861db05ab0ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 19:59:35 +0100 Subject: [PATCH 156/180] Fix errors --- 22_Hooks_UseContext/Readme.md | 2 +- 22_Hooks_UseContext/readme_es.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/22_Hooks_UseContext/Readme.md b/22_Hooks_UseContext/Readme.md index a2f2d29..eb05683 100644 --- a/22_Hooks_UseContext/Readme.md +++ b/22_Hooks_UseContext/Readme.md @@ -40,7 +40,7 @@ _./src/pages/pageB.tsx_ ```diff - import * as React from "react" -+ import * as React, {useContext} from "react" ++ import * as React from "react"; import { Link } from 'react-router-dom'; - import { Session } from '../../common/'; + import { SessionContext } from '../../common'; diff --git a/22_Hooks_UseContext/readme_es.md b/22_Hooks_UseContext/readme_es.md index f9c7d70..747cc6a 100644 --- a/22_Hooks_UseContext/readme_es.md +++ b/22_Hooks_UseContext/readme_es.md @@ -14,7 +14,7 @@ Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las t ## Pasos para construirlo -- Vamos a copiar el código del ejemplo _21 Hooks_. +- Vamos a copiar el código del ejemplo _17 Context_. - Vamos a instalar las dependencias. @@ -40,7 +40,7 @@ _./src/pages/pageB.tsx_ ```diff - import * as React from "react" -+ import * as React, {useContext} from "react" ++ import * as React from "react"; import { Link } from 'react-router-dom'; - import { Session } from '../../common/'; + import { SessionContext } from '../../common'; From 19312a9533c3e590f2b4d4a6f605a6c60ef6dfc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 20:04:05 +0100 Subject: [PATCH 157/180] Fix errors --- 22_Hooks_UseContext/Readme.md | 2 +- 22_Hooks_UseContext/readme_es.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/22_Hooks_UseContext/Readme.md b/22_Hooks_UseContext/Readme.md index eb05683..2ca078a 100644 --- a/22_Hooks_UseContext/Readme.md +++ b/22_Hooks_UseContext/Readme.md @@ -14,7 +14,7 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0) if they are not alrea ## Steps to build it -- Let's copy the code from sample _17 Context_. +- Let's copy the code from sample _19 RenderProps_. - Let's install the dependencies. diff --git a/22_Hooks_UseContext/readme_es.md b/22_Hooks_UseContext/readme_es.md index 747cc6a..3b49a66 100644 --- a/22_Hooks_UseContext/readme_es.md +++ b/22_Hooks_UseContext/readme_es.md @@ -14,7 +14,7 @@ Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las t ## Pasos para construirlo -- Vamos a copiar el código del ejemplo _17 Context_. +- Vamos a copiar el código del ejemplo _19 RenderProps_. - Vamos a instalar las dependencias. From 909cbd548f507a64baff7babc37e24dc77e40d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AntonioContrerasRom=C3=A1n?= Date: Sat, 17 Nov 2018 20:12:15 +0100 Subject: [PATCH 158/180] Fix more errors --- 22_Hooks_UseContext/Readme.md | 5 ++--- 22_Hooks_UseContext/readme_es.md | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/22_Hooks_UseContext/Readme.md b/22_Hooks_UseContext/Readme.md index 2ca078a..2665db9 100644 --- a/22_Hooks_UseContext/Readme.md +++ b/22_Hooks_UseContext/Readme.md @@ -39,8 +39,7 @@ npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save _./src/pages/pageB.tsx_ ```diff -- import * as React from "react" -+ import * as React from "react"; +import * as React from "react" import { Link } from 'react-router-dom'; - import { Session } from '../../common/'; + import { SessionContext } from '../../common'; @@ -53,7 +52,7 @@ import { Link } from 'react-router-dom'; + + return ( -
    +
    - ( diff --git a/22_Hooks_UseContext/readme_es.md b/22_Hooks_UseContext/readme_es.md index 3b49a66..5035bd7 100644 --- a/22_Hooks_UseContext/readme_es.md +++ b/22_Hooks_UseContext/readme_es.md @@ -39,8 +39,7 @@ npm install react@16.7.0-alpha.0 react-dom@16.7.0-alpha.0 --save _./src/pages/pageB.tsx_ ```diff -- import * as React from "react" -+ import * as React from "react"; +import * as React from "react" import { Link } from 'react-router-dom'; - import { Session } from '../../common/'; + import { SessionContext } from '../../common'; From 1d1ef538bf92e3250e5a8f565bcca166b3a2f2d0 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 26 Nov 2018 16:58:00 +0100 Subject: [PATCH 159/180] Update readme.md --- 09_ColorpRefactor/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/09_ColorpRefactor/readme.md b/09_ColorpRefactor/readme.md index d3b709b..9e55221 100644 --- a/09_ColorpRefactor/readme.md +++ b/09_ColorpRefactor/readme.md @@ -41,7 +41,7 @@ export const ColorSliderComponent = (props : Props) => { min="0" max="255" value={props.value} - onChange={(event : any) => props.onValueUpdated(event.target.value)} + onChange={(event) => props.onValueUpdated(event.target.value)} /> {props.value}
    @@ -68,7 +68,7 @@ export const ColorPicker = (props : Props) => { - min="0" - max="255" - value={props.color.red} -- onChange={(event : any) => props.onColorUpdated( +- onChange={(event) => props.onColorUpdated( - { - red: props.color.red, - green: event.target.value, From bfd4416ffa02bddd1df37fe3188c8c08bf2efb50 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Sat, 1 Dec 2018 12:18:49 +0100 Subject: [PATCH 160/180] Update readme.md --- 16 Validation/readme.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/16 Validation/readme.md b/16 Validation/readme.md index b1a5fbb..01dfaec 100644 --- a/16 Validation/readme.md +++ b/16 Validation/readme.md @@ -299,6 +299,13 @@ _./src/pages/login/loginPageContainer.tsx_ } + } else { + alert('error, review the fields'); ++ const updatedLoginFormErrors = { ++ ...this.state.loginFormErrors, ++ ...formValidationResult.fieldErrors, ++ } + ++ this.setState({loginFormErrors: updatedLoginFormErrors}) + + } + }) } @@ -306,4 +313,4 @@ _./src/pages/login/loginPageContainer.tsx_ // TODO: mapFormValidationResultToFieldValidationErrors -> Excercise create a generic info snack bar and remove alert. \ No newline at end of file +> Excercise create a generic info snack bar and remove alert. From e5b61369d911cedcd60e2bb177de81f34ae8ae5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 Dec 2018 18:55:36 +0000 Subject: [PATCH 161/180] Add files --- 12_TableHttp_Axios/.babelrc | 10 +++ 12_TableHttp_Axios/src/app.tsx | 30 +++++++++ 12_TableHttp_Axios/src/hello.tsx | 7 +++ 12_TableHttp_Axios/src/index.html | 13 ++++ 12_TableHttp_Axios/src/main.tsx | 10 +++ 12_TableHttp_Axios/src/memberHead.tsx | 15 +++++ 12_TableHttp_Axios/src/memberRow.tsx | 15 +++++ 12_TableHttp_Axios/src/membersTable.tsx | 54 ++++++++++++++++ 12_TableHttp_Axios/src/model/member.ts | 11 ++++ .../src/model/memberMockData.ts | 17 +++++ 12_TableHttp_Axios/src/nameEdit.tsx | 12 ++++ 12_TableHttp_Axios/tsconfig.json | 17 +++++ 12_TableHttp_Axios/webpack.config.js | 62 +++++++++++++++++++ 13 files changed, 273 insertions(+) create mode 100644 12_TableHttp_Axios/.babelrc create mode 100644 12_TableHttp_Axios/src/app.tsx create mode 100644 12_TableHttp_Axios/src/hello.tsx create mode 100644 12_TableHttp_Axios/src/index.html create mode 100644 12_TableHttp_Axios/src/main.tsx create mode 100644 12_TableHttp_Axios/src/memberHead.tsx create mode 100644 12_TableHttp_Axios/src/memberRow.tsx create mode 100644 12_TableHttp_Axios/src/membersTable.tsx create mode 100644 12_TableHttp_Axios/src/model/member.ts create mode 100644 12_TableHttp_Axios/src/model/memberMockData.ts create mode 100644 12_TableHttp_Axios/src/nameEdit.tsx create mode 100644 12_TableHttp_Axios/tsconfig.json create mode 100644 12_TableHttp_Axios/webpack.config.js diff --git a/12_TableHttp_Axios/.babelrc b/12_TableHttp_Axios/.babelrc new file mode 100644 index 0000000..957cae3 --- /dev/null +++ b/12_TableHttp_Axios/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry" + } + ] + ] +} diff --git a/12_TableHttp_Axios/src/app.tsx b/12_TableHttp_Axios/src/app.tsx new file mode 100644 index 0000000..74a4736 --- /dev/null +++ b/12_TableHttp_Axios/src/app.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import {MembersTableComponent} from './membersTable'; + +interface Props { +} + +interface State { + userName: string; +} + +export class App extends React.Component { + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName' }; + } + + setUsernameState = (event) => { + this.setState({ userName: event.target.value }); + } + + + public render() { + return ( + <> + + + ); + } +} \ No newline at end of file diff --git a/12_TableHttp_Axios/src/hello.tsx b/12_TableHttp_Axios/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/12_TableHttp_Axios/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

    Hello user: {props.userName} !

    + ); +} diff --git a/12_TableHttp_Axios/src/index.html b/12_TableHttp_Axios/src/index.html new file mode 100644 index 0000000..b0b7d25 --- /dev/null +++ b/12_TableHttp_Axios/src/index.html @@ -0,0 +1,13 @@ + + + + + + + +
    +

    Sample app

    +
    +
    + + diff --git a/12_TableHttp_Axios/src/main.tsx b/12_TableHttp_Axios/src/main.tsx new file mode 100644 index 0000000..be3985e --- /dev/null +++ b/12_TableHttp_Axios/src/main.tsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import {App} from './app'; + +import { HelloComponent } from './hello'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/12_TableHttp_Axios/src/memberHead.tsx b/12_TableHttp_Axios/src/memberHead.tsx new file mode 100644 index 0000000..1d8a947 --- /dev/null +++ b/12_TableHttp_Axios/src/memberHead.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + Avatar + + + Id + + + Name + + diff --git a/12_TableHttp_Axios/src/memberRow.tsx b/12_TableHttp_Axios/src/memberRow.tsx new file mode 100644 index 0000000..9e56039 --- /dev/null +++ b/12_TableHttp_Axios/src/memberRow.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import {MemberEntity} from './model/member'; + +export const MemberRow = (props: {member : MemberEntity}) => + + + + + + {props.member.id} + + + {props.member.login} + + diff --git a/12_TableHttp_Axios/src/membersTable.tsx b/12_TableHttp_Axios/src/membersTable.tsx new file mode 100644 index 0000000..dbe99e0 --- /dev/null +++ b/12_TableHttp_Axios/src/membersTable.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { MemberEntity } from './model/member'; +import { memberAPI } from './api/memberAPI'; +import { MemberRow } from './memberRow'; +import { MemberHead } from './memberHead'; +import {} from 'core-js'; + +interface Props { +} + +// We define members as a state (the compoment holding this will be a container +// component) +interface State { + members: Array +} + +// Nice tsx guide: https://github.com/Microsoft/TypeScript/wiki/JSX +export class MembersTableComponent extends React.Component { + + constructor(props: Props) { + super(props); + // set initial state + this.state = { members: [] }; + } + + // Standard react lifecycle function: + // https://facebook.github.io/react/docs/component-specs.html + public componentDidMount() { + memberAPI.getAllMembers().then((members) => + this.setState({ members }) + ); + } + + public render() { + + return ( +
    +

    Members Page

    + + + + + + { + this.state.members.map((member: MemberEntity) => + + ) + } + +
    +
    + ); + } +} diff --git a/12_TableHttp_Axios/src/model/member.ts b/12_TableHttp_Axios/src/model/member.ts new file mode 100644 index 0000000..8977f30 --- /dev/null +++ b/12_TableHttp_Axios/src/model/member.ts @@ -0,0 +1,11 @@ +export interface MemberEntity { + id: number; + login: string; + avatar_url: string; +} + +export const createEmptyMember = () : MemberEntity => ({ + id: -1, + login: "", + avatar_url: "" +}); diff --git a/12_TableHttp_Axios/src/model/memberMockData.ts b/12_TableHttp_Axios/src/model/memberMockData.ts new file mode 100644 index 0000000..8a2846a --- /dev/null +++ b/12_TableHttp_Axios/src/model/memberMockData.ts @@ -0,0 +1,17 @@ +import {MemberEntity} from './member'; + +var MembersMockData : MemberEntity[] = + [ + { + id: 1457912, + login: "brauliodiez", + avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3" + }, + { + id: 4374977, + login: "Nasdan", + avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3" + } + ]; + +export default MembersMockData; diff --git a/12_TableHttp_Axios/src/nameEdit.tsx b/12_TableHttp_Axios/src/nameEdit.tsx new file mode 100644 index 0000000..6c544b2 --- /dev/null +++ b/12_TableHttp_Axios/src/nameEdit.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + +interface Props { + userName : string; + onChange : (event) => void; +} + +export const NameEditComponent = (props : Props) => + <> + + + diff --git a/12_TableHttp_Axios/tsconfig.json b/12_TableHttp_Axios/tsconfig.json new file mode 100644 index 0000000..885d474 --- /dev/null +++ b/12_TableHttp_Axios/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} diff --git a/12_TableHttp_Axios/webpack.config.js b/12_TableHttp_Axios/webpack.config.js new file mode 100644 index 0000000..f665ad4 --- /dev/null +++ b/12_TableHttp_Axios/webpack.config.js @@ -0,0 +1,62 @@ +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var MiniCssExtractPlugin = require('mini-css-extract-plugin'); +var webpack = require('webpack'); +var path = require('path'); + +var basePath = __dirname; + +module.exports = { + context: path.join(basePath, 'src'), + resolve: { + extensions: ['.js', '.ts', '.tsx'], + }, + entry: ['@babel/polyfill', './main.tsx'], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js', + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only', + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + babelCore: '@babel/core', // needed for Babel v7 + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader'], + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]', + }, + }, + ], + }, + plugins: [ + //Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', //Name of file in ./dist/ + template: 'index.html', //Name of template in ./src + hash: true, + }), + new MiniCssExtractPlugin({ + filename: '[name].css', + chunkFilename: '[id].css', + }), + ], +}; From 7cda9a7da3a2da6f6c430b4eb3bc07d04a77a298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 Dec 2018 18:57:18 +0000 Subject: [PATCH 162/180] Add axios package --- 12_TableHttp_Axios/package.json | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 12_TableHttp_Axios/package.json diff --git a/12_TableHttp_Axios/package.json b/12_TableHttp_Axios/package.json new file mode 100644 index 0000000..4a914d7 --- /dev/null +++ b/12_TableHttp_Axios/package.json @@ -0,0 +1,39 @@ +{ + "name": "reactbysample", + "version": "1.0.0", + "description": "In this sample we setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/cli": "^7.1.2", + "@babel/core": "^7.1.2", + "@babel/polyfill": "^7.0.0", + "@babel/preset-env": "^7.1.0", + "@types/react": "^16.4.16", + "@types/react-dom": "^16.0.9", + "awesome-typescript-loader": "^5.2.1", + "babel-loader": "^8.0.4", + "core-js": "^2.5.7", + "css-loader": "^1.0.0", + "file-loader": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.3", + "style-loader": "^0.23.1", + "typescript": "^3.1.1", + "url-loader": "^1.1.1", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.2", + "webpack-dev-server": "^3.1.9" + }, + "dependencies": { + "axios": "^0.18.0", + "react": "^16.5.2", + "react-dom": "^16.5.2" + } +} From da795fcfc957945bb9ad7f6fda69f0f4d0accc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 Dec 2018 18:58:31 +0000 Subject: [PATCH 163/180] Add axios implementation --- 12_TableHttp_Axios/src/api/memberAPI.ts | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 12_TableHttp_Axios/src/api/memberAPI.ts diff --git a/12_TableHttp_Axios/src/api/memberAPI.ts b/12_TableHttp_Axios/src/api/memberAPI.ts new file mode 100644 index 0000000..eca55f3 --- /dev/null +++ b/12_TableHttp_Axios/src/api/memberAPI.ts @@ -0,0 +1,35 @@ +import { MemberEntity } from '../model/member'; +import Axios, { AxiosError, AxiosResponse } from 'axios'; + +// Sync mock data API, inspired from: +// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 +class MemberAPI { + getAllMembers(): Promise { + const gitHubMembersUrl: string = 'https://api.github.com/orgs/lemoncode/members'; + + return Axios.get(gitHubMembersUrl) + .then(this.resolveMembers) + .catch(this.onError); + } + + private onError(err: AxiosError): MemberEntity[] { + console.log(err.message); + return []; + } + + private resolveMembers({ data }: AxiosResponse): MemberEntity[] { + const members: MemberEntity[] = data.map(gitHubMember => { + let member: MemberEntity = { + id: gitHubMember.id, + login: gitHubMember.login, + avatar_url: gitHubMember.avatar_url, + }; + + return member; + }); + + return members; + } +} + +export const memberAPI = new MemberAPI(); From 6acabf7ae82958e88ceb3aa5adbd08e95be8ad83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 Dec 2018 18:58:42 +0000 Subject: [PATCH 164/180] Update readme files --- 12_TableHttp_Axios/readme.md | 151 ++++++++++++++++++++++++++++++++ 12_TableHttp_Axios/readme_es.md | 150 +++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) create mode 100644 12_TableHttp_Axios/readme.md create mode 100644 12_TableHttp_Axios/readme_es.md diff --git a/12_TableHttp_Axios/readme.md b/12_TableHttp_Axios/readme.md new file mode 100644 index 0000000..ecdb4ec --- /dev/null +++ b/12_TableHttp_Axios/readme.md @@ -0,0 +1,151 @@ +# 12 Table Http with Axios + +Let's move forward with the table sample, this time we are going to replace the +mock data by real one and we will make calls with the Axios package. + +We will take a startup point sample _11 TableMock_: + +## Summary steps: + +- Configure transpilation and add extra transpile step babel >> es5. +- Update API in order to work with [Axios](https://github.com/axios/axios) and fetch data from Github API. +- Update the _tableComponent_ in order to show this data. + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Copy the content from _11 TableMock_ and execute: + +``` +npm install +``` + +- We install the Axios package: + +``` + npm install --save axios +``` + +- Let's remove the file _memberMockData.ts_ in _src/api_ directory. + +- Let's replace _memberAPI_ load members with the fetch / promise one: + +_./src/api/memberAPI.ts_ + +```javascript +import { MemberEntity } from '../model/member'; +import Axios, { AxiosError, AxiosResponse } from 'axios'; + +// Sync mock data API, inspired from: +// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 +class MemberAPI { + getAllMembers(): Promise { + const gitHubMembersUrl: string = 'https://api.github.com/orgs/lemoncode/members'; + + return Axios.get(gitHubMembersUrl) + .then(this.resolveMembers) + .catch(this.onError); + } + + private onError(err: AxiosError): MemberEntity[] { + console.log(err.message); + return []; + } + + private resolveMembers({ data }: AxiosResponse): MemberEntity[] { + const members: MemberEntity[] = data.map(gitHubMember => { + let member: MemberEntity = { + id: gitHubMember.id, + login: gitHubMember.login, + avatar_url: gitHubMember.avatar_url, + }; + + return member; + }); + + return members; + } +} + +export const memberAPI = new MemberAPI(); + +``` + +- Add a new component _memberHead_ to create the table's header: + +_./src/memberHead.tsx_ + +```javascript +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + Avatar + + + Id + + + Name + + +``` + +- Now it's time to update our _membersTable_ component. + +_./src/membersTable.tsx_ + +- Import the new component: + +```diff + ++ import { MemberHead } from './memberHead'; + +``` + +- Modify the render function: + +```diff +- +- +- +- Avatar +- +- +- Id +- +- +- Name +- +- +- ++ ++ ++ +``` + +- Let's consume the new promise base method to retrieve the users: + +```diff +// Standard react lifecycle function: +// https://facebook.github.io/react/docs/component-specs.html +public componentDidMount() { +- this.setState({members: memberAPI.getAllMembers()}) ++ memberAPI.getAllMembers().then((members) => ++ this.setState({members: members}) ++ ); +} +``` + +- Let's give a try and check the results + +``` +npm start +``` diff --git a/12_TableHttp_Axios/readme_es.md b/12_TableHttp_Axios/readme_es.md new file mode 100644 index 0000000..7b3fe7d --- /dev/null +++ b/12_TableHttp_Axios/readme_es.md @@ -0,0 +1,150 @@ +# 12 Table Http con Axios + +Sigamos con nuestro ejemplo de la tabla, vamos a cambiar datos falsos por unos reales y realizaremos las llamadas con la librería Axios. + +Cogeremos como punto inicial el ejemplo _11 TableMock_: + +## Pasos resumidos: + +- Configurar transpilación y añadir una transpilación extra babel >> es5. +- Actualizar la API para trabajar con [Axios](https://github.com/axios/axios) y obtener datos de la API de Github. +- Actualizar _tableComponent_ para mostrar los datos. + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o mÔs nuevo) si no estÔn ya instalados. + +> Verificar que tienes al menos corriendo la versión de node v6.x.x y npm 3.x.x ejecutando `node -v` y `npm -v` en la terminal de Windows. Versiones mÔs antiguas pueden producir errores. + +## Pasos para construirlo + +- Copiar el contenido de _11 TableMock_ y ejecutar: + +``` +npm install +``` + +- Instalamos la librería Axios: + +``` + npm install --save axios +``` + +- Vamos a eliminar el fichero _memberMockData.ts_ del directorio _src/api_ . + +- Vamos a reemplazar _memberAPI_ cargando los miembros con promesas: + +_./src/api/memberAPI.ts_ + +```javascript +import { MemberEntity } from '../model/member'; +import Axios, { AxiosError, AxiosResponse } from 'axios'; + +// Sync mock data API, inspired from: +// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 +class MemberAPI { + getAllMembers(): Promise { + const gitHubMembersUrl: string = 'https://api.github.com/orgs/lemoncode/members'; + + return Axios.get(gitHubMembersUrl) + .then(this.resolveMembers) + .catch(this.onError); + } + + private onError(err: AxiosError): MemberEntity[] { + console.log(err.message); + return []; + } + + private resolveMembers({ data }: AxiosResponse): MemberEntity[] { + const members: MemberEntity[] = data.map(gitHubMember => { + let member: MemberEntity = { + id: gitHubMember.id, + login: gitHubMember.login, + avatar_url: gitHubMember.avatar_url, + }; + + return member; + }); + + return members; + } +} + +export const memberAPI = new MemberAPI(); + +``` + +- Añadimos un nuevo componente _memberHead_ para crear la cabecera de la tabla: + +_./src/memberHead.tsx_ + +```javascript +import * as React from 'react'; +import { MemberEntity } from './model/member'; + +export const MemberHead = () => + + + Avatar + + + Id + + + Name + + +``` + +- Ahora vamos a actualizar nuestro componente _membersTable_. + +_./src/memberTable.tsx_ + +- Importamos el nuevo componente: + +```diff + ++ import { MemberHead } from './memberHead'; + +``` + +- Modificamos la función render: + +```diff +- +- +- +- Avatar +- +- +- Id +- +- +- Name +- +- +- ++ ++ ++ +``` + +- Vamos a consumir el nuevo método de promesas para recuperar a los usuarios: + +```diff +// Standard react lifecycle function: +// https://facebook.github.io/react/docs/component-specs.html +public componentDidMount() { +- this.setState({members: memberAPI.getAllMembers()}) ++ memberAPI.getAllMembers().then((members) => ++ this.setState({members: members}) ++ ); +} +``` + +- Vamos a probar y verificar los resultados + +``` +npm start +``` From 17f3509e14ca2db2187c09e308fdd8fd5de13a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 Dec 2018 21:03:38 +0000 Subject: [PATCH 165/180] Refactor code, implement pure functional --- 12_TableHttp_Axios/readme.md | 58 ++++++++++--------------- 12_TableHttp_Axios/readme_es.md | 58 ++++++++++--------------- 12_TableHttp_Axios/src/api/memberAPI.ts | 48 ++++++++------------ 3 files changed, 67 insertions(+), 97 deletions(-) diff --git a/12_TableHttp_Axios/readme.md b/12_TableHttp_Axios/readme.md index ecdb4ec..4bba387 100644 --- a/12_TableHttp_Axios/readme.md +++ b/12_TableHttp_Axios/readme.md @@ -39,40 +39,30 @@ _./src/api/memberAPI.ts_ ```javascript import { MemberEntity } from '../model/member'; -import Axios, { AxiosError, AxiosResponse } from 'axios'; - -// Sync mock data API, inspired from: -// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 -class MemberAPI { - getAllMembers(): Promise { - const gitHubMembersUrl: string = 'https://api.github.com/orgs/lemoncode/members'; - - return Axios.get(gitHubMembersUrl) - .then(this.resolveMembers) - .catch(this.onError); - } - - private onError(err: AxiosError): MemberEntity[] { - console.log(err.message); - return []; - } - - private resolveMembers({ data }: AxiosResponse): MemberEntity[] { - const members: MemberEntity[] = data.map(gitHubMember => { - let member: MemberEntity = { - id: gitHubMember.id, - login: gitHubMember.login, - avatar_url: gitHubMember.avatar_url, - }; - - return member; - }); - - return members; - } -} - -export const memberAPI = new MemberAPI(); +import Axios from 'axios'; + +const gitHubURL = 'https://api.github.com'; +const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; + +const getAllMembers = (): Promise => { + const promise: Promise = new Promise((resolve, reject) => { + try { + Axios.get(gitHubMembersUrl) + .then(response => resolve(mapMemberListApiToModel(response.data))); + } catch (ex) { + reject(ex); + } + }); + + return promise; +}; + +const mapMemberListApiToModel = (data: MemberEntity[]) => + data.map(gitHubMember => gitHubMember); + +export const memberAPI = { + getAllMembers, +}; ``` diff --git a/12_TableHttp_Axios/readme_es.md b/12_TableHttp_Axios/readme_es.md index 7b3fe7d..2182415 100644 --- a/12_TableHttp_Axios/readme_es.md +++ b/12_TableHttp_Axios/readme_es.md @@ -38,40 +38,30 @@ _./src/api/memberAPI.ts_ ```javascript import { MemberEntity } from '../model/member'; -import Axios, { AxiosError, AxiosResponse } from 'axios'; - -// Sync mock data API, inspired from: -// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 -class MemberAPI { - getAllMembers(): Promise { - const gitHubMembersUrl: string = 'https://api.github.com/orgs/lemoncode/members'; - - return Axios.get(gitHubMembersUrl) - .then(this.resolveMembers) - .catch(this.onError); - } - - private onError(err: AxiosError): MemberEntity[] { - console.log(err.message); - return []; - } - - private resolveMembers({ data }: AxiosResponse): MemberEntity[] { - const members: MemberEntity[] = data.map(gitHubMember => { - let member: MemberEntity = { - id: gitHubMember.id, - login: gitHubMember.login, - avatar_url: gitHubMember.avatar_url, - }; - - return member; - }); - - return members; - } -} - -export const memberAPI = new MemberAPI(); +import Axios from 'axios'; + +const gitHubURL = 'https://api.github.com'; +const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; + +const getAllMembers = (): Promise => { + const promise: Promise = new Promise((resolve, reject) => { + try { + Axios.get(gitHubMembersUrl) + .then(response => resolve(mapMemberListApiToModel(response.data))); + } catch (ex) { + reject(ex); + } + }); + + return promise; +}; + +const mapMemberListApiToModel = (data: MemberEntity[]) => + data.map(gitHubMember => gitHubMember); + +export const memberAPI = { + getAllMembers, +}; ``` diff --git a/12_TableHttp_Axios/src/api/memberAPI.ts b/12_TableHttp_Axios/src/api/memberAPI.ts index eca55f3..033bf31 100644 --- a/12_TableHttp_Axios/src/api/memberAPI.ts +++ b/12_TableHttp_Axios/src/api/memberAPI.ts @@ -1,35 +1,25 @@ import { MemberEntity } from '../model/member'; -import Axios, { AxiosError, AxiosResponse } from 'axios'; +import Axios from 'axios'; -// Sync mock data API, inspired from: -// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 -class MemberAPI { - getAllMembers(): Promise { - const gitHubMembersUrl: string = 'https://api.github.com/orgs/lemoncode/members'; - - return Axios.get(gitHubMembersUrl) - .then(this.resolveMembers) - .catch(this.onError); - } +const gitHubURL = 'https://api.github.com'; +const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; - private onError(err: AxiosError): MemberEntity[] { - console.log(err.message); - return []; - } +const getAllMembers = (): Promise => { + const promise: Promise = new Promise((resolve, reject) => { + try { + Axios.get(gitHubMembersUrl) + .then(response => resolve(mapMemberListApiToModel(response.data))); + } catch (ex) { + reject(ex); + } + }); - private resolveMembers({ data }: AxiosResponse): MemberEntity[] { - const members: MemberEntity[] = data.map(gitHubMember => { - let member: MemberEntity = { - id: gitHubMember.id, - login: gitHubMember.login, - avatar_url: gitHubMember.avatar_url, - }; + return promise; +}; - return member; - }); +const mapMemberListApiToModel = (data: MemberEntity[]) => + data.map(gitHubMember => gitHubMember); - return members; - } -} - -export const memberAPI = new MemberAPI(); +export const memberAPI = { + getAllMembers, +}; From 07fd4b302ad61728d1cfa04f5299a9036694de87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sara=20Lissette=20Luis=20Ib=C3=A1=C3=B1ez?= Date: Sat, 1 Dec 2018 21:08:05 +0000 Subject: [PATCH 166/180] Implement destructuring --- 12_TableHttp_Axios/readme.md | 6 +++--- 12_TableHttp_Axios/readme_es.md | 7 ++++--- 12_TableHttp_Axios/src/api/memberAPI.ts | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/12_TableHttp_Axios/readme.md b/12_TableHttp_Axios/readme.md index 4bba387..c6f473c 100644 --- a/12_TableHttp_Axios/readme.md +++ b/12_TableHttp_Axios/readme.md @@ -38,8 +38,8 @@ npm install _./src/api/memberAPI.ts_ ```javascript +import Axios, { AxiosResponse } from 'axios'; import { MemberEntity } from '../model/member'; -import Axios from 'axios'; const gitHubURL = 'https://api.github.com'; const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; @@ -48,7 +48,7 @@ const getAllMembers = (): Promise => { const promise: Promise = new Promise((resolve, reject) => { try { Axios.get(gitHubMembersUrl) - .then(response => resolve(mapMemberListApiToModel(response.data))); + .then(response => resolve(mapMemberListApiToModel(response))); } catch (ex) { reject(ex); } @@ -57,7 +57,7 @@ const getAllMembers = (): Promise => { return promise; }; -const mapMemberListApiToModel = (data: MemberEntity[]) => +const mapMemberListApiToModel = ({ data }: AxiosResponse) => data.map(gitHubMember => gitHubMember); export const memberAPI = { diff --git a/12_TableHttp_Axios/readme_es.md b/12_TableHttp_Axios/readme_es.md index 2182415..9c7aea4 100644 --- a/12_TableHttp_Axios/readme_es.md +++ b/12_TableHttp_Axios/readme_es.md @@ -37,8 +37,8 @@ npm install _./src/api/memberAPI.ts_ ```javascript +import Axios, { AxiosResponse } from 'axios'; import { MemberEntity } from '../model/member'; -import Axios from 'axios'; const gitHubURL = 'https://api.github.com'; const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; @@ -47,7 +47,7 @@ const getAllMembers = (): Promise => { const promise: Promise = new Promise((resolve, reject) => { try { Axios.get(gitHubMembersUrl) - .then(response => resolve(mapMemberListApiToModel(response.data))); + .then(response => resolve(mapMemberListApiToModel(response))); } catch (ex) { reject(ex); } @@ -56,13 +56,14 @@ const getAllMembers = (): Promise => { return promise; }; -const mapMemberListApiToModel = (data: MemberEntity[]) => +const mapMemberListApiToModel = ({ data }: AxiosResponse) => data.map(gitHubMember => gitHubMember); export const memberAPI = { getAllMembers, }; + ``` - Añadimos un nuevo componente _memberHead_ para crear la cabecera de la tabla: diff --git a/12_TableHttp_Axios/src/api/memberAPI.ts b/12_TableHttp_Axios/src/api/memberAPI.ts index 033bf31..b860d7f 100644 --- a/12_TableHttp_Axios/src/api/memberAPI.ts +++ b/12_TableHttp_Axios/src/api/memberAPI.ts @@ -1,5 +1,5 @@ +import Axios, { AxiosResponse } from 'axios'; import { MemberEntity } from '../model/member'; -import Axios from 'axios'; const gitHubURL = 'https://api.github.com'; const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`; @@ -8,7 +8,7 @@ const getAllMembers = (): Promise => { const promise: Promise = new Promise((resolve, reject) => { try { Axios.get(gitHubMembersUrl) - .then(response => resolve(mapMemberListApiToModel(response.data))); + .then(response => resolve(mapMemberListApiToModel(response))); } catch (ex) { reject(ex); } @@ -17,7 +17,7 @@ const getAllMembers = (): Promise => { return promise; }; -const mapMemberListApiToModel = (data: MemberEntity[]) => +const mapMemberListApiToModel = ({ data }: AxiosResponse) => data.map(gitHubMember => gitHubMember); export const memberAPI = { From cbff1ee3a1984d2e4dd48c2845e4aad2612a22df Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Tue, 4 Dec 2018 12:57:22 +0100 Subject: [PATCH 167/180] Update readme.md --- 11_TableMock/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/11_TableMock/readme.md b/11_TableMock/readme.md index b1f7d4f..b65c53f 100644 --- a/11_TableMock/readme.md +++ b/11_TableMock/readme.md @@ -53,7 +53,7 @@ _./src/api/memberMockData.ts_ ```javascript import {MemberEntity} from '../model/member'; -var MembersMockData : MemberEntity[] = +const MembersMockData : MemberEntity[] = [ { id: 1457912, From 907b49c81bf13172aa232173d0aedb13f6c0ebec Mon Sep 17 00:00:00 2001 From: tgrrr Date: Wed, 5 Dec 2018 16:33:48 +1100 Subject: [PATCH 168/180] - Update broken links in readme.md - Relative links in readme - Renamed some folders to follow 05_Refactor rather than 05 Refactor pattern --- {16 Validation => 16_Validation}/.babelrc | 0 {16 Validation => 16_Validation}/package.json | 0 {16 Validation => 16_Validation}/readme.md | 0 .../src/api/login.ts | 0 .../src/common/forms/textFieldForm.tsx | 0 .../src/common/index.tsx | 0 .../src/common/notification.tsx | 0 .../src/hello.tsx | 0 .../src/index.html | 0 {16 Validation => 16_Validation}/src/main.tsx | 0 .../src/model/login.ts | 0 .../src/nameEdit.tsx | 0 .../src/pages/b/index.ts | 0 .../src/pages/b/pageB.tsx | 0 .../src/pages/login/index.ts | 0 .../src/pages/login/loginForm.tsx | 0 .../src/pages/login/loginPage.tsx | 0 .../src/pages/login/loginValidations.ts | 0 .../src/pages/login/viewmodel.ts | 0 .../tsconfig.json | 0 .../webpack.config.js | 0 {17 Context => 17_Context}/.babelrc | 0 {17 Context => 17_Context}/Readme.md | 0 {17 Context => 17_Context}/package.json | 0 {17 Context => 17_Context}/readme_es.md | 0 {17 Context => 17_Context}/src/api/login.ts | 0 .../src/common/forms/textFieldForm.tsx | 0 .../src/common/index.tsx | 0 .../src/common/notification.tsx | 0 .../src/common/sessionContext.tsx | 0 {17 Context => 17_Context}/src/hello.tsx | 0 {17 Context => 17_Context}/src/index.html | 0 {17 Context => 17_Context}/src/main.tsx | 0 {17 Context => 17_Context}/src/model/login.ts | 0 {17 Context => 17_Context}/src/nameEdit.tsx | 0 .../src/pages/b/index.ts | 0 .../src/pages/b/pageB.tsx | 0 .../src/pages/login/index.ts | 0 .../src/pages/login/loginForm.tsx | 0 .../src/pages/login/loginPage.tsx | 0 .../src/pages/login/loginValidations.ts | 0 .../src/pages/login/viewmodel.ts | 0 {17 Context => 17_Context}/tsconfig.json | 0 {17 Context => 17_Context}/webpack.config.js | 0 {18 Hoc => 18_Hoc}/.babelrc | 0 {18 Hoc => 18_Hoc}/Readme.md | 0 {18 Hoc => 18_Hoc}/package.json | 0 {18 Hoc => 18_Hoc}/readme_es.md | 0 {18 Hoc => 18_Hoc}/src/api/login.ts | 0 .../src/common/forms/textFieldForm.tsx | 0 {18 Hoc => 18_Hoc}/src/common/index.tsx | 0 .../src/common/notification.tsx | 0 .../src/common/sessionContext.tsx | 0 {18 Hoc => 18_Hoc}/src/hello.tsx | 0 {18 Hoc => 18_Hoc}/src/index.html | 0 {18 Hoc => 18_Hoc}/src/main.tsx | 0 {18 Hoc => 18_Hoc}/src/model/login.ts | 0 {18 Hoc => 18_Hoc}/src/nameEdit.tsx | 0 {18 Hoc => 18_Hoc}/src/pages/b/index.ts | 0 {18 Hoc => 18_Hoc}/src/pages/b/pageB.tsx | 0 {18 Hoc => 18_Hoc}/src/pages/login/index.ts | 0 .../src/pages/login/loginForm.tsx | 0 .../src/pages/login/loginPage.tsx | 0 .../src/pages/login/loginValidations.ts | 0 .../src/pages/login/viewmodel.ts | 0 {18 Hoc => 18_Hoc}/tsconfig.json | 0 {18 Hoc => 18_Hoc}/webpack.config.js | 0 {19 RenderProps => 19_RenderProps}/.babelrc | 0 {19 RenderProps => 19_RenderProps}/Readme.md | 0 .../package.json | 0 .../readme_es.md | 0 .../src/api/login.ts | 0 .../src/common/forms/textFieldForm.tsx | 0 .../src/common/index.tsx | 0 .../src/common/notification.tsx | 0 .../src/common/sessionContext.tsx | 0 .../src/hello.tsx | 0 .../src/index.html | 0 .../src/main.tsx | 0 .../src/model/login.ts | 0 .../src/nameEdit.tsx | 0 .../src/pages/b/index.ts | 0 .../src/pages/b/pageB.tsx | 0 .../src/pages/login/index.ts | 0 .../src/pages/login/loginForm.tsx | 0 .../src/pages/login/loginPage.tsx | 0 .../src/pages/login/loginValidations.ts | 0 .../src/pages/login/viewmodel.ts | 0 .../tsconfig.json | 0 .../webpack.config.js | 0 .../.babelrc | 0 .../package.json | 0 .../readme.md | 0 .../readme_es.md | 0 .../src/app.tsx | 0 .../src/errorBoundary.tsx | 0 .../src/faultyComponent.tsx | 0 .../src/hello.tsx | 0 .../src/index.html | 0 .../src/main.tsx | 0 .../src/nameEdit.tsx | 0 .../tsconfig.json | 0 .../webpack.config.js | 0 {21 Hooks => 21_Hooks}/.babelrc | 0 {21 Hooks => 21_Hooks}/package.json | 0 {21 Hooks => 21_Hooks}/readme.md | 2 +- {21 Hooks => 21_Hooks}/readme_es.md | 0 {21 Hooks => 21_Hooks}/src/api/memberAPI.ts | 0 {21 Hooks => 21_Hooks}/src/app.tsx | 0 {21 Hooks => 21_Hooks}/src/hello.tsx | 0 {21 Hooks => 21_Hooks}/src/index.html | 0 {21 Hooks => 21_Hooks}/src/main.tsx | 0 {21 Hooks => 21_Hooks}/src/memberHead.tsx | 0 {21 Hooks => 21_Hooks}/src/memberRow.tsx | 0 {21 Hooks => 21_Hooks}/src/membersTable.tsx | 0 {21 Hooks => 21_Hooks}/src/model/member.ts | 0 .../src/model/memberMockData.ts | 0 {21 Hooks => 21_Hooks}/src/nameEdit.tsx | 0 {21 Hooks => 21_Hooks}/tsconfig.json | 0 {21 Hooks => 21_Hooks}/webpack.config.js | 0 readme.md | 38 +++++++++---------- 121 files changed, 20 insertions(+), 20 deletions(-) rename {16 Validation => 16_Validation}/.babelrc (100%) rename {16 Validation => 16_Validation}/package.json (100%) rename {16 Validation => 16_Validation}/readme.md (100%) rename {16 Validation => 16_Validation}/src/api/login.ts (100%) rename {16 Validation => 16_Validation}/src/common/forms/textFieldForm.tsx (100%) rename {16 Validation => 16_Validation}/src/common/index.tsx (100%) rename {16 Validation => 16_Validation}/src/common/notification.tsx (100%) rename {16 Validation => 16_Validation}/src/hello.tsx (100%) rename {16 Validation => 16_Validation}/src/index.html (100%) rename {16 Validation => 16_Validation}/src/main.tsx (100%) rename {16 Validation => 16_Validation}/src/model/login.ts (100%) rename {16 Validation => 16_Validation}/src/nameEdit.tsx (100%) rename {16 Validation => 16_Validation}/src/pages/b/index.ts (100%) rename {16 Validation => 16_Validation}/src/pages/b/pageB.tsx (100%) rename {16 Validation => 16_Validation}/src/pages/login/index.ts (100%) rename {16 Validation => 16_Validation}/src/pages/login/loginForm.tsx (100%) rename {16 Validation => 16_Validation}/src/pages/login/loginPage.tsx (100%) rename {16 Validation => 16_Validation}/src/pages/login/loginValidations.ts (100%) rename {16 Validation => 16_Validation}/src/pages/login/viewmodel.ts (100%) rename {16 Validation => 16_Validation}/tsconfig.json (100%) rename {16 Validation => 16_Validation}/webpack.config.js (100%) rename {17 Context => 17_Context}/.babelrc (100%) rename {17 Context => 17_Context}/Readme.md (100%) rename {17 Context => 17_Context}/package.json (100%) rename {17 Context => 17_Context}/readme_es.md (100%) rename {17 Context => 17_Context}/src/api/login.ts (100%) rename {17 Context => 17_Context}/src/common/forms/textFieldForm.tsx (100%) rename {17 Context => 17_Context}/src/common/index.tsx (100%) rename {17 Context => 17_Context}/src/common/notification.tsx (100%) rename {17 Context => 17_Context}/src/common/sessionContext.tsx (100%) rename {17 Context => 17_Context}/src/hello.tsx (100%) rename {17 Context => 17_Context}/src/index.html (100%) rename {17 Context => 17_Context}/src/main.tsx (100%) rename {17 Context => 17_Context}/src/model/login.ts (100%) rename {17 Context => 17_Context}/src/nameEdit.tsx (100%) rename {17 Context => 17_Context}/src/pages/b/index.ts (100%) rename {17 Context => 17_Context}/src/pages/b/pageB.tsx (100%) rename {17 Context => 17_Context}/src/pages/login/index.ts (100%) rename {17 Context => 17_Context}/src/pages/login/loginForm.tsx (100%) rename {17 Context => 17_Context}/src/pages/login/loginPage.tsx (100%) rename {17 Context => 17_Context}/src/pages/login/loginValidations.ts (100%) rename {17 Context => 17_Context}/src/pages/login/viewmodel.ts (100%) rename {17 Context => 17_Context}/tsconfig.json (100%) rename {17 Context => 17_Context}/webpack.config.js (100%) rename {18 Hoc => 18_Hoc}/.babelrc (100%) rename {18 Hoc => 18_Hoc}/Readme.md (100%) rename {18 Hoc => 18_Hoc}/package.json (100%) rename {18 Hoc => 18_Hoc}/readme_es.md (100%) rename {18 Hoc => 18_Hoc}/src/api/login.ts (100%) rename {18 Hoc => 18_Hoc}/src/common/forms/textFieldForm.tsx (100%) rename {18 Hoc => 18_Hoc}/src/common/index.tsx (100%) rename {18 Hoc => 18_Hoc}/src/common/notification.tsx (100%) rename {18 Hoc => 18_Hoc}/src/common/sessionContext.tsx (100%) rename {18 Hoc => 18_Hoc}/src/hello.tsx (100%) rename {18 Hoc => 18_Hoc}/src/index.html (100%) rename {18 Hoc => 18_Hoc}/src/main.tsx (100%) rename {18 Hoc => 18_Hoc}/src/model/login.ts (100%) rename {18 Hoc => 18_Hoc}/src/nameEdit.tsx (100%) rename {18 Hoc => 18_Hoc}/src/pages/b/index.ts (100%) rename {18 Hoc => 18_Hoc}/src/pages/b/pageB.tsx (100%) rename {18 Hoc => 18_Hoc}/src/pages/login/index.ts (100%) rename {18 Hoc => 18_Hoc}/src/pages/login/loginForm.tsx (100%) rename {18 Hoc => 18_Hoc}/src/pages/login/loginPage.tsx (100%) rename {18 Hoc => 18_Hoc}/src/pages/login/loginValidations.ts (100%) rename {18 Hoc => 18_Hoc}/src/pages/login/viewmodel.ts (100%) rename {18 Hoc => 18_Hoc}/tsconfig.json (100%) rename {18 Hoc => 18_Hoc}/webpack.config.js (100%) rename {19 RenderProps => 19_RenderProps}/.babelrc (100%) rename {19 RenderProps => 19_RenderProps}/Readme.md (100%) rename {19 RenderProps => 19_RenderProps}/package.json (100%) rename {19 RenderProps => 19_RenderProps}/readme_es.md (100%) rename {19 RenderProps => 19_RenderProps}/src/api/login.ts (100%) rename {19 RenderProps => 19_RenderProps}/src/common/forms/textFieldForm.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/common/index.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/common/notification.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/common/sessionContext.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/hello.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/index.html (100%) rename {19 RenderProps => 19_RenderProps}/src/main.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/model/login.ts (100%) rename {19 RenderProps => 19_RenderProps}/src/nameEdit.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/b/index.ts (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/b/pageB.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/login/index.ts (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/login/loginForm.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/login/loginPage.tsx (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/login/loginValidations.ts (100%) rename {19 RenderProps => 19_RenderProps}/src/pages/login/viewmodel.ts (100%) rename {19 RenderProps => 19_RenderProps}/tsconfig.json (100%) rename {19 RenderProps => 19_RenderProps}/webpack.config.js (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/.babelrc (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/package.json (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/readme.md (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/readme_es.md (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/app.tsx (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/errorBoundary.tsx (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/faultyComponent.tsx (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/hello.tsx (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/index.html (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/main.tsx (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/src/nameEdit.tsx (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/tsconfig.json (100%) rename {20 ErrorBoundaries => 20_ErrorBoundaries}/webpack.config.js (100%) rename {21 Hooks => 21_Hooks}/.babelrc (100%) rename {21 Hooks => 21_Hooks}/package.json (100%) rename {21 Hooks => 21_Hooks}/readme.md (97%) rename {21 Hooks => 21_Hooks}/readme_es.md (100%) rename {21 Hooks => 21_Hooks}/src/api/memberAPI.ts (100%) rename {21 Hooks => 21_Hooks}/src/app.tsx (100%) rename {21 Hooks => 21_Hooks}/src/hello.tsx (100%) rename {21 Hooks => 21_Hooks}/src/index.html (100%) rename {21 Hooks => 21_Hooks}/src/main.tsx (100%) rename {21 Hooks => 21_Hooks}/src/memberHead.tsx (100%) rename {21 Hooks => 21_Hooks}/src/memberRow.tsx (100%) rename {21 Hooks => 21_Hooks}/src/membersTable.tsx (100%) rename {21 Hooks => 21_Hooks}/src/model/member.ts (100%) rename {21 Hooks => 21_Hooks}/src/model/memberMockData.ts (100%) rename {21 Hooks => 21_Hooks}/src/nameEdit.tsx (100%) rename {21 Hooks => 21_Hooks}/tsconfig.json (100%) rename {21 Hooks => 21_Hooks}/webpack.config.js (100%) diff --git a/16 Validation/.babelrc b/16_Validation/.babelrc similarity index 100% rename from 16 Validation/.babelrc rename to 16_Validation/.babelrc diff --git a/16 Validation/package.json b/16_Validation/package.json similarity index 100% rename from 16 Validation/package.json rename to 16_Validation/package.json diff --git a/16 Validation/readme.md b/16_Validation/readme.md similarity index 100% rename from 16 Validation/readme.md rename to 16_Validation/readme.md diff --git a/16 Validation/src/api/login.ts b/16_Validation/src/api/login.ts similarity index 100% rename from 16 Validation/src/api/login.ts rename to 16_Validation/src/api/login.ts diff --git a/16 Validation/src/common/forms/textFieldForm.tsx b/16_Validation/src/common/forms/textFieldForm.tsx similarity index 100% rename from 16 Validation/src/common/forms/textFieldForm.tsx rename to 16_Validation/src/common/forms/textFieldForm.tsx diff --git a/16 Validation/src/common/index.tsx b/16_Validation/src/common/index.tsx similarity index 100% rename from 16 Validation/src/common/index.tsx rename to 16_Validation/src/common/index.tsx diff --git a/16 Validation/src/common/notification.tsx b/16_Validation/src/common/notification.tsx similarity index 100% rename from 16 Validation/src/common/notification.tsx rename to 16_Validation/src/common/notification.tsx diff --git a/16 Validation/src/hello.tsx b/16_Validation/src/hello.tsx similarity index 100% rename from 16 Validation/src/hello.tsx rename to 16_Validation/src/hello.tsx diff --git a/16 Validation/src/index.html b/16_Validation/src/index.html similarity index 100% rename from 16 Validation/src/index.html rename to 16_Validation/src/index.html diff --git a/16 Validation/src/main.tsx b/16_Validation/src/main.tsx similarity index 100% rename from 16 Validation/src/main.tsx rename to 16_Validation/src/main.tsx diff --git a/16 Validation/src/model/login.ts b/16_Validation/src/model/login.ts similarity index 100% rename from 16 Validation/src/model/login.ts rename to 16_Validation/src/model/login.ts diff --git a/16 Validation/src/nameEdit.tsx b/16_Validation/src/nameEdit.tsx similarity index 100% rename from 16 Validation/src/nameEdit.tsx rename to 16_Validation/src/nameEdit.tsx diff --git a/16 Validation/src/pages/b/index.ts b/16_Validation/src/pages/b/index.ts similarity index 100% rename from 16 Validation/src/pages/b/index.ts rename to 16_Validation/src/pages/b/index.ts diff --git a/16 Validation/src/pages/b/pageB.tsx b/16_Validation/src/pages/b/pageB.tsx similarity index 100% rename from 16 Validation/src/pages/b/pageB.tsx rename to 16_Validation/src/pages/b/pageB.tsx diff --git a/16 Validation/src/pages/login/index.ts b/16_Validation/src/pages/login/index.ts similarity index 100% rename from 16 Validation/src/pages/login/index.ts rename to 16_Validation/src/pages/login/index.ts diff --git a/16 Validation/src/pages/login/loginForm.tsx b/16_Validation/src/pages/login/loginForm.tsx similarity index 100% rename from 16 Validation/src/pages/login/loginForm.tsx rename to 16_Validation/src/pages/login/loginForm.tsx diff --git a/16 Validation/src/pages/login/loginPage.tsx b/16_Validation/src/pages/login/loginPage.tsx similarity index 100% rename from 16 Validation/src/pages/login/loginPage.tsx rename to 16_Validation/src/pages/login/loginPage.tsx diff --git a/16 Validation/src/pages/login/loginValidations.ts b/16_Validation/src/pages/login/loginValidations.ts similarity index 100% rename from 16 Validation/src/pages/login/loginValidations.ts rename to 16_Validation/src/pages/login/loginValidations.ts diff --git a/16 Validation/src/pages/login/viewmodel.ts b/16_Validation/src/pages/login/viewmodel.ts similarity index 100% rename from 16 Validation/src/pages/login/viewmodel.ts rename to 16_Validation/src/pages/login/viewmodel.ts diff --git a/16 Validation/tsconfig.json b/16_Validation/tsconfig.json similarity index 100% rename from 16 Validation/tsconfig.json rename to 16_Validation/tsconfig.json diff --git a/16 Validation/webpack.config.js b/16_Validation/webpack.config.js similarity index 100% rename from 16 Validation/webpack.config.js rename to 16_Validation/webpack.config.js diff --git a/17 Context/.babelrc b/17_Context/.babelrc similarity index 100% rename from 17 Context/.babelrc rename to 17_Context/.babelrc diff --git a/17 Context/Readme.md b/17_Context/Readme.md similarity index 100% rename from 17 Context/Readme.md rename to 17_Context/Readme.md diff --git a/17 Context/package.json b/17_Context/package.json similarity index 100% rename from 17 Context/package.json rename to 17_Context/package.json diff --git a/17 Context/readme_es.md b/17_Context/readme_es.md similarity index 100% rename from 17 Context/readme_es.md rename to 17_Context/readme_es.md diff --git a/17 Context/src/api/login.ts b/17_Context/src/api/login.ts similarity index 100% rename from 17 Context/src/api/login.ts rename to 17_Context/src/api/login.ts diff --git a/17 Context/src/common/forms/textFieldForm.tsx b/17_Context/src/common/forms/textFieldForm.tsx similarity index 100% rename from 17 Context/src/common/forms/textFieldForm.tsx rename to 17_Context/src/common/forms/textFieldForm.tsx diff --git a/17 Context/src/common/index.tsx b/17_Context/src/common/index.tsx similarity index 100% rename from 17 Context/src/common/index.tsx rename to 17_Context/src/common/index.tsx diff --git a/17 Context/src/common/notification.tsx b/17_Context/src/common/notification.tsx similarity index 100% rename from 17 Context/src/common/notification.tsx rename to 17_Context/src/common/notification.tsx diff --git a/17 Context/src/common/sessionContext.tsx b/17_Context/src/common/sessionContext.tsx similarity index 100% rename from 17 Context/src/common/sessionContext.tsx rename to 17_Context/src/common/sessionContext.tsx diff --git a/17 Context/src/hello.tsx b/17_Context/src/hello.tsx similarity index 100% rename from 17 Context/src/hello.tsx rename to 17_Context/src/hello.tsx diff --git a/17 Context/src/index.html b/17_Context/src/index.html similarity index 100% rename from 17 Context/src/index.html rename to 17_Context/src/index.html diff --git a/17 Context/src/main.tsx b/17_Context/src/main.tsx similarity index 100% rename from 17 Context/src/main.tsx rename to 17_Context/src/main.tsx diff --git a/17 Context/src/model/login.ts b/17_Context/src/model/login.ts similarity index 100% rename from 17 Context/src/model/login.ts rename to 17_Context/src/model/login.ts diff --git a/17 Context/src/nameEdit.tsx b/17_Context/src/nameEdit.tsx similarity index 100% rename from 17 Context/src/nameEdit.tsx rename to 17_Context/src/nameEdit.tsx diff --git a/17 Context/src/pages/b/index.ts b/17_Context/src/pages/b/index.ts similarity index 100% rename from 17 Context/src/pages/b/index.ts rename to 17_Context/src/pages/b/index.ts diff --git a/17 Context/src/pages/b/pageB.tsx b/17_Context/src/pages/b/pageB.tsx similarity index 100% rename from 17 Context/src/pages/b/pageB.tsx rename to 17_Context/src/pages/b/pageB.tsx diff --git a/17 Context/src/pages/login/index.ts b/17_Context/src/pages/login/index.ts similarity index 100% rename from 17 Context/src/pages/login/index.ts rename to 17_Context/src/pages/login/index.ts diff --git a/17 Context/src/pages/login/loginForm.tsx b/17_Context/src/pages/login/loginForm.tsx similarity index 100% rename from 17 Context/src/pages/login/loginForm.tsx rename to 17_Context/src/pages/login/loginForm.tsx diff --git a/17 Context/src/pages/login/loginPage.tsx b/17_Context/src/pages/login/loginPage.tsx similarity index 100% rename from 17 Context/src/pages/login/loginPage.tsx rename to 17_Context/src/pages/login/loginPage.tsx diff --git a/17 Context/src/pages/login/loginValidations.ts b/17_Context/src/pages/login/loginValidations.ts similarity index 100% rename from 17 Context/src/pages/login/loginValidations.ts rename to 17_Context/src/pages/login/loginValidations.ts diff --git a/17 Context/src/pages/login/viewmodel.ts b/17_Context/src/pages/login/viewmodel.ts similarity index 100% rename from 17 Context/src/pages/login/viewmodel.ts rename to 17_Context/src/pages/login/viewmodel.ts diff --git a/17 Context/tsconfig.json b/17_Context/tsconfig.json similarity index 100% rename from 17 Context/tsconfig.json rename to 17_Context/tsconfig.json diff --git a/17 Context/webpack.config.js b/17_Context/webpack.config.js similarity index 100% rename from 17 Context/webpack.config.js rename to 17_Context/webpack.config.js diff --git a/18 Hoc/.babelrc b/18_Hoc/.babelrc similarity index 100% rename from 18 Hoc/.babelrc rename to 18_Hoc/.babelrc diff --git a/18 Hoc/Readme.md b/18_Hoc/Readme.md similarity index 100% rename from 18 Hoc/Readme.md rename to 18_Hoc/Readme.md diff --git a/18 Hoc/package.json b/18_Hoc/package.json similarity index 100% rename from 18 Hoc/package.json rename to 18_Hoc/package.json diff --git a/18 Hoc/readme_es.md b/18_Hoc/readme_es.md similarity index 100% rename from 18 Hoc/readme_es.md rename to 18_Hoc/readme_es.md diff --git a/18 Hoc/src/api/login.ts b/18_Hoc/src/api/login.ts similarity index 100% rename from 18 Hoc/src/api/login.ts rename to 18_Hoc/src/api/login.ts diff --git a/18 Hoc/src/common/forms/textFieldForm.tsx b/18_Hoc/src/common/forms/textFieldForm.tsx similarity index 100% rename from 18 Hoc/src/common/forms/textFieldForm.tsx rename to 18_Hoc/src/common/forms/textFieldForm.tsx diff --git a/18 Hoc/src/common/index.tsx b/18_Hoc/src/common/index.tsx similarity index 100% rename from 18 Hoc/src/common/index.tsx rename to 18_Hoc/src/common/index.tsx diff --git a/18 Hoc/src/common/notification.tsx b/18_Hoc/src/common/notification.tsx similarity index 100% rename from 18 Hoc/src/common/notification.tsx rename to 18_Hoc/src/common/notification.tsx diff --git a/18 Hoc/src/common/sessionContext.tsx b/18_Hoc/src/common/sessionContext.tsx similarity index 100% rename from 18 Hoc/src/common/sessionContext.tsx rename to 18_Hoc/src/common/sessionContext.tsx diff --git a/18 Hoc/src/hello.tsx b/18_Hoc/src/hello.tsx similarity index 100% rename from 18 Hoc/src/hello.tsx rename to 18_Hoc/src/hello.tsx diff --git a/18 Hoc/src/index.html b/18_Hoc/src/index.html similarity index 100% rename from 18 Hoc/src/index.html rename to 18_Hoc/src/index.html diff --git a/18 Hoc/src/main.tsx b/18_Hoc/src/main.tsx similarity index 100% rename from 18 Hoc/src/main.tsx rename to 18_Hoc/src/main.tsx diff --git a/18 Hoc/src/model/login.ts b/18_Hoc/src/model/login.ts similarity index 100% rename from 18 Hoc/src/model/login.ts rename to 18_Hoc/src/model/login.ts diff --git a/18 Hoc/src/nameEdit.tsx b/18_Hoc/src/nameEdit.tsx similarity index 100% rename from 18 Hoc/src/nameEdit.tsx rename to 18_Hoc/src/nameEdit.tsx diff --git a/18 Hoc/src/pages/b/index.ts b/18_Hoc/src/pages/b/index.ts similarity index 100% rename from 18 Hoc/src/pages/b/index.ts rename to 18_Hoc/src/pages/b/index.ts diff --git a/18 Hoc/src/pages/b/pageB.tsx b/18_Hoc/src/pages/b/pageB.tsx similarity index 100% rename from 18 Hoc/src/pages/b/pageB.tsx rename to 18_Hoc/src/pages/b/pageB.tsx diff --git a/18 Hoc/src/pages/login/index.ts b/18_Hoc/src/pages/login/index.ts similarity index 100% rename from 18 Hoc/src/pages/login/index.ts rename to 18_Hoc/src/pages/login/index.ts diff --git a/18 Hoc/src/pages/login/loginForm.tsx b/18_Hoc/src/pages/login/loginForm.tsx similarity index 100% rename from 18 Hoc/src/pages/login/loginForm.tsx rename to 18_Hoc/src/pages/login/loginForm.tsx diff --git a/18 Hoc/src/pages/login/loginPage.tsx b/18_Hoc/src/pages/login/loginPage.tsx similarity index 100% rename from 18 Hoc/src/pages/login/loginPage.tsx rename to 18_Hoc/src/pages/login/loginPage.tsx diff --git a/18 Hoc/src/pages/login/loginValidations.ts b/18_Hoc/src/pages/login/loginValidations.ts similarity index 100% rename from 18 Hoc/src/pages/login/loginValidations.ts rename to 18_Hoc/src/pages/login/loginValidations.ts diff --git a/18 Hoc/src/pages/login/viewmodel.ts b/18_Hoc/src/pages/login/viewmodel.ts similarity index 100% rename from 18 Hoc/src/pages/login/viewmodel.ts rename to 18_Hoc/src/pages/login/viewmodel.ts diff --git a/18 Hoc/tsconfig.json b/18_Hoc/tsconfig.json similarity index 100% rename from 18 Hoc/tsconfig.json rename to 18_Hoc/tsconfig.json diff --git a/18 Hoc/webpack.config.js b/18_Hoc/webpack.config.js similarity index 100% rename from 18 Hoc/webpack.config.js rename to 18_Hoc/webpack.config.js diff --git a/19 RenderProps/.babelrc b/19_RenderProps/.babelrc similarity index 100% rename from 19 RenderProps/.babelrc rename to 19_RenderProps/.babelrc diff --git a/19 RenderProps/Readme.md b/19_RenderProps/Readme.md similarity index 100% rename from 19 RenderProps/Readme.md rename to 19_RenderProps/Readme.md diff --git a/19 RenderProps/package.json b/19_RenderProps/package.json similarity index 100% rename from 19 RenderProps/package.json rename to 19_RenderProps/package.json diff --git a/19 RenderProps/readme_es.md b/19_RenderProps/readme_es.md similarity index 100% rename from 19 RenderProps/readme_es.md rename to 19_RenderProps/readme_es.md diff --git a/19 RenderProps/src/api/login.ts b/19_RenderProps/src/api/login.ts similarity index 100% rename from 19 RenderProps/src/api/login.ts rename to 19_RenderProps/src/api/login.ts diff --git a/19 RenderProps/src/common/forms/textFieldForm.tsx b/19_RenderProps/src/common/forms/textFieldForm.tsx similarity index 100% rename from 19 RenderProps/src/common/forms/textFieldForm.tsx rename to 19_RenderProps/src/common/forms/textFieldForm.tsx diff --git a/19 RenderProps/src/common/index.tsx b/19_RenderProps/src/common/index.tsx similarity index 100% rename from 19 RenderProps/src/common/index.tsx rename to 19_RenderProps/src/common/index.tsx diff --git a/19 RenderProps/src/common/notification.tsx b/19_RenderProps/src/common/notification.tsx similarity index 100% rename from 19 RenderProps/src/common/notification.tsx rename to 19_RenderProps/src/common/notification.tsx diff --git a/19 RenderProps/src/common/sessionContext.tsx b/19_RenderProps/src/common/sessionContext.tsx similarity index 100% rename from 19 RenderProps/src/common/sessionContext.tsx rename to 19_RenderProps/src/common/sessionContext.tsx diff --git a/19 RenderProps/src/hello.tsx b/19_RenderProps/src/hello.tsx similarity index 100% rename from 19 RenderProps/src/hello.tsx rename to 19_RenderProps/src/hello.tsx diff --git a/19 RenderProps/src/index.html b/19_RenderProps/src/index.html similarity index 100% rename from 19 RenderProps/src/index.html rename to 19_RenderProps/src/index.html diff --git a/19 RenderProps/src/main.tsx b/19_RenderProps/src/main.tsx similarity index 100% rename from 19 RenderProps/src/main.tsx rename to 19_RenderProps/src/main.tsx diff --git a/19 RenderProps/src/model/login.ts b/19_RenderProps/src/model/login.ts similarity index 100% rename from 19 RenderProps/src/model/login.ts rename to 19_RenderProps/src/model/login.ts diff --git a/19 RenderProps/src/nameEdit.tsx b/19_RenderProps/src/nameEdit.tsx similarity index 100% rename from 19 RenderProps/src/nameEdit.tsx rename to 19_RenderProps/src/nameEdit.tsx diff --git a/19 RenderProps/src/pages/b/index.ts b/19_RenderProps/src/pages/b/index.ts similarity index 100% rename from 19 RenderProps/src/pages/b/index.ts rename to 19_RenderProps/src/pages/b/index.ts diff --git a/19 RenderProps/src/pages/b/pageB.tsx b/19_RenderProps/src/pages/b/pageB.tsx similarity index 100% rename from 19 RenderProps/src/pages/b/pageB.tsx rename to 19_RenderProps/src/pages/b/pageB.tsx diff --git a/19 RenderProps/src/pages/login/index.ts b/19_RenderProps/src/pages/login/index.ts similarity index 100% rename from 19 RenderProps/src/pages/login/index.ts rename to 19_RenderProps/src/pages/login/index.ts diff --git a/19 RenderProps/src/pages/login/loginForm.tsx b/19_RenderProps/src/pages/login/loginForm.tsx similarity index 100% rename from 19 RenderProps/src/pages/login/loginForm.tsx rename to 19_RenderProps/src/pages/login/loginForm.tsx diff --git a/19 RenderProps/src/pages/login/loginPage.tsx b/19_RenderProps/src/pages/login/loginPage.tsx similarity index 100% rename from 19 RenderProps/src/pages/login/loginPage.tsx rename to 19_RenderProps/src/pages/login/loginPage.tsx diff --git a/19 RenderProps/src/pages/login/loginValidations.ts b/19_RenderProps/src/pages/login/loginValidations.ts similarity index 100% rename from 19 RenderProps/src/pages/login/loginValidations.ts rename to 19_RenderProps/src/pages/login/loginValidations.ts diff --git a/19 RenderProps/src/pages/login/viewmodel.ts b/19_RenderProps/src/pages/login/viewmodel.ts similarity index 100% rename from 19 RenderProps/src/pages/login/viewmodel.ts rename to 19_RenderProps/src/pages/login/viewmodel.ts diff --git a/19 RenderProps/tsconfig.json b/19_RenderProps/tsconfig.json similarity index 100% rename from 19 RenderProps/tsconfig.json rename to 19_RenderProps/tsconfig.json diff --git a/19 RenderProps/webpack.config.js b/19_RenderProps/webpack.config.js similarity index 100% rename from 19 RenderProps/webpack.config.js rename to 19_RenderProps/webpack.config.js diff --git a/20 ErrorBoundaries/.babelrc b/20_ErrorBoundaries/.babelrc similarity index 100% rename from 20 ErrorBoundaries/.babelrc rename to 20_ErrorBoundaries/.babelrc diff --git a/20 ErrorBoundaries/package.json b/20_ErrorBoundaries/package.json similarity index 100% rename from 20 ErrorBoundaries/package.json rename to 20_ErrorBoundaries/package.json diff --git a/20 ErrorBoundaries/readme.md b/20_ErrorBoundaries/readme.md similarity index 100% rename from 20 ErrorBoundaries/readme.md rename to 20_ErrorBoundaries/readme.md diff --git a/20 ErrorBoundaries/readme_es.md b/20_ErrorBoundaries/readme_es.md similarity index 100% rename from 20 ErrorBoundaries/readme_es.md rename to 20_ErrorBoundaries/readme_es.md diff --git a/20 ErrorBoundaries/src/app.tsx b/20_ErrorBoundaries/src/app.tsx similarity index 100% rename from 20 ErrorBoundaries/src/app.tsx rename to 20_ErrorBoundaries/src/app.tsx diff --git a/20 ErrorBoundaries/src/errorBoundary.tsx b/20_ErrorBoundaries/src/errorBoundary.tsx similarity index 100% rename from 20 ErrorBoundaries/src/errorBoundary.tsx rename to 20_ErrorBoundaries/src/errorBoundary.tsx diff --git a/20 ErrorBoundaries/src/faultyComponent.tsx b/20_ErrorBoundaries/src/faultyComponent.tsx similarity index 100% rename from 20 ErrorBoundaries/src/faultyComponent.tsx rename to 20_ErrorBoundaries/src/faultyComponent.tsx diff --git a/20 ErrorBoundaries/src/hello.tsx b/20_ErrorBoundaries/src/hello.tsx similarity index 100% rename from 20 ErrorBoundaries/src/hello.tsx rename to 20_ErrorBoundaries/src/hello.tsx diff --git a/20 ErrorBoundaries/src/index.html b/20_ErrorBoundaries/src/index.html similarity index 100% rename from 20 ErrorBoundaries/src/index.html rename to 20_ErrorBoundaries/src/index.html diff --git a/20 ErrorBoundaries/src/main.tsx b/20_ErrorBoundaries/src/main.tsx similarity index 100% rename from 20 ErrorBoundaries/src/main.tsx rename to 20_ErrorBoundaries/src/main.tsx diff --git a/20 ErrorBoundaries/src/nameEdit.tsx b/20_ErrorBoundaries/src/nameEdit.tsx similarity index 100% rename from 20 ErrorBoundaries/src/nameEdit.tsx rename to 20_ErrorBoundaries/src/nameEdit.tsx diff --git a/20 ErrorBoundaries/tsconfig.json b/20_ErrorBoundaries/tsconfig.json similarity index 100% rename from 20 ErrorBoundaries/tsconfig.json rename to 20_ErrorBoundaries/tsconfig.json diff --git a/20 ErrorBoundaries/webpack.config.js b/20_ErrorBoundaries/webpack.config.js similarity index 100% rename from 20 ErrorBoundaries/webpack.config.js rename to 20_ErrorBoundaries/webpack.config.js diff --git a/21 Hooks/.babelrc b/21_Hooks/.babelrc similarity index 100% rename from 21 Hooks/.babelrc rename to 21_Hooks/.babelrc diff --git a/21 Hooks/package.json b/21_Hooks/package.json similarity index 100% rename from 21 Hooks/package.json rename to 21_Hooks/package.json diff --git a/21 Hooks/readme.md b/21_Hooks/readme.md similarity index 97% rename from 21 Hooks/readme.md rename to 21_Hooks/readme.md index 6301103..de38e3c 100644 --- a/21 Hooks/readme.md +++ b/21_Hooks/readme.md @@ -91,7 +91,7 @@ _./src/membersTable.tsx_ } ``` -- Now we have tetchy issue... _componentDidMount_ we don't have this on hooks component, how can we dod that? To do that we can make use of react hooks _useEffect_. +- Now we have tetchy issue... _componentDidMount_ we don't have this on hooks component, how can we do that? To do that we can make use of react hooks _useEffect_. _./src/membersTable.tsx_ diff --git a/21 Hooks/readme_es.md b/21_Hooks/readme_es.md similarity index 100% rename from 21 Hooks/readme_es.md rename to 21_Hooks/readme_es.md diff --git a/21 Hooks/src/api/memberAPI.ts b/21_Hooks/src/api/memberAPI.ts similarity index 100% rename from 21 Hooks/src/api/memberAPI.ts rename to 21_Hooks/src/api/memberAPI.ts diff --git a/21 Hooks/src/app.tsx b/21_Hooks/src/app.tsx similarity index 100% rename from 21 Hooks/src/app.tsx rename to 21_Hooks/src/app.tsx diff --git a/21 Hooks/src/hello.tsx b/21_Hooks/src/hello.tsx similarity index 100% rename from 21 Hooks/src/hello.tsx rename to 21_Hooks/src/hello.tsx diff --git a/21 Hooks/src/index.html b/21_Hooks/src/index.html similarity index 100% rename from 21 Hooks/src/index.html rename to 21_Hooks/src/index.html diff --git a/21 Hooks/src/main.tsx b/21_Hooks/src/main.tsx similarity index 100% rename from 21 Hooks/src/main.tsx rename to 21_Hooks/src/main.tsx diff --git a/21 Hooks/src/memberHead.tsx b/21_Hooks/src/memberHead.tsx similarity index 100% rename from 21 Hooks/src/memberHead.tsx rename to 21_Hooks/src/memberHead.tsx diff --git a/21 Hooks/src/memberRow.tsx b/21_Hooks/src/memberRow.tsx similarity index 100% rename from 21 Hooks/src/memberRow.tsx rename to 21_Hooks/src/memberRow.tsx diff --git a/21 Hooks/src/membersTable.tsx b/21_Hooks/src/membersTable.tsx similarity index 100% rename from 21 Hooks/src/membersTable.tsx rename to 21_Hooks/src/membersTable.tsx diff --git a/21 Hooks/src/model/member.ts b/21_Hooks/src/model/member.ts similarity index 100% rename from 21 Hooks/src/model/member.ts rename to 21_Hooks/src/model/member.ts diff --git a/21 Hooks/src/model/memberMockData.ts b/21_Hooks/src/model/memberMockData.ts similarity index 100% rename from 21 Hooks/src/model/memberMockData.ts rename to 21_Hooks/src/model/memberMockData.ts diff --git a/21 Hooks/src/nameEdit.tsx b/21_Hooks/src/nameEdit.tsx similarity index 100% rename from 21 Hooks/src/nameEdit.tsx rename to 21_Hooks/src/nameEdit.tsx diff --git a/21 Hooks/tsconfig.json b/21_Hooks/tsconfig.json similarity index 100% rename from 21 Hooks/tsconfig.json rename to 21_Hooks/tsconfig.json diff --git a/21 Hooks/webpack.config.js b/21_Hooks/webpack.config.js similarity index 100% rename from 21 Hooks/webpack.config.js rename to 21_Hooks/webpack.config.js diff --git a/readme.md b/readme.md index aa51b5e..eb8126f 100644 --- a/readme.md +++ b/readme.md @@ -30,79 +30,79 @@ then cd to one of the demos projects. ## samples -### [00 Boiler plate](https://github.com/Lemoncode/react-by-sample/tree/master/00%20Boilerplate) +### [00 Boiler plate](react-by-sample/00_Boilerplate/readme.md) Bundling + npm start based on webpack. -### [01 Hello React](https://github.com/Lemoncode/react-by-sample/tree/master/01%20HelloReact) +### [01 Hello React](react-by-sample/01_HelloReact/readme.md) Hello world, simples react render sample. -### [02 Properties](https://github.com/Lemoncode/react-by-sample/tree/master/02%20Properties) +### [02 Properties](react-by-sample/02_Properties/readme.md) Introduce a basic React concept, handling properties. -### [03 State](https://github.com/Lemoncode/react-by-sample/tree/master/03%20State) +### [03 State](react-by-sample/03_State/readme.md) Introduce a basic React concept, handling State. -### [04 Callback](https://github.com/Lemoncode/react-by-sample/tree/master/04%20Callback) +### [04 Callback](react-by-sample/04_Callback/readme.md) Using callbacks. -### [05 Refactor](https://github.com/Lemoncode/react-by-sample/tree/master/05%20Refactor) +### [05 Refactor](react-by-sample/05_Refactor/readme.md) Refactor the job done. -### [06 Move Back To Stateless](https://github.com/Lemoncode/react-by-sample/tree/master/06%20MoveBackToStateless) +### [06 Move Back To Stateless](react-by-sample/06_MoveBackToStateless/readme.md) Remove state from a child control just to have clear governance of state. -### [07 Enable](https://github.com/Lemoncode/react-by-sample/tree/master/07%20Enable) +### [07 Enable](react-by-sample/07_Enable/readme.md) Enable/disable components. -### [08 ColorPicker](https://github.com/Lemoncode/react-by-sample/tree/master/08%20Colorpicker) +### [08 ColorPicker](react-by-sample/08_Colorpicker/readme.md) Simple color picker demo (show how properties work). -### [09 ColorPicker Refactor](https://github.com/Lemoncode/react-by-sample/tree/master/09%20ColorpRefactor) +### [09 ColorPicker Refactor](react-by-sample/09_ColorpRefactor/readme.md) ColorPicker refactor. -### [10 Sidebar](https://github.com/Lemoncode/react-by-sample/tree/master/10%20Sidebar) +### [10 Sidebar](react-by-sample/10_Sidebar/readme.md) Implementation of a single sidebar. -### [11 Table Mock](https://github.com/Lemoncode/react-by-sample/tree/master/11%20TableMock) +### [11 Table Mock](react-by-sample/11_TableMock/readme.md) Render a table and use a child component to render each row. -### [12 Table Http](https://github.com/Lemoncode/react-by-sample/tree/master/12%20TableHttp) +### [12 Table Http](react-by-sample/12_TableHttp/readme.md) Using Promises. -### [13 Should Update](https://github.com/Lemoncode/react-by-sample/tree/master/13%20ShouldUpdate) +### [13 Should Update](react-by-sample/13_ShouldUpdate/readme.md) Enhance rendering performance hooking to 'shouldComponentUpdate'. -### [14 React Router](https://github.com/Lemoncode/react-by-sample/tree/master/14%20ReactRouter) +### [14 React Router](react-by-sample/14_ReactRouter/readme.md) React Router navigation example. -### [15 Login Form](https://github.com/Lemoncode/react-by-sample/tree/master/15%20LoginForm) +### [15 Login Form](react-by-sample/15_LoginForm/readme.md) Basic implementation of a login page. -### [16 Validation](https://github.com/Lemoncode/react-by-sample/tree/master/16%20Validation) +### [16 Validation](react-by-sample/16_Validation/readme.md) React Form validation, using lc-form-validation library -### [17 Context](https://github.com/Lemoncode/react-by-sample/tree/master/17%20Context) +### [17 Context](react-by-sample/17_Context/readme.md) How to use React 16 context api. -### [18 HOC](https://github.com/Lemoncode/react-by-sample/tree/master/17%20Context) +### [18 HOC](react-by-sample/17_Context/readme.md) Hig Order component sample.. From 4d7bcc3cbeeaeb57b525ca518de788979182a6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20de=20la=20Pe=C3=B1a?= Date: Thu, 6 Dec 2018 19:24:55 +0000 Subject: [PATCH 169/180] Fixed polyfill whatwg-fetch in sample 12 and readme file updated --- 12_TableHttp/readme.md | 12 +++++++++++- 12_TableHttp/src/api/memberAPI.ts | 1 - 12_TableHttp/webpack.config.js | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/12_TableHttp/readme.md b/12_TableHttp/readme.md index d1dec19..ca0d4a2 100644 --- a/12_TableHttp/readme.md +++ b/12_TableHttp/readme.md @@ -34,13 +34,23 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are npm install whatwg-fetch --save-dev ``` +- In _webpack.config.js_ let's add the new _whatwg-fetch_ polyfill as entry point: + +_webpack.config.js_ + +```diff +entry: ['@babel/polyfill', ++ 'whatwg-fetch', + './main.tsx' +], +``` + - Let's replace _memberAPI_ load members with the fetch / promise one: _./src/api/memberAPI.ts_ ```javascript import { MemberEntity } from '../model/member'; -import {} from 'whatwg-fetch'; // Sync mock data API, inspired from: // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 diff --git a/12_TableHttp/src/api/memberAPI.ts b/12_TableHttp/src/api/memberAPI.ts index 6185ee0..1551ace 100644 --- a/12_TableHttp/src/api/memberAPI.ts +++ b/12_TableHttp/src/api/memberAPI.ts @@ -1,5 +1,4 @@ import {MemberEntity} from '../model/member'; -import {fetch} from 'whatwg-fetch'; // Sync mock data API, inspired from: // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 diff --git a/12_TableHttp/webpack.config.js b/12_TableHttp/webpack.config.js index 7c85f49..862fd32 100644 --- a/12_TableHttp/webpack.config.js +++ b/12_TableHttp/webpack.config.js @@ -11,6 +11,7 @@ module.exports = { extensions: ['.js', '.ts', '.tsx'] }, entry: ['@babel/polyfill', + 'whatwg-fetch', './main.tsx' ], output: { From e8662fdda30ac3b4d9b12d0156cfc966bb0f185a Mon Sep 17 00:00:00 2001 From: signed Date: Wed, 19 Dec 2018 13:02:39 +0100 Subject: [PATCH 170/180] editing name empty fix Add disabled state when `state.editingName === ''`, before the input get disabled for ever when `state.userName` was set to empty string. --- 07_Enable/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/07_Enable/readme.md b/07_Enable/readme.md index 6bf26af..8d911c1 100644 --- a/07_Enable/readme.md +++ b/07_Enable/readme.md @@ -110,7 +110,8 @@ _[./src/app.tsx](./src/app.tsx)_ Date: Sun, 6 Jan 2019 00:47:58 +0900 Subject: [PATCH 171/180] fix: :hammer: fix wrong readme link --- readme.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/readme.md b/readme.md index eb8126f..5f9aa3e 100644 --- a/readme.md +++ b/readme.md @@ -30,79 +30,79 @@ then cd to one of the demos projects. ## samples -### [00 Boiler plate](react-by-sample/00_Boilerplate/readme.md) +### [00 Boiler plate](react-by-sample/00_Boilerplate) Bundling + npm start based on webpack. -### [01 Hello React](react-by-sample/01_HelloReact/readme.md) +### [01 Hello React](react-by-sample/01_HelloReact) Hello world, simples react render sample. -### [02 Properties](react-by-sample/02_Properties/readme.md) +### [02 Properties](react-by-sample/02_Properties) Introduce a basic React concept, handling properties. -### [03 State](react-by-sample/03_State/readme.md) +### [03 State](react-by-sample/03_State) Introduce a basic React concept, handling State. -### [04 Callback](react-by-sample/04_Callback/readme.md) +### [04 Callback](react-by-sample/04_Callback) Using callbacks. -### [05 Refactor](react-by-sample/05_Refactor/readme.md) +### [05 Refactor](react-by-sample/05_Refactor) Refactor the job done. -### [06 Move Back To Stateless](react-by-sample/06_MoveBackToStateless/readme.md) +### [06 Move Back To Stateless](react-by-sample/06_MoveBackToStateless) Remove state from a child control just to have clear governance of state. -### [07 Enable](react-by-sample/07_Enable/readme.md) +### [07 Enable](react-by-sample/07_Enable) Enable/disable components. -### [08 ColorPicker](react-by-sample/08_Colorpicker/readme.md) +### [08 ColorPicker](react-by-sample/08_Colorpicker) Simple color picker demo (show how properties work). -### [09 ColorPicker Refactor](react-by-sample/09_ColorpRefactor/readme.md) +### [09 ColorPicker Refactor](react-by-sample/09_ColorpRefactor) ColorPicker refactor. -### [10 Sidebar](react-by-sample/10_Sidebar/readme.md) +### [10 Sidebar](react-by-sample/10_Sidebar) Implementation of a single sidebar. -### [11 Table Mock](react-by-sample/11_TableMock/readme.md) +### [11 Table Mock](react-by-sample/11_TableMock) Render a table and use a child component to render each row. -### [12 Table Http](react-by-sample/12_TableHttp/readme.md) +### [12 Table Http](react-by-sample/12_TableHttp) Using Promises. -### [13 Should Update](react-by-sample/13_ShouldUpdate/readme.md) +### [13 Should Update](react-by-sample/13_ShouldUpdate) Enhance rendering performance hooking to 'shouldComponentUpdate'. -### [14 React Router](react-by-sample/14_ReactRouter/readme.md) +### [14 React Router](react-by-sample/14_ReactRouter) React Router navigation example. -### [15 Login Form](react-by-sample/15_LoginForm/readme.md) +### [15 Login Form](react-by-sample/15_LoginForm) Basic implementation of a login page. -### [16 Validation](react-by-sample/16_Validation/readme.md) +### [16 Validation](react-by-sample/16_Validation) React Form validation, using lc-form-validation library -### [17 Context](react-by-sample/17_Context/readme.md) +### [17 Context](react-by-sample/17_Context) How to use React 16 context api. -### [18 HOC](react-by-sample/17_Context/readme.md) +### [18 HOC](react-by-sample/17_Context) Hig Order component sample.. From 025af4f1f9fd4c302b26cc7ef1fa90dbb1249173 Mon Sep 17 00:00:00 2001 From: seungkyu2 Date: Sun, 6 Jan 2019 00:55:14 +0900 Subject: [PATCH 172/180] feat: :book: add readme examples --- readme.md | 54 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/readme.md b/readme.md index 5f9aa3e..33203c9 100644 --- a/readme.md +++ b/readme.md @@ -30,81 +30,95 @@ then cd to one of the demos projects. ## samples -### [00 Boiler plate](react-by-sample/00_Boilerplate) +### [00 Boiler plate](00_Boilerplate/readme.md) Bundling + npm start based on webpack. -### [01 Hello React](react-by-sample/01_HelloReact) +### [01 Hello React](01_HelloReact/readme.md) Hello world, simples react render sample. -### [02 Properties](react-by-sample/02_Properties) +### [02 Properties](02_Properties/readme.md) Introduce a basic React concept, handling properties. -### [03 State](react-by-sample/03_State) +### [03 State](03_State/readme.md) Introduce a basic React concept, handling State. -### [04 Callback](react-by-sample/04_Callback) +### [04 Callback](04_Callback/readme.md) Using callbacks. -### [05 Refactor](react-by-sample/05_Refactor) +### [05 Refactor](05_Refactor/readme.md) Refactor the job done. -### [06 Move Back To Stateless](react-by-sample/06_MoveBackToStateless) +### [06 Move Back To Stateless](06_MoveBackToStateless/readme.md) Remove state from a child control just to have clear governance of state. -### [07 Enable](react-by-sample/07_Enable) +### [07 Enable](07_Enable/readme.md) Enable/disable components. -### [08 ColorPicker](react-by-sample/08_Colorpicker) +### [08 ColorPicker](08_Colorpicker/readme.md) Simple color picker demo (show how properties work). -### [09 ColorPicker Refactor](react-by-sample/09_ColorpRefactor) +### [09 ColorPicker Refactor](09_ColorpRefactor/readme.md) ColorPicker refactor. -### [10 Sidebar](react-by-sample/10_Sidebar) +### [10 Sidebar](10_Sidebar/readme.md) Implementation of a single sidebar. -### [11 Table Mock](react-by-sample/11_TableMock) +### [11 Table Mock](11_TableMock/readme.md) Render a table and use a child component to render each row. -### [12 Table Http](react-by-sample/12_TableHttp) +### [12 Table Http](12_TableHttp/readme.md) Using Promises. -### [13 Should Update](react-by-sample/13_ShouldUpdate) +### [13 Should Update](13_ShouldUpdate/readme.md) Enhance rendering performance hooking to 'shouldComponentUpdate'. -### [14 React Router](react-by-sample/14_ReactRouter) +### [14 React Router](14_ReactRouter/readme.md) React Router navigation example. -### [15 Login Form](react-by-sample/15_LoginForm) +### [15 Login Form](15_LoginForm/readme.md) Basic implementation of a login page. -### [16 Validation](react-by-sample/16_Validation) +### [16 Validation](16_Validation/readme.md) React Form validation, using lc-form-validation library -### [17 Context](react-by-sample/17_Context) +### [17 Context](17_Context/readme.md) How to use React 16 context api. -### [18 HOC](react-by-sample/17_Context) +### [18 HOC](18_Hoc/readme.md) + +High Order component sample.. + +### [19 RenderProps](19_RenderProps/readme.md) +Learn how use render props in React + +### [20 Error Boundaries](20_ErrorBoundaries/readme.md) +Play with the Error Boundary concept + +### [21 Hooks](21_Hooks/readme.md) +Play with the Error Boundary concept +make use of hooks a cool concept introduced in React 16.7.0 + +### [22 Hooks_UseContext](22_Hooks_UseContext/readme.md) +Let's see how this is solved using Hooks + Use Context. -Hig Order component sample.. # About Lemoncode From 658a2d3e9796c62d87d29ef384e795b1a04472b4 Mon Sep 17 00:00:00 2001 From: seungkyu2 Date: Sun, 6 Jan 2019 00:57:05 +0900 Subject: [PATCH 173/180] fix: :hammer: fix wrong readme link --- readme.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/readme.md b/readme.md index 33203c9..bd9cac7 100644 --- a/readme.md +++ b/readme.md @@ -30,93 +30,93 @@ then cd to one of the demos projects. ## samples -### [00 Boiler plate](00_Boilerplate/readme.md) +### [00 Boiler plate](./00_Boilerplate/readme.md) Bundling + npm start based on webpack. -### [01 Hello React](01_HelloReact/readme.md) +### [01 Hello React](./01_HelloReact/readme.md) Hello world, simples react render sample. -### [02 Properties](02_Properties/readme.md) +### [02 Properties](./02_Properties/readme.md) Introduce a basic React concept, handling properties. -### [03 State](03_State/readme.md) +### [03 State](./03_State/readme.md) Introduce a basic React concept, handling State. -### [04 Callback](04_Callback/readme.md) +### [04 Callback](./04_Callback/readme.md) Using callbacks. -### [05 Refactor](05_Refactor/readme.md) +### [05 Refactor](./05_Refactor/readme.md) Refactor the job done. -### [06 Move Back To Stateless](06_MoveBackToStateless/readme.md) +### [06 Move Back To Stateless](./06_MoveBackToStateless/readme.md) Remove state from a child control just to have clear governance of state. -### [07 Enable](07_Enable/readme.md) +### [07 Enable](./07_Enable/readme.md) Enable/disable components. -### [08 ColorPicker](08_Colorpicker/readme.md) +### [08 ColorPicker](./08_Colorpicker/readme.md) Simple color picker demo (show how properties work). -### [09 ColorPicker Refactor](09_ColorpRefactor/readme.md) +### [09 ColorPicker Refactor](./09_ColorpRefactor/readme.md) ColorPicker refactor. -### [10 Sidebar](10_Sidebar/readme.md) +### [10 Sidebar](./10_Sidebar/readme.md) Implementation of a single sidebar. -### [11 Table Mock](11_TableMock/readme.md) +### [11 Table Mock](./11_TableMock/readme.md) Render a table and use a child component to render each row. -### [12 Table Http](12_TableHttp/readme.md) +### [12 Table Http](./12_TableHttp/readme.md) Using Promises. -### [13 Should Update](13_ShouldUpdate/readme.md) +### [13 Should Update](./13_ShouldUpdate/readme.md) Enhance rendering performance hooking to 'shouldComponentUpdate'. -### [14 React Router](14_ReactRouter/readme.md) +### [14 React Router](./14_ReactRouter/readme.md) React Router navigation example. -### [15 Login Form](15_LoginForm/readme.md) +### [15 Login Form](./15_LoginForm/readme.md) Basic implementation of a login page. -### [16 Validation](16_Validation/readme.md) +### [16 Validation](./16_Validation/readme.md) React Form validation, using lc-form-validation library -### [17 Context](17_Context/readme.md) +### [17 Context](./17_Context/readme.md) How to use React 16 context api. -### [18 HOC](18_Hoc/readme.md) +### [18 HOC](./18_Hoc/readme.md) High Order component sample.. -### [19 RenderProps](19_RenderProps/readme.md) +### [19 RenderProps](./19_RenderProps/readme.md) Learn how use render props in React -### [20 Error Boundaries](20_ErrorBoundaries/readme.md) +### [20 Error Boundaries](./20_ErrorBoundaries/readme.md) Play with the Error Boundary concept -### [21 Hooks](21_Hooks/readme.md) +### [21 Hooks](./21_Hooks/readme.md) Play with the Error Boundary concept make use of hooks a cool concept introduced in React 16.7.0 -### [22 Hooks_UseContext](22_Hooks_UseContext/readme.md) +### [22 Hooks_UseContext](./22_Hooks_UseContext/readme.md) Let's see how this is solved using Hooks + Use Context. From d501feee38f4802d1aed368e22bd0c3d911d2159 Mon Sep 17 00:00:00 2001 From: seungkyu2 Date: Sun, 6 Jan 2019 01:03:23 +0900 Subject: [PATCH 174/180] fix: :hammer: fix wrong readme link --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bd9cac7..e6b2d90 100644 --- a/readme.md +++ b/readme.md @@ -112,7 +112,7 @@ Learn how use render props in React ### [20 Error Boundaries](./20_ErrorBoundaries/readme.md) Play with the Error Boundary concept -### [21 Hooks](./21_Hooks/readme.md) +### [21 Hooks](./21_Hooks/Readme.md) Play with the Error Boundary concept make use of hooks a cool concept introduced in React 16.7.0 From 93ae87aef4681e9cf91a188f817f14f504e407df Mon Sep 17 00:00:00 2001 From: seungkyu2 Date: Sun, 6 Jan 2019 01:04:45 +0900 Subject: [PATCH 175/180] fix: :hammer: fix wrong readme link of 21 and 22 --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index e6b2d90..4833cf2 100644 --- a/readme.md +++ b/readme.md @@ -112,11 +112,11 @@ Learn how use render props in React ### [20 Error Boundaries](./20_ErrorBoundaries/readme.md) Play with the Error Boundary concept -### [21 Hooks](./21_Hooks/Readme.md) +### [21 Hooks](./21_Hooks/readme.md) Play with the Error Boundary concept make use of hooks a cool concept introduced in React 16.7.0 -### [22 Hooks_UseContext](./22_Hooks_UseContext/readme.md) +### [22 Hooks_UseContext](./22_Hooks_UseContext/Readme.md) Let's see how this is solved using Hooks + Use Context. From aae2d92765ea35cbbc5f3c229aacef55bbba36de Mon Sep 17 00:00:00 2001 From: seungkyu2 Date: Sun, 6 Jan 2019 01:06:20 +0900 Subject: [PATCH 176/180] fix: :hammer: fix wrong readme link --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4833cf2..bd9cac7 100644 --- a/readme.md +++ b/readme.md @@ -116,7 +116,7 @@ Play with the Error Boundary concept Play with the Error Boundary concept make use of hooks a cool concept introduced in React 16.7.0 -### [22 Hooks_UseContext](./22_Hooks_UseContext/Readme.md) +### [22 Hooks_UseContext](./22_Hooks_UseContext/readme.md) Let's see how this is solved using Hooks + Use Context. From 51fdf1cf326a8e829dc11b16da1b4a35fca01587 Mon Sep 17 00:00:00 2001 From: seungkyu2 Date: Sun, 6 Jan 2019 01:07:40 +0900 Subject: [PATCH 177/180] fix: :hammer: fix wrong readme link --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index bd9cac7..b8e61f0 100644 --- a/readme.md +++ b/readme.md @@ -98,15 +98,15 @@ Basic implementation of a login page. React Form validation, using lc-form-validation library -### [17 Context](./17_Context/readme.md) +### [17 Context](./17_Context/Readme.md) How to use React 16 context api. -### [18 HOC](./18_Hoc/readme.md) +### [18 HOC](./18_Hoc/Readme.md) High Order component sample.. -### [19 RenderProps](./19_RenderProps/readme.md) +### [19 RenderProps](./19_RenderProps/Readme.md) Learn how use render props in React ### [20 Error Boundaries](./20_ErrorBoundaries/readme.md) @@ -116,7 +116,7 @@ Play with the Error Boundary concept Play with the Error Boundary concept make use of hooks a cool concept introduced in React 16.7.0 -### [22 Hooks_UseContext](./22_Hooks_UseContext/readme.md) +### [22 Hooks_UseContext](./22_Hooks_UseContext/Readme.md) Let's see how this is solved using Hooks + Use Context. From fb89e5491034e0372734380f4c3890e44e004740 Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Thu, 17 Jan 2019 12:24:55 +0100 Subject: [PATCH 178/180] Update Readme.md --- 19_RenderProps/Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/19_RenderProps/Readme.md b/19_RenderProps/Readme.md index c65342f..24e4b93 100644 --- a/19_RenderProps/Readme.md +++ b/19_RenderProps/Readme.md @@ -130,7 +130,7 @@ interface Props { login : string; } -const LoginComponent = (props: Props) => +const PageBComponent = (props: Props) => <>

    Hello from page B


    @@ -146,7 +146,7 @@ export const PageB = () => ( - + )} > From cc8e713e8b0afe281fb9efdbb9ddf817be2ec51e Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 11 Feb 2019 10:02:51 +0100 Subject: [PATCH 179/180] Update readme.md --- readme.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index b8e61f0..ed4ef8c 100644 --- a/readme.md +++ b/readme.md @@ -121,10 +121,13 @@ Let's see how this is solved using Hooks + Use Context. -# About Lemoncode +# About Basefactor + Lemoncode We are a team of long-term experienced freelance developers, established as a group in 2010. -We specialize in Front End technologies and .NET. [Click here](http://lemoncode.net/services/en/#en-home) to get more info about us. +We are specialized in Front End technologies. -For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend +[Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. + +[Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. +For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend From d18533c163738940e8dd4e062f63e5bd4800815e Mon Sep 17 00:00:00 2001 From: Braulio Diez Date: Mon, 11 Feb 2019 10:05:25 +0100 Subject: [PATCH 180/180] Update readme.md --- readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readme.md b/readme.md index ed4ef8c..fdb8c53 100644 --- a/readme.md +++ b/readme.md @@ -123,8 +123,7 @@ Let's see how this is solved using Hooks + Use Context. # About Basefactor + Lemoncode -We are a team of long-term experienced freelance developers, established as a group in 2010. -We are specialized in Front End technologies. +We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.