├── catch-of-the-day ├── .eslintrc.json ├── .gitignore ├── build │ └── favicon.ico ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── security-rules.json └── src │ ├── base.js │ ├── components │ ├── .gitkeep │ ├── AddFishForm.js │ ├── App.js │ ├── Fish.js │ ├── Header.js │ ├── Inventory.js │ ├── NotFound.js │ ├── Order.js │ └── StorePicker.js │ ├── css │ ├── _animations-finished.styl │ ├── _animations.styl │ ├── _colours.styl │ ├── _fonts.styl │ ├── _normalize.styl │ ├── _typography.styl │ ├── fonts │ │ ├── blanch_caps_inline-webfont.eot │ │ ├── blanch_caps_inline-webfont.svg │ │ ├── blanch_caps_inline-webfont.ttf │ │ ├── blanch_caps_inline-webfont.woff │ │ ├── haymaker-webfont.eot │ │ ├── haymaker-webfont.svg │ │ ├── haymaker-webfont.ttf │ │ └── haymaker-webfont.woff │ ├── images │ │ └── anchor.svg │ ├── style.css │ └── style.styl │ ├── helpers.js │ ├── index.js │ └── sample-fishes.js └── readme.md /catch-of-the-day/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaFeatures": { 11 | "experimentalObjectRestSpread": true, 12 | "jsx": true 13 | }, 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "react" 18 | ], 19 | "rules": { 20 | "indent": [ 21 | "error", 22 | "tab" 23 | ], 24 | "linebreak-style": [ 25 | 0 26 | ], 27 | "quotes": [ 28 | 0 29 | ], 30 | "semi": [ 31 | 0 32 | ], 33 | "no-unused-vars": [0], 34 | "no-console": [0] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /catch-of-the-day/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.zip 3 | .DS_Store 4 | npm-debug.log 5 | stepped-solutions/ 6 | notes.txt 7 | -------------------------------------------------------------------------------- /catch-of-the-day/build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/build/favicon.ico -------------------------------------------------------------------------------- /catch-of-the-day/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cotd", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "autoprefixer-stylus": "0.10.0", 7 | "concurrently": "3.0.0", 8 | "react-scripts": "0.6.0", 9 | "stylus": "^0.54.5" 10 | }, 11 | "dependencies": { 12 | "history": "4.2.0", 13 | "re-base": "2.2.0", 14 | "react": "15.3.2", 15 | "react-addons-css-transition-group": "15.3.2", 16 | "react-dom": "15.3.2", 17 | "react-router": "4.0.0-alpha.3" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "watch": "concurrently --names 'webpack, stylus' --prefix name 'npm run start' 'npm run styles:watch'", 22 | "build": "react-scripts build", 23 | "eject": "react-scripts eject", 24 | "styles": "stylus -u autoprefixer-stylus ./src/css/style.styl -o ./src/css/style.css", 25 | "styles:watch": "stylus -u autoprefixer-stylus -w ./src/css/style.styl -o ./src/css/style.css", 26 | "deploy": "ns ./build --cmd 'list ./content -s'" 27 | }, 28 | "eslintConfig": { 29 | "extends": "./node_modules/react-scripts/config/eslint.js" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /catch-of-the-day/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/public/favicon.ico -------------------------------------------------------------------------------- /catch-of-the-day/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Catch of the Day 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /catch-of-the-day/security-rules.json: -------------------------------------------------------------------------------- 1 | // These are your firebase security rules - put them in the "Security & Rules" tab of your database 2 | { 3 | "rules": { 4 | // won't let people delete an existing room 5 | ".write": "!data.exists()", 6 | ".read": true, 7 | "$room" : { 8 | // only the store owner can edit the data 9 | ".write" : "newData.child('owner').val() === auth.uid", 10 | ".read" : true 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /catch-of-the-day/src/base.js: -------------------------------------------------------------------------------- 1 | import Rebase from 're-base'; 2 | 3 | const base = Rebase.createClass({ 4 | apiKey: "AIzaSyAmBH7Ih3ZsyPOhk8PFekHIShaXRK4RfCM", 5 | authDomain: "catch-of-the-day-maria-dc.firebaseapp.com", 6 | databaseURL: "https://catch-of-the-day-maria-dc.firebaseio.com", 7 | }); 8 | 9 | export default base; 10 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/.gitkeep: -------------------------------------------------------------------------------- 1 | // this is just an empty file so the empty folder will stay in git! 2 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/AddFishForm.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class AddFishForm extends React.Component { 4 | createFish(event) { 5 | event.preventDefault(); 6 | console.log('Gonna make some fish!'); 7 | const fish = { 8 | name: this.name.value, 9 | price: this.price.value, 10 | status: this.status.value, 11 | desc: this.desc.value, 12 | image: this.image.value, 13 | } 14 | this.props.addFish(fish); 15 | this.fishForm.reset(); 16 | } 17 | render() { 18 | return ( 19 |
this.fishForm = input} className="fish-edit" onSubmit={(e) => this.createFish(e)}> 20 | this.name = input} type="text" placeholder="Fish Name" /> 21 | this.price = input} type="text" placeholder="Fish Price" /> 22 | 26 | 27 | this.image = input} type="text" placeholder="Fish Image" /> 28 | 29 |
30 | ) 31 | } 32 | } 33 | 34 | AddFishForm.propTypes = { 35 | addFish: React.PropTypes.func.isRequired 36 | } 37 | 38 | export default AddFishForm; 39 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Header from './Header'; 3 | import Order from './Order'; 4 | import Inventory from './Inventory'; 5 | import Fish from './Fish'; 6 | import sampleFishes from '../sample-fishes'; 7 | import base from '../base'; 8 | 9 | class App extends React.Component { 10 | constructor() { 11 | super(); 12 | this.addFish = this.addFish.bind(this); 13 | this.removeFromOrder.bind(this); 14 | this.removeFish = this.removeFish.bind(this); 15 | this.updateFish = this.updateFish.bind(this); 16 | this.loadSamples = this.loadSamples.bind(this); 17 | this.addToOrder = this.addToOrder.bind(this); 18 | this.removeFromOrder = this.removeFromOrder.bind(this); 19 | // initial state (getInitialState if you are using reactCreateClass as we are here) 20 | this.state = { 21 | fishes: {}, 22 | order: {} 23 | }; 24 | 25 | } 26 | 27 | componentWillMount() { 28 | // this runs right before is rendered 29 | this.ref = base.syncState(`${this.props.params.storeId}/fishes`, { 30 | context: this, 31 | state: 'fishes' 32 | }); 33 | // check if there is any order in local storage 34 | const localStorageRef = localStorage.getItem(`order-${this.props.params.storeId}`); 35 | if(localStorageRef) { 36 | // update our App Component's order state 37 | this.setState({ 38 | // changes string back to object 39 | order: JSON.parse(localStorageRef) 40 | }); 41 | } 42 | } 43 | 44 | componentWillUnMount() { 45 | base.removeBinding(this.ref); 46 | } 47 | 48 | componentWillUpdate(nextProps, nextState) { 49 | localStorage.setItem(`order-${this.props.params.storeId}`, JSON.stringify(nextState.order)); 50 | } 51 | 52 | addFish(fish) { 53 | // update our state 54 | const fishes = {...this.state.fishes}; 55 | // set state. Date.now() gives timestamp. New to JS. 56 | const timestamp = Date.now(); 57 | fishes[`fish-${timestamp}`] = fish; 58 | // set state passing only the object that has changed here. 59 | this.setState({ fishes }); 60 | } 61 | 62 | updateFish(key, updatedFish) { 63 | const fishes = {...this.state.fishes}; 64 | fishes[key] = updatedFish; 65 | this.setState({ fishes }); 66 | } 67 | 68 | removeFish(key) { 69 | const fishes = {...this.state.fishes}; 70 | fishes[key] = null; 71 | this.setState({ fishes }); 72 | } 73 | 74 | loadSamples() { 75 | this.setState({ 76 | fishes: sampleFishes 77 | }); 78 | } 79 | 80 | addToOrder(key) { 81 | // take a copy of our state 82 | const order = {...this.state.order}; 83 | // update or add new number of fish ordered 84 | order[key] = order[key] + 1 || 1; 85 | // update our state (or ({ order: order })) 86 | this.setState({ order }); 87 | 88 | } 89 | 90 | removeFromOrder(key) { 91 | const order = {...this.state.order}; 92 | delete order[key]; 93 | this.setState({ order }); 94 | } 95 | 96 | render() { 97 | return ( 98 |
99 |
100 |
101 |
    102 | { 103 | Object 104 | .keys(this.state.fishes) 105 | .map(key => ) 106 | } 107 |
108 |
109 | 110 | 112 |
113 | ) 114 | } 115 | } 116 | 117 | App.propTypes = { 118 | params: React.PropTypes.object.isRequired 119 | } 120 | 121 | export default App; 122 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/Fish.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { formatPrice } from '../helpers'; 3 | 4 | class Fish extends React.Component { 5 | render() { 6 | const { details, index } = this.props; 7 | const isAvailable = details.status === 'available'; 8 | const buttonText = isAvailable ? 'Add To Order' : 'Sold Out'; 9 | return ( 10 |
  • 11 | {details.name} 12 |

    13 | {details.name} 14 | {formatPrice(details.price)} 15 |

    16 |

    {details.desc}

    17 | 18 |
  • 19 | ) 20 | } 21 | } 22 | 23 | Fish.propTypes = { 24 | details: React.PropTypes.object.isRequired, 25 | index: React.PropTypes.string.isRequired, 26 | addToOrder: React.PropTypes.func.isRequired 27 | } 28 | 29 | export default Fish; 30 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Header = (props) => { 4 | return ( 5 |
    6 |

    7 | Catch 8 | 9 | of 10 | the 11 | 12 | Day 13 |

    14 |

    15 | {props.tagline} 16 |

    17 |
    18 | ) 19 | } 20 | 21 | Header.propTypes = { 22 | tagline: React.PropTypes.string.isRequired 23 | } 24 | 25 | export default Header; 26 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/Inventory.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddFishForm from './AddFishForm'; 3 | import base from '../base'; 4 | 5 | class Inventory extends React.Component { 6 | constructor() { 7 | super(); 8 | this.renderInventory = this.renderInventory.bind(this); 9 | this.renderLogin = this.renderLogin.bind(this); 10 | this.renderLogin = this.renderLogin.bind(this); 11 | this.authenticate = this.authenticate.bind(this); 12 | this.logout = this.logout.bind(this); 13 | this.authHandler = this.authHandler.bind(this); 14 | this.handleChange = this.handleChange.bind(this); 15 | this.state = { 16 | uid: null, 17 | owner: null 18 | } 19 | } 20 | 21 | componentDidMount() { 22 | base.onAuth((user) => { 23 | if(user) { 24 | this.authHandler(null, { user }); 25 | } 26 | }); 27 | } 28 | 29 | handleChange(e, key) { 30 | const fish = this.props.fishes[key]; 31 | // take copy of fish and update it with new data 32 | const updatedFish = { 33 | ...fish, 34 | [e.target.name]: e.target.value 35 | } 36 | this.props.updateFish(key, updatedFish); 37 | } 38 | 39 | authenticate(provider) { 40 | console.log(`Trying to log in with ${provider}`); 41 | base.authWithOAuthPopup(provider, this.authHandler); 42 | } 43 | 44 | logout() { 45 | base.unauth(); 46 | this.setState({ 47 | uid: null 48 | }); 49 | } 50 | 51 | authHandler(err, authData) { 52 | console.log(authData); 53 | if(err) { 54 | console.error(err); 55 | return; 56 | } 57 | 58 | // grab store info if the current user is owner 59 | const storeRef = base.database().ref(this.props.storeId); 60 | // query firebase data once for store data 61 | storeRef.once('value', (snapshot) => { 62 | const data = snapshot.val() || {}; 63 | 64 | // claim as our own if no owner already 65 | if(!data.owner) { 66 | storeRef.set({ 67 | owner: authData.user.uid 68 | }); 69 | } 70 | 71 | this.setState({ 72 | uid: authData.user.uid, 73 | owner: data.owner || authData.user.uid 74 | }); 75 | }); 76 | 77 | } 78 | 79 | renderLogin() { 80 | return ( 81 | 87 | ) 88 | } 89 | 90 | renderInventory(key) { 91 | const fish = this.props.fishes[key]; 92 | return ( 93 |
    94 | this.handleChange(e, key)} /> 95 | this.handleChange(e, key)} /> 96 | 100 | 101 | this.handleChange(e, key)} /> 102 | 103 |
    104 | ) 105 | } 106 | render() { 107 | const logout = ; 108 | // check if anyone is logged in (really not logged in at all) 109 | if(!this.state.uid) { 110 | return
    {this.renderLogin()}
    111 | } 112 | // check if current user is owner of current store 113 | if(this.state.uid !== this.state.owner) { 114 | return ( 115 |
    116 |

    Sorry, you aren't the owner of this store!

    117 | {logout} 118 |
    119 | ) 120 | } 121 | return ( 122 |
    123 |

    Inventory

    124 | {logout} 125 | {Object.keys(this.props.fishes).map(this.renderInventory)} 126 | 127 | 128 |
    129 | 130 | ) 131 | } 132 | } 133 | 134 | Inventory.propTypes = { 135 | fishes: React.PropTypes.object.isRequired, 136 | updateFish: React.PropTypes.func.isRequired, 137 | removeFish: React.PropTypes.func.isRequired, 138 | addFish: React.PropTypes.func.isRequired, 139 | loadSamples: React.PropTypes.func.isRequired, 140 | storeId: React.PropTypes.string.isRequired 141 | } 142 | 143 | export default Inventory; 144 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class NotFound extends React.Component { 4 | render() { 5 | return ( 6 |

    Not Found!111!!

    7 | ) 8 | } 9 | } 10 | 11 | export default NotFound; 12 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/Order.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { formatPrice } from '../helpers'; 3 | import CSSTransitionGroup from 'react-addons-css-transition-group'; 4 | 5 | class Order extends React.Component { 6 | constructor() { 7 | super(); 8 | this.renderOrder = this.renderOrder.bind(this); 9 | } 10 | renderOrder(key) { 11 | const fish = this.props.fishes[key]; 12 | const count = this.props.order[key]; 13 | const removeButton = 14 | 15 | if(!fish || fish.status === 'unavailable') { 16 | return
  • Sorry, {fish ? fish.name : 'fish'} is no longer available!{removeButton}
  • 17 | } 18 | 19 | return ( 20 |
  • 21 | 22 | 23 | {count} 24 | 25 | lbs {fish.name} {removeButton} 26 | 27 | {formatPrice(count * fish.price)} 28 |
  • 29 | ) 30 | } 31 | render() { 32 | const orderIds = Object.keys(this.props.order); 33 | const total = orderIds.reduce((prevTotal, key) => { 34 | // because all fishes and not current/specific fish 35 | const fish = this.props.fishes[key]; 36 | // need count - how many bought 37 | const count = this.props.order[key]; 38 | // because things can change at any given moment. real time app 39 | const isAvailable = fish && fish.status === 'available'; 40 | // add it up 41 | if(isAvailable) { 42 | // || 0 because sometimes the fish will be in order but subsequently deleted 43 | return prevTotal + (count * fish.price || 0) 44 | } 45 | // otherwise last amount 46 | return prevTotal; 47 | // need to start with starting value 48 | }, 0); 49 | return ( 50 |
    51 |

    Your Order

    52 | 53 | {orderIds.map(this.renderOrder)} 54 |
  • 55 | Total: 56 | {formatPrice(total)} 57 |
  • 58 |
    59 |
    60 | ) 61 | } 62 | } 63 | 64 | Order.propTypes = { 65 | fishes: React.PropTypes.object.isRequired, 66 | order: React.PropTypes.object.isRequired, 67 | removeFromOrder: React.PropTypes.func.isRequired 68 | } 69 | 70 | export default Order; 71 | -------------------------------------------------------------------------------- /catch-of-the-day/src/components/StorePicker.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { getFunName } from '../helpers'; 3 | 4 | class StorePicker extends React.Component { 5 | // every component you build needs at least one method and that's the render method 6 | goToStore(event) { 7 | // prevents page refresh 8 | event.preventDefault(); 9 | console.log('You changed the URL'); 10 | // first grab text from box 11 | const storeId = this.storeInput.value; 12 | console.log(`Going to ${storeId}`) 13 | // second transition from / to /store/storeId 14 | this.context.router.transitionTo(`/store/${storeId}`); 15 | } 16 | render() { 17 | // Anywhere else 18 | return ( 19 |
    this.goToStore(e)}> 20 |

    Please Enter A Store

    21 | { this.storeInput = input}} /> 22 | 23 |
    24 | ) 25 | } 26 | } 27 | 28 | // surface router. tell React that StorePicker component expects something called a router, and router says ok, I will make this router available to you. 29 | StorePicker.contextTypes = { 30 | router: React.PropTypes.object 31 | } 32 | 33 | export default StorePicker; 34 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/_animations-finished.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/_animations-finished.styl -------------------------------------------------------------------------------- /catch-of-the-day/src/css/_animations.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Possible Animations: 3 | enter 4 | leave 5 | appear Must set transitionAppear={true} on animation component 6 | */ 7 | 8 | .order-enter 9 | max-height 0 10 | transition all 0.5s 11 | transform: translateX(-120%) 12 | 13 | &.order-enter-active 14 | max-height 60px 15 | padding 2rem 0 !important 16 | transform: translateX(0) 17 | 18 | .order-leave 19 | transition all 0.5s 20 | transform translateX(0) 21 | &.order-leave-active 22 | transform translateX(120%) 23 | max-height: 0 24 | padding: 0 25 | 26 | .count-enter 27 | transition all .5s 28 | // start offscreen 29 | transform translateY(100%) 30 | &.count-enter-active 31 | // back to normal 32 | transform translateY(0) 33 | 34 | .count-leave 35 | transition all .5s 36 | // because need one number to overlap other 37 | position absolute 38 | left 0 39 | bottom 0 40 | // starting from where it is 41 | transform translateY(0) 42 | &.count-leave-active 43 | transform translateY(-100%) 44 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/_colours.styl: -------------------------------------------------------------------------------- 1 | orange = #F5A623 2 | red = #d12028 3 | green = #2DC22D 4 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/_fonts.styl: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "haymakerregular"; 3 | src: url("./fonts/haymaker-webfont.eot"); 4 | src: url("./fonts/haymaker-webfont.eot?#iefix") format("embedded-opentype"), url("./fonts/haymaker-webfont.woff") format("woff"), url("./fonts/haymaker-webfont.ttf") format("truetype"), url("./fonts/haymaker-webfont.svg#haymakerregular") format("svg"); 5 | font-weight: normal; 6 | font-style: normal; 7 | } 8 | 9 | @font-face { 10 | font-family: 'blanchcaps_inline'; 11 | src: url('./fonts/blanch_caps_inline-webfont.eot'); 12 | src: url('./fonts/blanch_caps_inline-webfont.eot?#iefix') format('embedded-opentype'), 13 | url('./fonts/blanch_caps_inline-webfont.woff') format('woff'), 14 | url('./fonts/blanch_caps_inline-webfont.ttf') format('truetype'), 15 | url('./fonts/blanch_caps_inline-webfont.svg#blanchcaps_inline') format('svg'); 16 | font-weight: normal; 17 | font-style: normal; 18 | } 19 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/_normalize.styl: -------------------------------------------------------------------------------- 1 | // CSS Normalize 2 | 3 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button], input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;} 4 | 5 | // clearfix 6 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; } 7 | 8 | // Sane border box 9 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } 10 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/_typography.styl: -------------------------------------------------------------------------------- 1 | html 2 | font-size 62.5% 3 | 4 | body 5 | background #D4D4D4 6 | -webkit-font-smoothing antialiased 7 | -moz-osx-font-smoothing grayscale 8 | font-family 'Open Sans', sans-serif 9 | font-size 2rem 10 | 11 | h1 12 | font-family 'blanchcaps_inline', sans-serif; 13 | text-align center 14 | font-weight normal 15 | margin 0 16 | 17 | h2,h3,h4,h5,h6 18 | font-weight normal 19 | font-family 'haymakerregular', sans-serif 20 | 21 | h2 22 | text-align center 23 | margin-top 0 24 | margin-bottom 2rem 25 | 26 | h3 27 | font-size 3rem 28 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.eot -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.ttf -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/fonts/blanch_caps_inline-webfont.woff -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/haymaker-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/fonts/haymaker-webfont.eot -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/haymaker-webfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/haymaker-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/fonts/haymaker-webfont.ttf -------------------------------------------------------------------------------- /catch-of-the-day/src/css/fonts/haymaker-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaearon/react-for-beginners/d1fad715855fb2a02e02b93e7da8ccac01a41484/catch-of-the-day/src/css/fonts/haymaker-webfont.woff -------------------------------------------------------------------------------- /catch-of-the-day/src/css/images/anchor.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/style.css: -------------------------------------------------------------------------------- 1 | .order-enter { 2 | -webkit-transition: all 0.5s; 3 | transition: all 0.5s; 4 | } 5 | transform: translateX(-120%) { 6 | max-height: 0; 7 | } 8 | transform: translateX(-120%).order-enter-active { 9 | max-height: 60px; 10 | padding: 2rem 0 !important; 11 | -webkit-transform: translateX(0); 12 | transform: translateX(0); 13 | } 14 | .order-leave { 15 | -webkit-transition: all 0.5s; 16 | transition: all 0.5s; 17 | -webkit-transform: translateX(0); 18 | transform: translateX(0); 19 | } 20 | .order-leave.order-leave-active { 21 | -webkit-transform: translateX(120%); 22 | transform: translateX(120%); 23 | max-height: 0; 24 | padding: 0; 25 | } 26 | .count-enter { 27 | -webkit-transition: all 0.5s; 28 | transition: all 0.5s; 29 | -webkit-transform: translateY(100%); 30 | transform: translateY(100%); 31 | } 32 | .count-enter.count-enter-active { 33 | -webkit-transform: translateY(0); 34 | transform: translateY(0); 35 | } 36 | .count-leave { 37 | -webkit-transition: all 0.5s; 38 | transition: all 0.5s; 39 | position: absolute; 40 | left: 0; 41 | bottom: 0; 42 | -webkit-transform: translateY(0); 43 | transform: translateY(0); 44 | } 45 | .count-leave.count-leave-active { 46 | -webkit-transform: translateY(-100%); 47 | transform: translateY(-100%); 48 | } 49 | @font-face { 50 | font-family: "haymakerregular"; 51 | src: url("./fonts/haymaker-webfont.eot"); 52 | src: url("./fonts/haymaker-webfont.eot?#iefix") format("embedded-opentype"), url("./fonts/haymaker-webfont.woff") format("woff"), url("./fonts/haymaker-webfont.ttf") format("truetype"), url("./fonts/haymaker-webfont.svg#haymakerregular") format("svg"); 53 | font-weight: normal; 54 | font-style: normal; 55 | } 56 | @font-face { 57 | font-family: 'blanchcaps_inline'; 58 | src: url("./fonts/blanch_caps_inline-webfont.eot"); 59 | src: url("./fonts/blanch_caps_inline-webfont.eot?#iefix") format('embedded-opentype'), url("./fonts/blanch_caps_inline-webfont.woff") format('woff'), url("./fonts/blanch_caps_inline-webfont.ttf") format('truetype'), url("./fonts/blanch_caps_inline-webfont.svg#blanchcaps_inline") format('svg'); 60 | font-weight: normal; 61 | font-style: normal; 62 | } 63 | article, 64 | aside, 65 | details, 66 | figcaption, 67 | figure, 68 | footer, 69 | header, 70 | hgroup, 71 | nav, 72 | section, 73 | summary { 74 | display: block; 75 | } 76 | audio, 77 | canvas, 78 | video { 79 | display: inline-block; 80 | } 81 | audio:not([controls]) { 82 | display: none; 83 | height: 0; 84 | } 85 | [hidden] { 86 | display: none; 87 | } 88 | html { 89 | font-family: sans-serif; 90 | -webkit-text-size-adjust: 100%; 91 | -ms-text-size-adjust: 100%; 92 | } 93 | a:focus { 94 | outline: thin dotted; 95 | } 96 | a:active, 97 | a:hover { 98 | outline: 0; 99 | } 100 | h1 { 101 | font-size: 2em; 102 | } 103 | abbr[title] { 104 | border-bottom: 1px dotted; 105 | } 106 | b, 107 | strong { 108 | font-weight: 700; 109 | } 110 | dfn { 111 | font-style: italic; 112 | } 113 | mark { 114 | background: #ff0; 115 | color: #000; 116 | } 117 | code, 118 | kbd, 119 | pre, 120 | samp { 121 | font-family: monospace, serif; 122 | font-size: 1em; 123 | } 124 | pre { 125 | white-space: pre-wrap; 126 | word-wrap: break-word; 127 | } 128 | q { 129 | quotes: 2 1C 2 1D 2 18 2 19; 130 | } 131 | small { 132 | font-size: 80%; 133 | } 134 | sub, 135 | sup { 136 | font-size: 75%; 137 | line-height: 0; 138 | position: relative; 139 | vertical-align: baseline; 140 | } 141 | sup { 142 | top: -0.5em; 143 | } 144 | sub { 145 | bottom: -0.25em; 146 | } 147 | img { 148 | border: 0; 149 | } 150 | svg:not(:root) { 151 | overflow: hidden; 152 | } 153 | fieldset { 154 | border: 1px solid #c0c0c0; 155 | margin: 0 2px; 156 | padding: 0.35em 0.625em 0.75em; 157 | } 158 | button, 159 | input, 160 | select, 161 | textarea { 162 | font-family: inherit; 163 | font-size: 100%; 164 | margin: 0; 165 | } 166 | button, 167 | input { 168 | line-height: normal; 169 | } 170 | button, 171 | html input[type=button], 172 | input[type=reset], 173 | input[type=submit] { 174 | -webkit-appearance: button; 175 | cursor: pointer; 176 | } 177 | button[disabled], 178 | input[disabled] { 179 | cursor: default; 180 | } 181 | input[type=checkbox], 182 | input[type=radio] { 183 | box-sizing: border-box; 184 | padding: 0; 185 | } 186 | input[type=search] { 187 | -webkit-appearance: textfield; 188 | box-sizing: content-box; 189 | } 190 | input[type=search]::-webkit-search-cancel-button, 191 | input[type=search]::-webkit-search-decoration { 192 | -webkit-appearance: none; 193 | } 194 | textarea { 195 | overflow: auto; 196 | vertical-align: top; 197 | } 198 | table { 199 | border-collapse: collapse; 200 | border-spacing: 0; 201 | } 202 | body, 203 | figure { 204 | margin: 0; 205 | } 206 | legend, 207 | button::-moz-focus-inner, 208 | input::-moz-focus-inner { 209 | border: 0; 210 | padding: 0; 211 | } 212 | .clearfix:after { 213 | visibility: hidden; 214 | display: block; 215 | font-size: 0; 216 | content: " "; 217 | clear: both; 218 | height: 0; 219 | } 220 | * { 221 | box-sizing: border-box; 222 | } 223 | html { 224 | font-size: 62.5%; 225 | } 226 | body { 227 | background: #d4d4d4; 228 | -webkit-font-smoothing: antialiased; 229 | -moz-osx-font-smoothing: grayscale; 230 | font-family: 'Open Sans', sans-serif; 231 | font-size: 2rem; 232 | } 233 | h1 { 234 | font-family: 'blanchcaps_inline', sans-serif; 235 | text-align: center; 236 | font-weight: normal; 237 | margin: 0; 238 | } 239 | h2, 240 | h3, 241 | h4, 242 | h5, 243 | h6 { 244 | font-weight: normal; 245 | font-family: 'haymakerregular', sans-serif; 246 | } 247 | h2 { 248 | text-align: center; 249 | margin-top: 0; 250 | margin-bottom: 2rem; 251 | } 252 | h3 { 253 | font-size: 3rem; 254 | } 255 | header.top { 256 | text-align: center; 257 | } 258 | header.top h1 { 259 | font-size: 14.4rem; 260 | line-height: 0.7; 261 | display: -webkit-box; 262 | display: -ms-flexbox; 263 | display: flex; 264 | -webkit-box-pack: center; 265 | -ms-flex-pack: center; 266 | justify-content: center; 267 | } 268 | header.top h1 .ofThe { 269 | display: -webkit-box; 270 | display: -ms-flexbox; 271 | display: flex; 272 | font-size: 3rem; 273 | color: #f5a623; 274 | -webkit-box-pack: center; 275 | -ms-flex-pack: center; 276 | justify-content: center; 277 | -webkit-box-align: center; 278 | -ms-flex-align: center; 279 | align-items: center; 280 | background: url("images/anchor.svg") center no-repeat; 281 | background-size: cover; 282 | padding: 0 1rem; 283 | } 284 | header.top h1 .ofThe .of { 285 | padding-right: 2rem; 286 | position: relative; 287 | right: -0.5rem; 288 | } 289 | header.top h3 { 290 | margin: 0; 291 | font-size: 2rem; 292 | color: #f5a623; 293 | position: relative; 294 | display: inline-block; 295 | } 296 | header.top h3 span { 297 | background: #fff; 298 | position: relative; 299 | z-index: 2; 300 | padding-left: 1rem; 301 | padding-right: 1rem; 302 | } 303 | header.top h3:before, 304 | header.top h3:after { 305 | display: block; 306 | z-index: 1; 307 | background: #000; 308 | position: absolute; 309 | width: 130%; 310 | height: 1px; 311 | content: ''; 312 | top: 5px; 313 | margin-left: -15%; 314 | } 315 | header.top h3:after { 316 | top: auto; 317 | bottom: 7px; 318 | } 319 | .catch-of-the-day { 320 | display: -webkit-box; 321 | display: -ms-flexbox; 322 | display: flex; 323 | height: 90vh; 324 | max-width: 1500px; 325 | margin: 0 auto; 326 | margin-top: 5vh; 327 | -webkit-perspective: 1000; 328 | perspective: 1000; 329 | -webkit-transform-style: preserve-3d; 330 | transform-style: preserve-3d; 331 | } 332 | .catch-of-the-day > * { 333 | -webkit-box-flex: 1; 334 | -ms-flex: 1 4 auto; 335 | flex: 1 4 auto; 336 | padding: 2rem; 337 | border: 1rem double #1a1a1a; 338 | position: relative; 339 | background: #fff; 340 | -webkit-transition: all 0.3s; 341 | transition: all 0.3s; 342 | box-shadow: 0px 5px 5px rgba(0,0,0,0.1); 343 | overflow: scroll; 344 | } 345 | .catch-of-the-day > *:first-child { 346 | -ms-flex-negative: 1; 347 | flex-shrink: 1; 348 | -ms-flex-preferred-size: 50%; 349 | flex-basis: 50%; 350 | -webkit-transform: translateX(50%) rotateY(6deg) translateX(-50%); 351 | transform: translateX(50%) rotateY(6deg) translateX(-50%); 352 | } 353 | .catch-of-the-day > *:nth-child(2) { 354 | -webkit-transform: translateX(-50%) rotateY(-14deg) translateX(50%); 355 | transform: translateX(-50%) rotateY(-14deg) translateX(50%); 356 | border-left: 0; 357 | border-right: 0; 358 | min-width: 300px; 359 | } 360 | .catch-of-the-day > *:last-child { 361 | -ms-flex-negative: 1; 362 | flex-shrink: 1; 363 | -ms-flex-preferred-size: 50%; 364 | flex-basis: 50%; 365 | -webkit-transform: translateX(-50%) rotateY(10deg) translateX(50%) scale(1.08) translateX(24px); 366 | transform: translateX(-50%) rotateY(10deg) translateX(50%) scale(1.08) translateX(24px); 367 | } 368 | input#fold:not(:checked) ~ #main .catch-of-the-day > * { 369 | -webkit-transform: none; 370 | transform: none; 371 | } 372 | label[for="fold"] { 373 | position: absolute; 374 | top: 1rem; 375 | left: 1rem; 376 | text-transform: uppercase; 377 | font-size: 1.3rem; 378 | background: #000; 379 | color: #fff; 380 | border: 2px solid #000; 381 | cursor: pointer; 382 | padding: 0.5rem 1rem; 383 | } 384 | input#fold { 385 | display: none; 386 | } 387 | input#fold:checked + label { 388 | background: #fff; 389 | color: #000; 390 | } 391 | ul { 392 | list-style: none; 393 | margin: 0; 394 | padding: 0; 395 | } 396 | ul.order li { 397 | border-bottom: 1px solid #000; 398 | padding: 2rem 0; 399 | display: -webkit-box; 400 | display: -ms-flexbox; 401 | display: flex; 402 | font-size: 1.4rem; 403 | -webkit-box-pack: justify; 404 | -ms-flex-pack: justify; 405 | justify-content: space-between; 406 | -webkit-box-align: center; 407 | -ms-flex-align: center; 408 | align-items: center; 409 | } 410 | ul.order li:hover button { 411 | display: inline; 412 | } 413 | ul.order li button { 414 | border: 0; 415 | display: none; 416 | line-height: 1; 417 | padding: 0; 418 | } 419 | ul.order li.total { 420 | font-weight: 600; 421 | border-bottom: 3px solid #000; 422 | border-top: 3px double #000; 423 | } 424 | ul.order li.unavailable { 425 | text-decoration: line-through; 426 | background: #f8d0d2; 427 | } 428 | ul.order li .price { 429 | font-size: 1.2rem; 430 | } 431 | ul.order li span.count { 432 | position: relative; 433 | overflow: hidden; 434 | float: left; 435 | } 436 | ul.order li span.count span { 437 | display: inline-block; 438 | } 439 | .order-title { 440 | text-align: center; 441 | } 442 | .fish-edit { 443 | margin-bottom: 20px; 444 | border: 2px solid #000; 445 | overflow: hidden; 446 | display: -webkit-box; 447 | display: -ms-flexbox; 448 | display: flex; 449 | -ms-flex-wrap: wrap; 450 | flex-wrap: wrap; 451 | } 452 | .fish-edit input, 453 | .fish-edit textarea, 454 | .fish-edit select { 455 | width: 33.33%; 456 | padding: 10px; 457 | line-height: 1; 458 | font-size: 1.2rem; 459 | border: 0; 460 | border-bottom: 1px solid #000; 461 | border-right: 1px solid #000; 462 | -webkit-appearance: none; 463 | -moz-appearance: none; 464 | appearance: none; 465 | border-radius: 0; 466 | background: #fff; 467 | } 468 | .fish-edit input:focus, 469 | .fish-edit textarea:focus, 470 | .fish-edit select:focus { 471 | outline: 0; 472 | background: #fef2de; 473 | } 474 | .fish-edit textarea { 475 | width: 100%; 476 | } 477 | .fish-edit input:last-of-type { 478 | width: 100%; 479 | } 480 | .fish-edit button { 481 | width: 100%; 482 | border: 0; 483 | } 484 | .list-of-fish { 485 | border-top: 2px solid #000; 486 | border-bottom: 1px solid #000; 487 | padding-top: 5px; 488 | margin-top: 2rem; 489 | -webkit-transform: translateZ(0); 490 | transform: translateZ(0); 491 | } 492 | .menu-fish { 493 | border-bottom: 2px solid #000; 494 | border-top: 1px solid #000; 495 | padding-bottom: 2rem; 496 | padding-top: 2rem; 497 | margin-bottom: 5px; 498 | clear: both; 499 | overflow: hidden; 500 | } 501 | .menu-fish p { 502 | margin: 0; 503 | font-size: 1.8rem; 504 | } 505 | .menu-fish .fish-name { 506 | margin: 0; 507 | display: -webkit-box; 508 | display: -ms-flexbox; 509 | display: flex; 510 | -webkit-box-pack: justify; 511 | -ms-flex-pack: justify; 512 | justify-content: space-between; 513 | -webkit-box-align: center; 514 | -ms-flex-align: center; 515 | align-items: center; 516 | } 517 | .menu-fish .price { 518 | font-size: 1.4rem; 519 | -webkit-box-pack: end; 520 | -ms-flex-pack: end; 521 | justify-content: flex-end; 522 | } 523 | .menu-fish img { 524 | float: left; 525 | width: 150px; 526 | margin-right: 1rem; 527 | } 528 | button, 529 | input[type=submit] { 530 | text-transform: uppercase; 531 | background: none; 532 | border: 1px solid #000; 533 | font-weight: 600; 534 | font-size: 1.5rem; 535 | font-family: 'Open Sans'; 536 | -webkit-transition: all 0.2s; 537 | transition: all 0.2s; 538 | position: relative; 539 | z-index: 2; 540 | } 541 | button[disabled], 542 | input[type=submit][disabled] { 543 | color: #d12028; 544 | background: #fff; 545 | border-color: #d12028; 546 | -webkit-transform: rotate(-10deg) scale(2) translateX(50%) translateY(-50%); 547 | transform: rotate(-10deg) scale(2) translateX(50%) translateY(-50%); 548 | } 549 | button[disabled]:hover, 550 | input[type=submit][disabled]:hover { 551 | color: #d12028; 552 | cursor: not-allowed; 553 | } 554 | button[disabled]:after, 555 | input[type=submit][disabled]:after { 556 | display: none; 557 | } 558 | button:after, 559 | input[type=submit]:after { 560 | content: ''; 561 | z-index: -1; 562 | display: block; 563 | background: #000; 564 | position: absolute; 565 | width: 100%; 566 | height: 0%; 567 | left: 0; 568 | top: 0; 569 | -webkit-transition: all 0.2s; 570 | transition: all 0.2s; 571 | } 572 | button:hover, 573 | input[type=submit]:hover, 574 | button:focus, 575 | input[type=submit]:focus { 576 | color: #fff; 577 | outline: 0; 578 | } 579 | button:hover:after, 580 | input[type=submit]:hover:after, 581 | button:focus:after, 582 | input[type=submit]:focus:after { 583 | height: 100%; 584 | } 585 | button.warning:after, 586 | input[type=submit].warning:after { 587 | background: #d12028; 588 | } 589 | button.success:after, 590 | input[type=submit].success:after { 591 | background: #2dc22d; 592 | } 593 | button.github, 594 | input[type=submit].github, 595 | button.facebook, 596 | input[type=submit].facebook, 597 | button.twitter, 598 | input[type=submit].twitter { 599 | border: 0; 600 | display: block; 601 | margin-bottom: 2rem; 602 | width: 100%; 603 | color: #fff; 604 | padding: 2rem; 605 | } 606 | button.github, 607 | input[type=submit].github { 608 | background: #82d465; 609 | } 610 | button.github:after, 611 | input[type=submit].github:after { 612 | background: #5cc437; 613 | } 614 | button.facebook, 615 | input[type=submit].facebook { 616 | background: #3864a3; 617 | } 618 | button.facebook:after, 619 | input[type=submit].facebook:after { 620 | background: #2d5082; 621 | } 622 | button.twitter, 623 | input[type=submit].twitter { 624 | background: #5ea9dd; 625 | } 626 | button.twitter:after, 627 | input[type=submit].twitter:after { 628 | background: #2c8dd0; 629 | } 630 | .store-selector { 631 | background: #fff; 632 | max-width: 500px; 633 | margin: 50px auto; 634 | padding: 2rem; 635 | border: 2px solid #000; 636 | } 637 | .store-selector input, 638 | .store-selector button { 639 | width: 100%; 640 | } 641 | .store-selector input[type="text"], 642 | .store-selector button[type="text"] { 643 | text-align: center; 644 | font-size: 3rem; 645 | } 646 | -------------------------------------------------------------------------------- /catch-of-the-day/src/css/style.styl: -------------------------------------------------------------------------------- 1 | // Import all partials 2 | @require './_*.styl' 3 | 4 | 5 | header.top 6 | text-align center 7 | h1 8 | font-size 14.4rem 9 | line-height 0.7 // this font has a wacky baseline 10 | display flex 11 | justify-content: center; 12 | 13 | // This is a bunch of goofy CSS to make the logo look decent 14 | .ofThe 15 | display flex 16 | font-size 3rem 17 | color orange 18 | justify-content: center; 19 | align-items: center; 20 | background url('images/anchor.svg') center no-repeat; 21 | background-size cover 22 | padding 0 1rem 23 | .of 24 | padding-right 2rem 25 | position relative 26 | right -0.5rem 27 | h3 28 | margin 0 29 | font-size 2rem 30 | color orange 31 | position relative 32 | display inline-block 33 | span 34 | background white 35 | position relative 36 | z-index 2 37 | padding-left 1rem 38 | padding-right 1rem 39 | &:before, &:after 40 | display block 41 | z-index 1 42 | background black 43 | position absolute 44 | width 130% 45 | height 1px 46 | content '' 47 | top 5px 48 | margin-left -15% 49 | &:after 50 | top auto 51 | bottom 7px 52 | 53 | .catch-of-the-day 54 | display flex 55 | height 90vh 56 | max-width:1500px 57 | margin 0 auto 58 | margin-top 5vh 59 | perspective: 1000; 60 | transform-style preserve-3d 61 | & > * 62 | flex 1 4 auto 63 | padding 2rem 64 | border 1rem double lighten(black,10%) 65 | position relative 66 | background white 67 | transition all 0.3s 68 | box-shadow 0px 5px 5px rgba(0,0,0,0.1) 69 | overflow scroll 70 | &:first-child 71 | flex-shrink 1 // take 4x the extra room 72 | flex-basis 50% 73 | transform translateX(50%) rotateY(6deg) translateX(-50%) 74 | &:nth-child(2) 75 | transform translateX(-50%) rotateY(-14deg) translateX(50%) 76 | border-left 0 77 | border-right 0 78 | min-width 300px 79 | &:last-child 80 | flex-shrink 1 // take 4x the extra room 81 | flex-basis 50% 82 | transform translateX(-50%) rotateY(10deg) translateX(50%) scale(1.08) translateX(24px) 83 | // Folding Transforms 84 | // Take off folding when not checked 85 | input#fold:not(:checked) ~ #main 86 | .catch-of-the-day > * 87 | transform none 88 | 89 | label[for="fold"] 90 | position absolute 91 | top 1rem 92 | left 1rem 93 | text-transform uppercase 94 | font-size 1.3rem 95 | background black 96 | color white 97 | border 2px solid black 98 | cursor pointer 99 | padding 0.5rem 1rem 100 | 101 | input#fold 102 | display none 103 | &:checked + label 104 | background white 105 | color black 106 | 107 | ul 108 | list-style none 109 | margin 0 110 | padding 0 111 | 112 | ul.order 113 | // Default state 114 | li 115 | border-bottom 1px solid black 116 | padding 2rem 0 117 | display flex 118 | font-size 1.4rem 119 | justify-content space-between 120 | align-items center 121 | &:hover 122 | // padding 1rem 0 123 | button 124 | display inline 125 | button 126 | border 0 127 | display none 128 | line-height 1 129 | padding 0 130 | &.total 131 | font-weight 600 132 | border-bottom 3px solid black 133 | border-top 3px double black 134 | &.unavailable 135 | text-decoration line-through 136 | background lighten(red, 80%) 137 | .price 138 | font-size 1.2rem 139 | span.count 140 | position relative 141 | overflow hidden 142 | float left // only works if it's floated?! 143 | span 144 | display inline-block 145 | // transition all 0.5s 146 | 147 | .order-title 148 | text-align center 149 | 150 | .fish-edit 151 | margin-bottom 20px 152 | border 2px solid black 153 | overflow hidden 154 | display flex 155 | flex-wrap wrap 156 | input, textarea, select 157 | width 33.33% 158 | padding 10px 159 | line-height 1 160 | font-size 1.2rem 161 | border 0 162 | border-bottom 1px solid black 163 | border-right 1px solid black 164 | appearance none 165 | border-radius 0 166 | background white 167 | &:focus 168 | outline 0 169 | background lighten(orange, 85%) 170 | textarea 171 | width 100% 172 | input:last-of-type 173 | width 100% 174 | button 175 | width 100% 176 | border 0 177 | // Menu Styles 178 | .list-of-fish 179 | border-top 2px solid black 180 | border-bottom 1px solid black 181 | padding-top 5px 182 | margin-top 2rem 183 | transform translateZ(0); 184 | 185 | .menu-fish 186 | border-bottom 2px solid black 187 | border-top 1px solid black 188 | padding-bottom 2rem 189 | padding-top 2rem 190 | margin-bottom 5px 191 | clear both 192 | overflow hidden 193 | p 194 | margin 0 195 | font-size 1.8rem 196 | .fish-name 197 | margin 0 198 | display flex 199 | justify-content space-between 200 | align-items center 201 | .price 202 | font-size 1.4rem 203 | // color orange 204 | justify-content flex-end 205 | // font-family 'Open Sans Condensed' 206 | img 207 | float left 208 | width 150px 209 | margin-right 1rem 210 | 211 | button, input[type=submit] 212 | text-transform uppercase 213 | background none 214 | border 1px solid black 215 | font-weight 600 216 | font-size 1.5rem 217 | font-family 'Open Sans' 218 | transition all 0.2s 219 | position relative 220 | z-index 2 221 | &[disabled] 222 | color red 223 | background white 224 | border-color red 225 | transform rotate(-10deg) scale(2) translateX(50%) translateY(-50%) 226 | &:hover 227 | color red 228 | cursor not-allowed 229 | &:after 230 | display none 231 | &:after 232 | content '' 233 | z-index -1 234 | display block 235 | background black 236 | position absolute 237 | width 100% 238 | height 0% 239 | left 0 240 | top 0 241 | transition all 0.2s 242 | &:hover, &:focus 243 | color white 244 | outline 0 245 | &:after 246 | height 100% 247 | // variants 248 | &.warning 249 | &:after 250 | background red 251 | &.success 252 | &:after 253 | background green 254 | 255 | &.github, &.facebook, &.twitter 256 | border 0 257 | display block 258 | margin-bottom 2rem 259 | width 100% 260 | color white 261 | padding 2rem 262 | &.github 263 | background #82D465 264 | &:after 265 | background darken(#82D465, 20%) 266 | &.facebook 267 | background #3864A3 268 | &:after 269 | background darken(#3864A3, 20%) 270 | &.twitter 271 | background #5EA9DD 272 | &:after 273 | background darken(#5EA9DD, 20%) 274 | // Store Selector 275 | .store-selector 276 | background white 277 | max-width 500px 278 | margin 50px auto 279 | padding 2rem 280 | border 2px solid black 281 | input, button 282 | width 100% 283 | &[type="text"] 284 | text-align center 285 | font-size 3rem 286 | -------------------------------------------------------------------------------- /catch-of-the-day/src/helpers.js: -------------------------------------------------------------------------------- 1 | export function formatPrice(cents) { 2 | return `$${(cents / 100).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`; 3 | } 4 | 5 | export function rando(arr) { 6 | return arr[Math.floor(Math.random() * arr.length)]; 7 | } 8 | 9 | export function slugify(text) { 10 | return text.toString().toLowerCase() 11 | .replace(/\s+/g, '-') // Replace spaces with - 12 | .replace(/[^\w\-]+/g, '') // Remove all non-word chars 13 | .replace(/\-\-+/g, '-') // Replace multiple - with single - 14 | .replace(/^-+/, '') // Trim - from start of text 15 | .replace(/-+$/, ''); // Trim - from end of text 16 | } 17 | 18 | export function getFunName() { 19 | const adjectives = ['adorable', 'beautiful', 'clean', 'drab', 'elegant', 'fancy', 'glamorous', 'handsome', 'long', 'magnificent', 'old-fashioned', 'plain', 'quaint', 'sparkling', 'ugliest', 'unsightly', 'angry', 'bewildered', 'clumsy', 'defeated', 'embarrassed', 'fierce', 'grumpy', 'helpless', 'itchy', 'jealous', 'lazy', 'mysterious', 'nervous', 'obnoxious', 'panicky', 'repulsive', 'scary', 'thoughtless', 'uptight', 'worried']; 20 | 21 | const nouns = ['women', 'men', 'children', 'teeth', 'feet', 'people', 'leaves', 'mice', 'geese', 'halves', 'knives', 'wives', 'lives', 'elves', 'loaves', 'potatoes', 'tomatoes', 'cacti', 'foci', 'fungi', 'nuclei', 'syllabuses', 'analyses', 'diagnoses', 'oases', 'theses', 'crises', 'phenomena', 'criteria', 'data']; 22 | 23 | return `${rando(adjectives)}-${rando(adjectives)}-${rando(nouns)}`; 24 | } 25 | -------------------------------------------------------------------------------- /catch-of-the-day/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { BrowserRouter, Match, Miss } from 'react-router'; 4 | import './css/style.css'; 5 | import App from './components/App'; 6 | 7 | import StorePicker from './components/StorePicker'; 8 | import NotFound from './components/NotFound'; 9 | 10 | const Root = () => { 11 | return ( 12 | 13 |
    14 | 15 | 16 | 17 |
    18 |
    19 | ) 20 | } 21 | 22 | render(, document.querySelector('#main')); 23 | -------------------------------------------------------------------------------- /catch-of-the-day/src/sample-fishes.js: -------------------------------------------------------------------------------- 1 | // This is just some sample data so you don't have to think of your own! 2 | module.exports = { 3 | fish1: { 4 | name: 'Pacific Halibut', 5 | image: 'http://i.istockimg.com/file_thumbview_approve/36248396/5/stock-photo-36248396-blackened-cajun-sea-bass.jpg', 6 | desc: 'Everyones favorite white fish. We will cut it to the size you need and ship it.', 7 | price: 1724, 8 | status: 'available' 9 | }, 10 | 11 | fish2: { 12 | name: 'Lobster', 13 | image: 'http://i.istockimg.com/file_thumbview_approve/32135274/5/stock-photo-32135274-cooked-lobster.jpg', 14 | desc: 'These tender, mouth-watering beauties are a fantastic hit at any dinner party.', 15 | price: 3200, 16 | status: 'available' 17 | }, 18 | 19 | fish3: { 20 | name: 'Sea Scallops', 21 | image: 'http://i.istockimg.com/file_thumbview_approve/58624176/5/stock-photo-58624176-scallops-on-black-stone-plate.jpg', 22 | desc: 'Big, sweet and tender. True dry-pack scallops from the icey waters of Alaska. About 8-10 per pound', 23 | price: 1684, 24 | status: 'unavailable' 25 | }, 26 | 27 | fish4: { 28 | name: 'Mahi Mahi', 29 | image: 'http://i.istockimg.com/file_thumbview_approve/12556651/5/stock-photo-12556651-mahimahi.jpg', 30 | desc: 'Lean flesh with a mild, sweet flavor profile, moderately firm texture and large, moist flakes. ', 31 | price: 1129, 32 | status: 'available' 33 | }, 34 | 35 | fish5: { 36 | name: 'King Crab', 37 | image: 'http://i.istockimg.com/file_thumbview_approve/18294110/5/stock-photo-18294110-king-crab-legs.jpg', 38 | desc: 'Crack these open and enjoy them plain or with one of our cocktail sauces', 39 | price: 4234, 40 | status: 'available' 41 | }, 42 | 43 | fish6: { 44 | name: 'Atlantic Salmon', 45 | image: 'http://i.istockimg.com/file_thumbview_approve/56241842/5/stock-photo-56241842-salmon-fish.jpg', 46 | desc: 'This flaky, oily salmon is truly the king of the sea. Bake it, grill it, broil it...as good as it gets!', 47 | price: 1453, 48 | status: 'available' 49 | }, 50 | 51 | fish7: { 52 | name: 'Oysters', 53 | image: 'http://i.istockimg.com/file_thumbview_approve/58626682/5/stock-photo-58626682-fresh-oysters-on-a-black-stone-plate-top-view.jpg', 54 | desc: 'A soft plump oyster with a sweet salty flavor and a clean finish.', 55 | price: 2543, 56 | status: 'available' 57 | }, 58 | 59 | fish8: { 60 | name: 'Mussels', 61 | image: 'http://i.istockimg.com/file_thumbview_approve/40450406/5/stock-photo-40450406-steamed-mussels.jpg', 62 | desc: 'The best mussels from the Pacific Northwest with a full-flavored and complex taste.', 63 | price: 425, 64 | status: 'available' 65 | }, 66 | 67 | fish9: { 68 | name: 'Jumbo Prawns', 69 | image: 'http://i.istockimg.com/file_thumbview_approve/67121439/5/stock-photo-67121439-fresh-tiger-shrimp-on-ice-on-a-black-stone-table.jpg', 70 | desc: 'With 21-25 two bite prawns in each pound, these sweet morsels are perfect for shish-kabobs.', 71 | price: 2250, 72 | status: 'available' 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![](http://wes.io/dgAQ/content) 2 | 3 | # React For Beginners — [ReactForBeginners.com](https://ReactForBeginners.com) 4 | 5 | Starter files for the React For Beginners course. Come Learn React with me! 6 | 7 | The code in this repo meant to be a reference point for anyone following along with the video course. 8 | 9 | ## To Start 10 | 11 | 1. cd into `catch-of-the-day` and follow along with the videos 12 | 13 | The `stepped-solutions` Each folder contains the files for the beginning of each video should you need them. So, if you need any code, pull the appropriate file into your `catch-of-the-day` folder. 14 | 15 | You are welcome to submit Pull Requests but I'd like to keep the code as similar as possible to the course content. 16 | 17 | ### Code Use 18 | 19 | You are welcome to use this code in your own applications. If you would like to use it for training purposes, please shoot me a message first to make sure it's okay. 20 | 21 | 22 | # Frequently Asked Questions 23 | 24 | #### :question: Where are folders `2`, `3`, and `5`? 25 | Not all the videos have significant enough code changes to warrant an entire folder. Although you should be coding them all yourself, the code is available in the next video's folder. 26 | 27 | #### :question: I tried installing the Babel syntax highlighter but it didn't work! 28 | 29 | There are a few possible options: 30 | 31 | * If you are on Sublime Text 2, you should Upgrade to Sublime Text 3. 32 | * Some users have reported restarting works 33 | * You can try the [JavaScript Next](https://packagecontrol.io/packages/JavaScriptNext%20-%20ES6%20Syntax) syntax highlighter instead 34 | 35 | #### :question: I can't set Babel as the default syntax highlighter! 36 | 37 | Make sure you are in a file with the extension of `.js` before you do this step - you can't set the default for a file without having a file open! 38 | 39 | #### :question: I can't see the React tab in my dev tools 40 | 41 | Restart your dev tools or your chrome browser entirely. They will only show up when you are viewing a React app - so make sure you test it on Facebook or another website that is running React. It won't work on your empty `main.js` file until you `import React from 'react'`. 42 | 43 | #### :question: `npm start` doesn't update the app on file save, or doesn't run correctly. 44 | 45 | There may be a few different causes for this: 46 | - Webpack currently can't handle folder/file names that contain parentheses. 47 | - Webpack also has problems running inside folders for Dropbox/Google Drive type services. Git is recommended for keeping your files in sync across multiple computers. 48 | 49 | #### :question: What Sublime Text Packages are you using? What Terminal Theme? 50 | 51 | * I've written indepth over at [WesBos.com/uses](http://wesbos.com/uses) 52 | * Theme + Colour Scheme → [Cobalt 2](https://packagecontrol.io/packages/Theme%20-%20Cobalt2) 53 | * JS Syntax Highlighting → [Babel](https://packagecontrol.io/packages/Babel) 54 | * HTML + CSS Shortcuts → [Emmet](https://packagecontrol.io/packages/Emmet) — You can [get emmet working with JSX here](http://wesbos.com/emmet-react-jsx-sublime/) 55 | --------------------------------------------------------------------------------