├── .gitignore ├── css └── styles.css ├── index.html └── js └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', 'Sans Serif'; 3 | font-size: 16px; 4 | padding: 0; 5 | margin: 0; 6 | } 7 | 8 | header { 9 | background-color: #03A9F4; 10 | padding: 14px; 11 | box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); 12 | } 13 | 14 | h1, h2 { 15 | font-weight: 300; 16 | } 17 | 18 | header > h1 { 19 | font-weight: 300; 20 | font-size: 24px; 21 | margin: 0; 22 | color: #FFFFFF; 23 | } 24 | 25 | h2 { 26 | font-size: 24px; 27 | margin: 24px 0; 28 | } 29 | 30 | h2 > .currency { 31 | color: #0288D1; 32 | } 33 | 34 | .principal { 35 | color: #0288D1; 36 | } 37 | 38 | .interest { 39 | color: #EF6C00; 40 | } 41 | 42 | input[type=text] { 43 | -webkit-appearance: none; 44 | width: 150px; 45 | height: 24px; 46 | padding: 3px 8px; 47 | font-size: 14px; 48 | line-height: 1.42857143; 49 | color: #555; 50 | border: 1px solid #ccc; 51 | border-radius: 2px; 52 | -webkit-box-shadow: none; 53 | box-shadow: none; 54 | } 55 | 56 | table { 57 | border-collapse: collapse; 58 | font-weight: 300; 59 | } 60 | 61 | th { 62 | text-align: right; 63 | font-weight: 400; 64 | } 65 | 66 | td { 67 | text-align: right; 68 | padding: 0 .5rem; 69 | } 70 | 71 | 72 | 73 | th, 74 | td { 75 | border: solid 1px #EEEEEE !important; 76 | padding: 1px 4px; 77 | } 78 | 79 | label { 80 | display: inline-block; 81 | width: 80px; 82 | text-align: right; 83 | margin-right: 4px; 84 | } 85 | 86 | .content { 87 | padding: 20px; 88 | } 89 | 90 | .bar { 91 | display: inline-block; 92 | border: none; 93 | height: 8px; 94 | } 95 | 96 | .bar.principal { 97 | background-color: #0288D1; 98 | margin-right:.5px; 99 | } 100 | 101 | .bar.interest { 102 | background-color: #EF6C00; 103 | margin-left:.5px; 104 | } 105 | 106 | .stretch { 107 | width: 100%; 108 | padding-left:0; 109 | padding-right:0; 110 | } 111 | 112 | .flex { 113 | display: -webkit-flex; 114 | display: flex; 115 | } 116 | 117 | .currency::before { 118 | content:"$"; 119 | } 120 | 121 | .left { 122 | text-align: left; 123 | } 124 | 125 | .form > div { 126 | margin: 6px 0; 127 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /js/app.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var ReactDOM = require('react-dom'); 3 | 4 | var calculatePayment = function(principal, years, rate) { 5 | var monthlyRate = rate / 100 / 12; 6 | var monthlyPayment = principal * monthlyRate / (1 - (Math.pow(1/(1 + monthlyRate), years * 12))); 7 | var balance = principal; 8 | var amortization = []; 9 | for (var y=0; y 28 |

{this.props.title}

29 | 30 | ); 31 | } 32 | }); 33 | 34 | var AmortizationChart = React.createClass({ 35 | render: function () { 36 | var items = this.props.data.map(function (year, index) { 37 | return ( 38 | 39 | {index + 1} 40 | {Math.round(year.principalY).toLocaleString()} 41 | 42 |
43 |
44 |
45 |
46 | 47 | {Math.round(year.interestY).toLocaleString()} 48 | {Math.round(year.balance).toLocaleString()} 49 | 50 | ); 51 | }); 52 | return ( 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | {items} 64 |
YearPrincipalInterestBalance
65 | ); 66 | } 67 | }); 68 | 69 | var MortgageCalculator = React.createClass({ 70 | getInitialState: function() { 71 | return { 72 | principal: this.props.principal, 73 | years: this.props.years, 74 | rate: this.props.rate 75 | }; 76 | }, 77 | principalChange: function(event) { 78 | this.setState({principal: event.target.value}); 79 | }, 80 | yearsChange: function(event) { 81 | this.setState({years: event.target.value}); 82 | }, 83 | rateChange: function(event) { 84 | this.setState({rate: event.target.value}); 85 | }, 86 | render: function () { 87 | var payment = calculatePayment(this.state.principal, this.state.years, this.state.rate); 88 | var monthlyPayment = payment.monthlyPayment; 89 | var amortization = payment.amortization; 90 | return ( 91 |
92 |
93 |
94 | 95 | 96 |
97 |
98 | 99 | 100 |
101 |
102 | 103 | 104 |
105 |
106 |

Monthly Payment: {Number(monthlyPayment.toFixed(2)).toLocaleString()}

107 | 108 |
109 | ); 110 | } 111 | }); 112 | 113 | var App = React.createClass({ 114 | render: function () { 115 | return ( 116 |
117 |
118 | 119 |
120 | ); 121 | } 122 | }); 123 | 124 | ReactDOM.render(, document.getElementById("app")); --------------------------------------------------------------------------------