├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── bower.json ├── dist ├── react-simple-currency.js └── react-simple-currency.min.js ├── example └── src │ ├── .gitignore │ ├── example.js │ ├── example.less │ └── index.html ├── gulpfile.js ├── lib └── SimpleCurrencyInput.js ├── package.json └── src └── SimpleCurrencyInput.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = true 10 | indent_style = tab 11 | 12 | [*.json] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .publish/* 2 | dist/* 3 | example/dist/* 4 | lib/* 5 | node_modules/* 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "plugins": [ 8 | "react" 9 | ], 10 | "rules": { 11 | "curly": [2, "multi-line"], 12 | "quotes": [2, "single", "avoid-escape"], 13 | "react/display-name": 0, 14 | "react/jsx-boolean-value": 1, 15 | "react/jsx-quotes": 1, 16 | "react/jsx-no-undef": 1, 17 | "react/jsx-sort-props": 0, 18 | "react/jsx-sort-prop-types": 1, 19 | "react/jsx-uses-react": 1, 20 | "react/jsx-uses-vars": 1, 21 | "react/no-did-mount-set-state": 1, 22 | "react/no-did-update-set-state": 1, 23 | "react/no-multi-comp": 1, 24 | "react/no-unknown-property": 1, 25 | "react/prop-types": 1, 26 | "react/react-in-jsx-scope": 1, 27 | "react/self-closing-comp": 1, 28 | "react/wrap-multilines": 1, 29 | "semi": [2, 'never'], 30 | "strict": 0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage tools 11 | lib-cov 12 | coverage 13 | coverage.html 14 | .cover* 15 | 16 | # Dependency directory 17 | node_modules 18 | 19 | # Example build directory 20 | example/dist 21 | .publish 22 | 23 | # Editor and other tmp files 24 | *.swp 25 | *.un~ 26 | *.iml 27 | *.ipr 28 | *.iws 29 | *.sublime-* 30 | .idea/ 31 | *.DS_Store 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Currency Input 2 | 3 | React Simple Currency is a wrapper that takes a value in cents and then 4 | masks it as currency. 5 | 6 | ## Demo & Examples 7 | 8 | Live demo: [leonardowf.github.io/react-simple-currency](http://leonardowf.github.io/react-simple-currency/) 9 | 10 | To build the examples locally, run: 11 | 12 | ``` 13 | npm install 14 | npm start 15 | ``` 16 | 17 | Then open [`localhost:8000`](http://localhost:8000) in a browser. 18 | 19 | 20 | ## Installation 21 | 22 | The easiest way to use react-simple-currency is to install it from NPM and include it in your own React build process (using [Browserify](http://browserify.org), [Webpack](http://webpack.github.io/), etc). 23 | 24 | You can also use the standalone build by including `dist/react-simple-currency.js` in your page. If you use this, make sure you have already included React, and it is available as a global variable. 25 | 26 | ``` 27 | npm install react-simple-currency --save 28 | ``` 29 | 30 | 31 | ## Usage 32 | 33 | ```js 34 | var SimpleCurrencyInput = require('react-simple-currency'); 35 | 36 | 48 | 49 | // onMoneyInputChange will be called with params: rawValue and displayValue 50 | // onMoneyInputBlur will be called with param: event 51 | // onInputFocus will be called with param: event 52 | 53 | ``` 54 | 55 | ## Development (`src`, `lib` and the build process) 56 | 57 | **NOTE:** The source code for the component is in `src` . A transpiled CommonJS version (generated with Babel) is available in `lib` for use with node.js, browserify and webpack. A UMD bundle is also built to `dist`, which can be included without the need for any build system. 58 | 59 | To build, watch and serve the examples (which will also watch the component source), run `npm start`. If you just want to watch changes to `src` and rebuild `lib`, run `npm run watch` (this is useful if you are working with `npm link`). 60 | 61 | ## License 62 | 63 | MIT 64 | 65 | 2016 Leonardo Wistuba de França. 66 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-simple-currency", 3 | "version": "0.0.0", 4 | "description": "SimpleCurrencyInput", 5 | "main": "dist/react-simple-currency.min.js", 6 | "homepage": "https://github.com/leonardowf/react-simple-currency", 7 | "authors": [ 8 | "Leonardo Wistuba de França" 9 | ], 10 | "moduleType": [ 11 | "amd", 12 | "globals", 13 | "node" 14 | ], 15 | "keywords": [ 16 | "react", 17 | "react-component" 18 | ], 19 | "license": "MIT", 20 | "ignore": [ 21 | ".editorconfig", 22 | ".gitignore", 23 | "package.json", 24 | "src", 25 | "node_modules", 26 | "example", 27 | "test" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /dist/react-simple-currency.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SimpleCurrencyInput = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 3) { 104 | var chars = beforeSeparator.split('').reverse(); 105 | var withDots = ''; 106 | for (var i = chars.length - 1; i >= 0; i--) { 107 | var char = chars[i]; 108 | var dot = i % 3 === 0 ? this.props.delimiter : ''; 109 | withDots = '' + withDots + char + dot; 110 | } 111 | withDots = withDots.substring(0, withDots.length - 1); 112 | beforeSeparator = withDots; 113 | } 114 | result = beforeSeparator + this.props.separator + afterSeparator; 115 | 116 | if (this.props.unit) { 117 | result = this.props.unit + ' ' + result; 118 | } 119 | 120 | return result; 121 | } 122 | }, { 123 | key: 'render', 124 | value: function render() { 125 | return React.createElement('input', { 126 | id: this.props.id, 127 | className: this.props.className, 128 | onBlur: this.props.onInputBlur, 129 | onFocus: this.props.onInputFocus, 130 | onChange: this.onInputType, 131 | value: this.formattedRawValue(this.state.rawValue), 132 | disabled: this.props.disabled, 133 | autoFocus: this.props.autoFocus, 134 | tabIndex: this.state.tabIndex, 135 | readOnly: this.state.readOnly 136 | }); 137 | } 138 | }]); 139 | 140 | return SimpleCurrencyInput; 141 | })(React.Component); 142 | 143 | var repeatZeroes = function repeatZeroes(times) { 144 | var result = ''; 145 | var i = 0; 146 | for (i = 0; i < times; i++) { 147 | result += '0'; 148 | } 149 | 150 | return result; 151 | }; 152 | 153 | var removeOccurrences = function removeOccurrences(from, toRemove) { 154 | toRemove = toRemove.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 155 | var re = new RegExp(toRemove, 'g'); 156 | return from.replace(re, ''); 157 | }; 158 | 159 | SimpleCurrencyInput.propTypes = { 160 | id: PropTypes.string, 161 | autoFocus: PropTypes.bool, 162 | delimiter: PropTypes.string, 163 | disabled: PropTypes.bool, 164 | onInputChange: PropTypes.func, 165 | onInputBlur: PropTypes.func, 166 | onInputFocus: PropTypes.func, 167 | precision: PropTypes.number, 168 | readOnly: PropTypes.bool, 169 | separator: PropTypes.string, 170 | tabIndex: PropTypes.number, 171 | unit: PropTypes.string, 172 | value: PropTypes.number.isRequired 173 | }; 174 | 175 | SimpleCurrencyInput.defaultProps = { 176 | value: 0, 177 | precision: 2, 178 | separator: '.', 179 | delimiter: ',', 180 | unit: '', 181 | disabled: false, 182 | autoFocus: false, 183 | onInputChange: function onInputChange() {}, 184 | onInputBlur: function onInputBlur() {}, 185 | onInputFocus: function onInputFocus() {} 186 | }; 187 | 188 | exports['default'] = SimpleCurrencyInput; 189 | module.exports = exports['default']; 190 | 191 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 192 | },{"prop-types":undefined}]},{},[1])(1) 193 | }); -------------------------------------------------------------------------------- /dist/react-simple-currency.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.SimpleCurrencyInput=e()}}(function(){return function e(t,n,r){function o(u,a){if(!n[u]){if(!t[u]){var s="function"==typeof require&&require;if(!a&&s)return s(u,!0);if(i)return i(u,!0);var p=new Error("Cannot find module '"+u+"'");throw p.code="MODULE_NOT_FOUND",p}var l=n[u]={exports:{}};t[u][0].call(l.exports,function(e){var n=t[u][1][e];return o(n?n:e)},l,l.exports,e,t,n,r)}return n[u].exports}for(var i="function"==typeof require&&require,u=0;u3){for(var u=o.split("").reverse(),a="",s=u.length-1;s>=0;s--){var p=u[s],l=s%3===0?this.props.delimiter:"";a=""+a+p+l}a=a.substring(0,a.length-1),o=a}return n=o+this.props.separator+i,this.props.unit&&(n=this.props.unit+" "+n),n}},{key:"render",value:function(){return s.createElement("input",{id:this.props.id,className:this.props.className,onBlur:this.props.onInputBlur,onFocus:this.props.onInputFocus,onChange:this.onInputType,value:this.formattedRawValue(this.state.rawValue),disabled:this.props.disabled,autoFocus:this.props.autoFocus,tabIndex:this.state.tabIndex,readOnly:this.state.readOnly})}}]),t}(s.Component),f=function(e){var t="",n=0;for(n=0;n 34 |
35 | 48 | 49 | 50 |

Raw: {this.state.raw}

51 |

Display: {this.state.display}

52 | 53 | ) 54 | } 55 | } 56 | 57 | ReactDOM.render(, document.getElementById('app')) 58 | -------------------------------------------------------------------------------- /example/src/example.less: -------------------------------------------------------------------------------- 1 | /* 2 | // Examples Stylesheet 3 | // ------------------- 4 | */ 5 | 6 | body { 7 | font-family: Helvetica Neue, Helvetica, Arial, sans-serif; 8 | font-size: 14px; 9 | color: #333; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | a { 15 | color: #08c; 16 | text-decoration: none; 17 | } 18 | 19 | a:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .container { 24 | margin-left: auto; 25 | margin-right: auto; 26 | max-width: 720px; 27 | padding: 1em; 28 | } 29 | 30 | .footer { 31 | margin-top: 50px; 32 | border-top: 1px solid #eee; 33 | padding: 20px 0; 34 | font-size: 12px; 35 | color: #999; 36 | } 37 | 38 | h1, h2, h3, h4, h5, h6 { 39 | color: #222; 40 | font-weight: 100; 41 | margin: 0.5em 0; 42 | } 43 | 44 | label { 45 | color: #999; 46 | display: inline-block; 47 | font-size: 0.85em; 48 | font-weight: bold; 49 | margin: 1em 0; 50 | text-transform: uppercase; 51 | } 52 | 53 | .hint { 54 | margin: 15px 0; 55 | font-style: italic; 56 | color: #999; 57 | } 58 | 59 | .my-input-class { 60 | background-color: #afafaf; 61 | } 62 | -------------------------------------------------------------------------------- /example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | SimpleCurrencyInput 4 | 5 | 6 | 7 |
8 |

Simple Currency Input

9 |

View project on GitHub

10 | 11 |
12 |
13 | 14 |
15 | 18 |
19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var initGulpTasks = require('react-component-gulp-tasks') 3 | 4 | /** 5 | * Tasks are added by the react-component-gulp-tasks package 6 | * 7 | * See https://github.com/JedWatson/react-component-gulp-tasks 8 | * for documentation. 9 | * 10 | * You can also add your own additional gulp tasks if you like. 11 | */ 12 | 13 | var taskConfig = { 14 | 15 | component: { 16 | name: 'SimpleCurrencyInput', 17 | dependencies: [ 18 | 'classnames', 19 | 'react', 20 | 'prop-types', 21 | 'react-dom' 22 | ], 23 | lib: 'lib' 24 | }, 25 | 26 | example: { 27 | src: 'example/src', 28 | dist: 'example/dist', 29 | files: [ 30 | 'index.html', 31 | '.gitignore' 32 | ], 33 | scripts: [ 34 | 'example.js' 35 | ], 36 | less: [ 37 | 'example.less' 38 | ] 39 | } 40 | 41 | } 42 | 43 | initGulpTasks(gulp, taskConfig) 44 | -------------------------------------------------------------------------------- /lib/SimpleCurrencyInput.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var React = require('react'); 16 | var PropTypes = require('prop-types'); 17 | 18 | var SimpleCurrencyInput = (function (_React$Component) { 19 | _inherits(SimpleCurrencyInput, _React$Component); 20 | 21 | function SimpleCurrencyInput(props) { 22 | _classCallCheck(this, SimpleCurrencyInput); 23 | 24 | _get(Object.getPrototypeOf(SimpleCurrencyInput.prototype), 'constructor', this).call(this, props); 25 | 26 | this.onInputType = this.onInputType.bind(this); 27 | this.formattedRawValue = this.formattedRawValue.bind(this); 28 | this.getRawValue = this.getRawValue.bind(this); 29 | 30 | this.state = { 31 | rawValue: this.props.value, 32 | tabIndex: this.props.tabIndex, 33 | readOnly: this.props.readOnly 34 | }; 35 | } 36 | 37 | _createClass(SimpleCurrencyInput, [{ 38 | key: 'componentWillMount', 39 | value: function componentWillMount() { 40 | this.notifyParentWithRawValue(this.state.rawValue); 41 | } 42 | }, { 43 | key: 'componentWillReceiveProps', 44 | value: function componentWillReceiveProps(nextProps) { 45 | if (nextProps.value || nextProps.value === 0) { 46 | this.setState({ 47 | rawValue: nextProps.value 48 | }); 49 | } 50 | } 51 | }, { 52 | key: 'onInputType', 53 | value: function onInputType(event) { 54 | var input = event.target.value; 55 | var rawValue = this.getRawValue(input); 56 | if (!rawValue) { 57 | rawValue = 0; 58 | } 59 | 60 | this.notifyParentWithRawValue(rawValue); 61 | 62 | this.setState({ 63 | rawValue: rawValue 64 | }); 65 | } 66 | }, { 67 | key: 'notifyParentWithRawValue', 68 | value: function notifyParentWithRawValue(rawValue) { 69 | var display = this.formattedRawValue(rawValue); 70 | this.props.onInputChange(rawValue, display); 71 | } 72 | }, { 73 | key: 'getRawValue', 74 | value: function getRawValue(displayedValue) { 75 | var result = displayedValue; 76 | 77 | result = removeOccurrences(result, this.props.delimiter); 78 | result = removeOccurrences(result, this.props.separator); 79 | result = removeOccurrences(result, this.props.unit); 80 | 81 | var intValue = parseInt(result); 82 | 83 | return intValue; 84 | } 85 | }, { 86 | key: 'formattedRawValue', 87 | value: function formattedRawValue(rawValue) { 88 | var minChars = '0'.length + this.props.precision; 89 | 90 | var result = ''; 91 | result = '' + rawValue; 92 | 93 | if (result.length < minChars) { 94 | var leftZeroesToAdd = minChars - result.length; 95 | result = '' + repeatZeroes(leftZeroesToAdd) + result; 96 | } 97 | 98 | var beforeSeparator = result.slice(0, result.length - this.props.precision); 99 | var afterSeparator = result.slice(result.length - this.props.precision); 100 | 101 | if (beforeSeparator.length > 3) { 102 | var chars = beforeSeparator.split('').reverse(); 103 | var withDots = ''; 104 | for (var i = chars.length - 1; i >= 0; i--) { 105 | var char = chars[i]; 106 | var dot = i % 3 === 0 ? this.props.delimiter : ''; 107 | withDots = '' + withDots + char + dot; 108 | } 109 | withDots = withDots.substring(0, withDots.length - 1); 110 | beforeSeparator = withDots; 111 | } 112 | result = beforeSeparator + this.props.separator + afterSeparator; 113 | 114 | if (this.props.unit) { 115 | result = this.props.unit + ' ' + result; 116 | } 117 | 118 | return result; 119 | } 120 | }, { 121 | key: 'render', 122 | value: function render() { 123 | return React.createElement('input', { 124 | id: this.props.id, 125 | className: this.props.className, 126 | onBlur: this.props.onInputBlur, 127 | onFocus: this.props.onInputFocus, 128 | onChange: this.onInputType, 129 | value: this.formattedRawValue(this.state.rawValue), 130 | disabled: this.props.disabled, 131 | autoFocus: this.props.autoFocus, 132 | tabIndex: this.state.tabIndex, 133 | readOnly: this.state.readOnly, 134 | autoComplete: this.props.autoComplete, 135 | autoCorrect: this.props.autoCorrect, 136 | name: this.props.name, 137 | placeholder: this.props.placeholder 138 | }); 139 | } 140 | }]); 141 | 142 | return SimpleCurrencyInput; 143 | })(React.Component); 144 | 145 | var repeatZeroes = function repeatZeroes(times) { 146 | var result = ''; 147 | var i = 0; 148 | for (i = 0; i < times; i++) { 149 | result += '0'; 150 | } 151 | 152 | return result; 153 | }; 154 | 155 | var removeOccurrences = function removeOccurrences(from, toRemove) { 156 | toRemove = toRemove.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 157 | var re = new RegExp(toRemove, 'g'); 158 | return from.replace(re, ''); 159 | }; 160 | 161 | SimpleCurrencyInput.propTypes = { 162 | id: PropTypes.string, 163 | autoFocus: PropTypes.bool, 164 | delimiter: PropTypes.string, 165 | disabled: PropTypes.bool, 166 | onInputChange: PropTypes.func, 167 | onInputBlur: PropTypes.func, 168 | onInputFocus: PropTypes.func, 169 | precision: PropTypes.number, 170 | readOnly: PropTypes.bool, 171 | separator: PropTypes.string, 172 | tabIndex: PropTypes.number, 173 | unit: PropTypes.string, 174 | value: PropTypes.number.isRequired 175 | }; 176 | 177 | SimpleCurrencyInput.defaultProps = { 178 | value: 0, 179 | precision: 2, 180 | separator: '.', 181 | delimiter: ',', 182 | unit: '', 183 | disabled: false, 184 | autoFocus: false, 185 | onInputChange: function onInputChange() {}, 186 | onInputBlur: function onInputBlur() {}, 187 | onInputFocus: function onInputFocus() {} 188 | }; 189 | 190 | exports['default'] = SimpleCurrencyInput; 191 | module.exports = exports['default']; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-simple-currency", 3 | "version": "1.0.16", 4 | "description": "A react input wrapper to mask and handle currency values", 5 | "main": "lib/SimpleCurrencyInput.js", 6 | "author": "Leonardo Wistuba de França", 7 | "homepage": "https://github.com/leonardowf/react-simple-currency", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/leonardowf/react-simple-currency.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/leonardowf/react-simple-currency/issues" 14 | }, 15 | "dependencies": { 16 | "classnames": "^2.1.2", 17 | "prop-types": "^15.6.0" 18 | }, 19 | "devDependencies": { 20 | "babel-eslint": "^4.1.3", 21 | "eslint": "^1.6.0", 22 | "eslint-plugin-react": "^3.5.1", 23 | "gulp": "^3.9.0", 24 | "react": "^0.14.0", 25 | "react-component-gulp-tasks": "^0.7.6", 26 | "react-dom": "^0.14.0" 27 | }, 28 | "peerDependencies": { 29 | "react": "^0.14.0" 30 | }, 31 | "browserify-shim": { 32 | "react": "global:React" 33 | }, 34 | "scripts": { 35 | "build": "gulp clean && NODE_ENV=production gulp build", 36 | "examples": "gulp dev:server", 37 | "lint": "eslint ./; true", 38 | "publish:site": "NODE_ENV=production gulp publish:examples", 39 | "release": "NODE_ENV=production gulp release", 40 | "start": "gulp dev", 41 | "test": "echo \"no tests yet\" && exit 0", 42 | "watch": "gulp watch:lib" 43 | }, 44 | "keywords": [ 45 | "react", 46 | "react-component", 47 | "currency", 48 | "money", 49 | "mask", 50 | "masker", 51 | "input", 52 | "value" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /src/SimpleCurrencyInput.js: -------------------------------------------------------------------------------- 1 | var React = require('react') 2 | var PropTypes = require('prop-types') 3 | 4 | class SimpleCurrencyInput extends React.Component { 5 | constructor(props) { 6 | super(props) 7 | 8 | this.onInputType = this.onInputType.bind(this) 9 | this.formattedRawValue = this.formattedRawValue.bind(this) 10 | this.getRawValue = this.getRawValue.bind(this) 11 | 12 | this.state = { 13 | rawValue: this.props.value, 14 | tabIndex: this.props.tabIndex, 15 | readOnly: this.props.readOnly 16 | } 17 | } 18 | 19 | componentWillMount() { 20 | this.notifyParentWithRawValue(this.state.rawValue) 21 | } 22 | 23 | componentWillReceiveProps (nextProps) { 24 | if (nextProps.value || nextProps.value === 0) { 25 | this.setState({ 26 | rawValue: nextProps.value 27 | }) 28 | } 29 | } 30 | 31 | onInputType (event) { 32 | const input = event.target.value 33 | let rawValue = this.getRawValue(input) 34 | if (!rawValue) { 35 | rawValue = 0 36 | } 37 | 38 | this.notifyParentWithRawValue(rawValue) 39 | 40 | this.setState({ 41 | rawValue 42 | }) 43 | } 44 | 45 | notifyParentWithRawValue (rawValue) { 46 | let display = this.formattedRawValue(rawValue) 47 | this.props.onInputChange(rawValue, display) 48 | } 49 | 50 | getRawValue (displayedValue) { 51 | let result = displayedValue 52 | 53 | result = removeOccurrences(result, this.props.delimiter) 54 | result = removeOccurrences(result, this.props.separator) 55 | result = removeOccurrences(result, this.props.unit) 56 | 57 | let intValue = parseInt(result) 58 | 59 | return intValue 60 | } 61 | 62 | formattedRawValue (rawValue) { 63 | const minChars = '0'.length + this.props.precision 64 | 65 | let result = '' 66 | result = `${rawValue}` 67 | 68 | if (result.length < minChars) { 69 | const leftZeroesToAdd = minChars - result.length 70 | result = `${repeatZeroes(leftZeroesToAdd)}${result}` 71 | } 72 | 73 | let beforeSeparator = result.slice(0, result.length - this.props.precision) 74 | let afterSeparator = result.slice(result.length - this.props.precision) 75 | 76 | if (beforeSeparator.length > 3) { 77 | var chars = beforeSeparator.split('').reverse() 78 | let withDots = '' 79 | for (var i = chars.length -1; i >= 0; i--) { 80 | let char = chars[i] 81 | let dot = i % 3 === 0 ? this.props.delimiter : '' 82 | withDots = `${withDots}${char}${dot}` 83 | } 84 | withDots = withDots.substring(0, withDots.length - 1) 85 | beforeSeparator = withDots 86 | } 87 | result = beforeSeparator + this.props.separator + afterSeparator 88 | 89 | if (this.props.unit) { 90 | result = `${this.props.unit} ${result}` 91 | } 92 | 93 | return result 94 | } 95 | 96 | render() { 97 | return ( 98 | 114 | ) 115 | } 116 | } 117 | 118 | const repeatZeroes = (times) => { 119 | let result = '' 120 | let i = 0 121 | for (i = 0; i < times; i++) { 122 | result += '0' 123 | } 124 | 125 | return result 126 | } 127 | 128 | const removeOccurrences = (from, toRemove) => { 129 | toRemove = toRemove.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') 130 | var re = new RegExp(toRemove, 'g') 131 | return from.replace(re, '') 132 | } 133 | 134 | SimpleCurrencyInput.propTypes = { 135 | id: PropTypes.string, 136 | autoFocus: PropTypes.bool, 137 | delimiter: PropTypes.string, 138 | disabled: PropTypes.bool, 139 | onInputChange: PropTypes.func, 140 | onInputBlur: PropTypes.func, 141 | onInputFocus: PropTypes.func, 142 | precision: PropTypes.number, 143 | readOnly: PropTypes.bool, 144 | separator: PropTypes.string, 145 | tabIndex: PropTypes.number, 146 | unit: PropTypes.string, 147 | value: PropTypes.number.isRequired 148 | } 149 | 150 | SimpleCurrencyInput.defaultProps = { 151 | value: 0, 152 | precision: 2, 153 | separator: '.', 154 | delimiter: ',', 155 | unit: '', 156 | disabled: false, 157 | autoFocus: false, 158 | onInputChange: () => {}, 159 | onInputBlur: () => {}, 160 | onInputFocus: () => {} 161 | } 162 | 163 | export default SimpleCurrencyInput 164 | --------------------------------------------------------------------------------