├── .babelrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── examples
├── index.html
└── index.js
├── lib
├── react-currency-input.cjs.js
├── react-currency-input.cjs.js.map
├── react-currency-input.es.js
├── react-currency-input.es.js.map
├── react-currency-input.min.js
└── react-currency-input.min.js.map
├── package.json
├── rollup.config.js
├── src
├── index.js
├── mask.js
└── object-assign-polyfill.js
├── test
├── index.spec.js
├── mask.spec.js
└── setup.js
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": ["es2015", "react", "stage-3"] }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | npm-debug.log
4 | coverage
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | src/
3 | test/
4 | .idea/
5 | .babelrc
6 | .npmignore
7 | .gitignore
8 | examples/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | - "4"
5 | - "6"
6 | - "7"
7 | - "8"
8 | install:
9 | - yarn install
10 | script:
11 | - yarn test
12 | - yarn run build-example
13 | cache:
14 | yarn: true
15 | directories:
16 | - node_modules
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Cedric Dugas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-currency-input
2 |
3 | An ES2015 react component for currency. Supports custom decimal and thousand separators as well as precision.
4 |
5 | [](https://travis-ci.org/jsillitoe/react-currency-input)
6 |
7 | ## Changes
8 |
9 | ## v1.3.0:
10 |
11 | - Deprecated "onChange" option in favor of "onChangeEvent". This fixes the argument order to better match React's default input handling
12 | - Updated dependencies to React 15
13 | - Added parseFloat polyfill
14 | - Persist events to deal with an issue of event pooling
15 | - Other bug fixes.
16 |
17 | ## Installation
18 |
19 | ```
20 | npm install react-currency-input --save
21 | ```
22 |
23 | ## Integration
24 |
25 | You can store the value passed in to the change handler in your state.
26 |
27 | ```javascript
28 | import React from 'react'
29 | import CurrencyInput from 'react-currency-input';
30 |
31 | const MyApp = React.createClass({
32 | getInitialState(){
33 | return ({amount: "0.00"});
34 | },
35 |
36 | handleChange(event, maskedvalue, floatvalue){
37 | this.setState({amount: maskedvalue});
38 | },
39 | render() {
40 | return (
41 |
42 |
43 |
44 | );
45 | }
46 | });
47 | export default MyApp
48 | ```
49 |
50 | You can also assign a reference then access the value using a call to getMaskedValue().
51 |
52 | ```javascript
53 | import React from 'react'
54 | import CurrencyInput from 'react-currency-input';
55 |
56 | const MyApp = React.createClass({
57 | handleSubmit(event){
58 | event.preventDefault();
59 | console.log(this.refs.myinput.getMaskedValue())
60 | },
61 | render() {
62 | return (
63 |
66 | );
67 | }
68 | });
69 | export default MyApp
70 | ```
71 |
72 | ## Separators and Precision
73 |
74 | Specify custom decimal and thousand separators:
75 |
76 | ```javascript
77 | // 1.234.567,89
78 |
79 | ```
80 |
81 | Specify a specific precision:
82 |
83 | ```javascript
84 | // 123,456.789
85 |
86 | ```
87 |
88 | ```javascript
89 | // 123,456,789
90 |
91 | ```
92 |
93 | ## Currency
94 |
95 | Optionally set a currency symbol as a prefix or suffix
96 |
97 | ```javascript
98 | // $1,234,567.89
99 |
100 | ```
101 |
102 | ```javascript
103 | // 1,234,567.89 kr
104 |
105 | ```
106 |
107 | Negative signs come before the prefix
108 |
109 | ```javascript
110 | // -$20.00
111 |
112 | ```
113 |
114 | All other attributes are applied to the input element. For example, you can integrate bootstrap styling:
115 |
116 | ```javascript
117 |
118 | ```
119 |
120 | ## Options
121 |
122 | Option | Default Value | Description
123 | ----------------- | ------------- | -----------------------------------------------------------------------------
124 | value | 0 | The initial currency value
125 | onChange | n/a | Callback function to handle value changes. Deprecated, use onChangeEvent.
126 | onChangeEvent | n/a | Callback function to handle value changes
127 | precision | 2 | Number of digits after the decimal separator
128 | decimalSeparator | '.' | The decimal separator
129 | thousandSeparator | ',' | The thousand separator
130 | inputType | "text" | Input field tag type. You may want to use `number` or `tel`*
131 | allowNegative | false | Allows negative numbers in the input
132 | allowEmpty | false | If no `value` is given, defines if it starts as null (`true`) or '' (`false`)
133 | selectAllOnFocus | false | Selects all text on focus or does not
134 | prefix | '' | Currency prefix
135 | suffix | '' | Currency suffix
136 | autoFocus | false | Autofocus
137 |
138 | ***Note:** Enabling any mask-related features such as prefix, suffix or separators with an inputType="number" or "tel" could trigger errors. Most of those characters would be invalid in such input types.
139 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Currency Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 0
14 |
15 |
16 | 1
17 |
18 |
19 | 2
20 |
21 |
22 | 3
23 |
24 |
25 | 4
26 |
27 |
28 | 5
29 |
30 |
31 | 6
32 |
33 | 7
34 |
35 | Autofocus:
36 | 8
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/examples/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jrs1 on 3/7/17.
3 | */
4 |
5 | import React from 'react';
6 | import ReactDOM from 'react-dom';
7 |
8 | import CurrencyInput from '../src/index';
9 |
10 |
11 | ReactDOM.render(, document.getElementById('example0'));
12 |
13 | ReactDOM.render(, document.getElementById('example1'));
14 |
15 | ReactDOM.render(, document.getElementById('example2'));
16 |
17 | ReactDOM.render(, document.getElementById('example3'));
18 |
19 | ReactDOM.render(, document.getElementById('example4'));
20 |
21 | ReactDOM.render(, document.getElementById('example5'));
22 |
23 | ReactDOM.render(, document.getElementById('example6'));
24 |
25 | var onChangeEvent = function(event, mask, floatValue) {
26 | console.log(event)
27 | console.log(mask)
28 | console.log(floatValue)
29 | }
30 |
31 | ReactDOM.render(
32 | ,
33 | document.getElementById('example7')
34 | );
35 |
36 | ReactDOM.render(, document.getElementById('example8'));
37 |
38 |
--------------------------------------------------------------------------------
/lib/react-currency-input.cjs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
4 |
5 | var PropTypes = _interopDefault(require('prop-types'));
6 | var React = require('react');
7 | var React__default = _interopDefault(React);
8 | var ReactDOM = _interopDefault(require('react-dom'));
9 |
10 | Object.assign = Object.assign ||
11 | function(target) {
12 | var arguments$1 = arguments;
13 |
14 | for (var i = 1; i < arguments.length; i++) {
15 | var source = arguments$1[i];
16 | for (var key in source) {
17 | if (Object.prototype.hasOwnProperty.call(source, key)) {
18 | target[key] = source[key];
19 | }
20 | }
21 | }
22 | return target;
23 | };
24 |
25 | function mask(value, precision, decimalSeparator, thousandSeparator, allowNegative, prefix, suffix){
26 | if ( precision === void 0 ) precision = 2;
27 | if ( decimalSeparator === void 0 ) decimalSeparator = '.';
28 | if ( thousandSeparator === void 0 ) thousandSeparator = ',';
29 | if ( allowNegative === void 0 ) allowNegative = false;
30 | if ( prefix === void 0 ) prefix = '';
31 | if ( suffix === void 0 ) suffix = '';
32 |
33 | // provide some default values and arg validation.
34 | if (precision < 0) { precision = 0; } // precision cannot be negative
35 | if (precision > 20) { precision = 20; } // precision cannot be greater than 20
36 |
37 | if (value === null || value===undefined) {
38 | return {
39 | value: 0,
40 | maskedValue: ''
41 | };
42 | }
43 |
44 | value = String(value); //if the given value is a Number, let's convert into String to manipulate that
45 |
46 | if (value.length == 0) {
47 | return {
48 | value: 0,
49 | maskedValue: ''
50 | };
51 | }
52 |
53 |
54 | // extract digits. if no digits, fill in a zero.
55 | var digits = value.match(/\d/g) || ['0'];
56 |
57 | var numberIsNegative = false;
58 | if (allowNegative) {
59 | var negativeSignCount = (value.match(/-/g) || []).length;
60 | // number will be negative if we have an odd number of "-"
61 | // ideally, we should only ever have 0, 1 or 2 (positive number, making a number negative
62 | // and making a negative number positive, respectively)
63 | numberIsNegative = negativeSignCount % 2 === 1;
64 |
65 | // if every digit in the array is '0', then the number should never be negative
66 | var allDigitsAreZero = true;
67 | for (var idx=0; idx < digits.length; idx += 1) {
68 | if(digits[idx] !== '0') {
69 | allDigitsAreZero = false;
70 | break;
71 | }
72 | }
73 | if (allDigitsAreZero) {
74 | numberIsNegative = false;
75 | }
76 | }
77 |
78 | // zero-pad a input
79 | while (digits.length <= precision) { digits.unshift('0'); }
80 |
81 | if (precision > 0) {
82 | // add the decimal separator
83 | digits.splice(digits.length - precision, 0, ".");
84 | }
85 |
86 | // clean up extraneous digits like leading zeros.
87 | digits = Number(digits.join('')).toFixed(precision).split('');
88 | var raw = Number(digits.join(''));
89 |
90 | var decimalpos = digits.length - precision - 1; // -1 needed to position the decimal separator before the digits.
91 | if (precision > 0) {
92 | // set the final decimal separator
93 | digits[decimalpos] = decimalSeparator;
94 | } else {
95 | // when precision is 0, there is no decimal separator.
96 | decimalpos = digits.length;
97 | }
98 |
99 | // add in any thousand separators
100 | for (var x=decimalpos - 3; x > 0; x = x - 3) {
101 | digits.splice(x, 0, thousandSeparator);
102 | }
103 |
104 | // if we have a prefix or suffix, add them in.
105 | if (prefix.length > 0) { digits.unshift(prefix); }
106 | if (suffix.length > 0) { digits.push(suffix); }
107 |
108 | // if the number is negative, insert a "-" to
109 | // the front of the array and negate the raw value
110 | if (allowNegative && numberIsNegative) {
111 | digits.unshift('-');
112 | raw = -raw;
113 | }
114 |
115 | return {
116 | value: raw,
117 | maskedValue: digits.join('').trim()
118 | };
119 | }
120 |
121 | // IE* parseFloat polyfill
122 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#Polyfill
123 | Number.parseFloat = parseFloat;
124 |
125 | var CurrencyInput = (function (Component$$1) {
126 | function CurrencyInput(props) {
127 | Component$$1.call(this, props);
128 | this.prepareProps = this.prepareProps.bind(this);
129 | this.handleChange = this.handleChange.bind(this);
130 | this.handleFocus = this.handleFocus.bind(this);
131 | this.state = this.prepareProps(this.props);
132 |
133 | this.inputSelectionStart = 1;
134 | this.inputSelectionEnd = 1;
135 | }
136 |
137 | if ( Component$$1 ) CurrencyInput.__proto__ = Component$$1;
138 | CurrencyInput.prototype = Object.create( Component$$1 && Component$$1.prototype );
139 | CurrencyInput.prototype.constructor = CurrencyInput;
140 |
141 |
142 | /**
143 | * Exposes the current masked value.
144 | *
145 | * @returns {String}
146 | */
147 | CurrencyInput.prototype.getMaskedValue = function getMaskedValue () {
148 | return this.state.maskedValue;
149 | };
150 |
151 |
152 | /**
153 | * General function used to cleanup and define the final props used for rendering
154 | * @returns {{ maskedValue: {String}, value: {Number}, customProps: {Object} }}
155 | */
156 | CurrencyInput.prototype.prepareProps = function prepareProps (props) {
157 | var customProps = Object.assign({}, props); // babeljs converts to Object.assign, then polyfills.
158 | delete customProps.onChange;
159 | delete customProps.onChangeEvent;
160 | delete customProps.value;
161 | delete customProps.decimalSeparator;
162 | delete customProps.thousandSeparator;
163 | delete customProps.precision;
164 | delete customProps.inputType;
165 | delete customProps.allowNegative;
166 | delete customProps.allowEmpty;
167 | delete customProps.prefix;
168 | delete customProps.suffix;
169 | delete customProps.selectAllOnFocus;
170 | delete customProps.autoFocus;
171 |
172 | var initialValue = props.value;
173 | if (initialValue === null) {
174 | initialValue = props.allowEmpty? null : '';
175 | }else{
176 |
177 | if (typeof initialValue == 'string') {
178 | // Some people, when confronted with a problem, think "I know, I'll use regular expressions."
179 | // Now they have two problems.
180 |
181 | // Strip out thousand separators, prefix, and suffix, etc.
182 | if (props.thousandSeparator === "."){
183 | // special handle the . thousand separator
184 | initialValue = initialValue.replace(/\./g, '');
185 | }
186 |
187 | if (props.decimalSeparator != "."){
188 | // fix the decimal separator
189 | initialValue = initialValue.replace(new RegExp(props.decimalSeparator, 'g'), '.');
190 | }
191 |
192 | //Strip out anything that is not a digit, -, or decimal separator
193 | initialValue = initialValue.replace(/[^0-9-.]/g, '');
194 |
195 | // now we can parse.
196 | initialValue = Number.parseFloat(initialValue);
197 | }
198 | initialValue = Number(initialValue).toLocaleString(undefined, {
199 | style : 'decimal',
200 | minimumFractionDigits: props.precision,
201 | maximumFractionDigits: props.precision
202 | });
203 |
204 | }
205 |
206 | var ref = mask(
207 | initialValue,
208 | props.precision,
209 | props.decimalSeparator,
210 | props.thousandSeparator,
211 | props.allowNegative,
212 | props.prefix,
213 | props.suffix
214 | );
215 | var maskedValue = ref.maskedValue;
216 | var value = ref.value;
217 |
218 | return { maskedValue: maskedValue, value: value, customProps: customProps };
219 | };
220 |
221 |
222 | /**
223 | * Component lifecycle function.
224 | * Invoked when a component is receiving new props. This method is not called for the initial render.
225 | *
226 | * @param nextProps
227 | * @see https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops
228 | */
229 | CurrencyInput.prototype.componentWillReceiveProps = function componentWillReceiveProps (nextProps) {
230 | this.setState(this.prepareProps(nextProps));
231 | };
232 |
233 |
234 | /**
235 | * Component lifecycle function.
236 | * @returns {XML}
237 | * @see https://facebook.github.io/react/docs/react-component.html#componentdidmount
238 | */
239 | CurrencyInput.prototype.componentDidMount = function componentDidMount (){
240 | var node = ReactDOM.findDOMNode(this.theInput);
241 | var selectionStart, selectionEnd;
242 |
243 | if (this.props.autoFocus) {
244 | this.theInput.focus();
245 | selectionEnd = this.state.maskedValue.length - this.props.suffix.length;
246 | selectionStart = selectionEnd;
247 | } else {
248 | selectionEnd = Math.min(node.selectionEnd, this.theInput.value.length - this.props.suffix.length);
249 | selectionStart = Math.min(node.selectionStart, selectionEnd);
250 | }
251 |
252 | node.setSelectionRange(selectionStart, selectionEnd);
253 | };
254 |
255 |
256 | /**
257 | * Component lifecycle function
258 | * @returns {XML}
259 | * @see https://facebook.github.io/react/docs/react-component.html#componentwillupdate
260 | */
261 | CurrencyInput.prototype.componentWillUpdate = function componentWillUpdate () {
262 | var node = ReactDOM.findDOMNode(this.theInput);
263 | this.inputSelectionStart = node.selectionStart;
264 | this.inputSelectionEnd = node.selectionEnd;
265 | };
266 |
267 |
268 | /**
269 | * Component lifecycle function.
270 | * @returns {XML}
271 | * @see https://facebook.github.io/react/docs/react-component.html#componentdidupdate
272 | */
273 | CurrencyInput.prototype.componentDidUpdate = function componentDidUpdate (prevProps, prevState){
274 | var ref = this.props;
275 | var decimalSeparator = ref.decimalSeparator;
276 | var node = ReactDOM.findDOMNode(this.theInput);
277 | var isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;
278 | var minPos = this.props.prefix.length + (isNegative ? 1 : 0);
279 | var selectionEnd = Math.max(minPos, Math.min(this.inputSelectionEnd, this.theInput.value.length - this.props.suffix.length));
280 | var selectionStart = Math.max(minPos, Math.min(this.inputSelectionEnd, selectionEnd));
281 |
282 | var regexEscapeRegex = /[-[\]{}()*+?.,\\^$|#\s]/g;
283 | var separatorsRegex = new RegExp(decimalSeparator.replace(regexEscapeRegex, '\\$&') + '|' + this.props.thousandSeparator.replace(regexEscapeRegex, '\\$&'), 'g');
284 | var currSeparatorCount = (this.state.maskedValue.match(separatorsRegex) || []).length;
285 | var prevSeparatorCount = (prevState.maskedValue.match(separatorsRegex) || []).length;
286 | var adjustment = Math.max(currSeparatorCount - prevSeparatorCount, 0);
287 |
288 | selectionEnd = selectionEnd + adjustment;
289 | selectionStart = selectionStart + adjustment;
290 |
291 | var precision = Number(this.props.precision);
292 |
293 | var baselength = this.props.suffix.length
294 | + this.props.prefix.length
295 | + (precision > 0 ? decimalSeparator.length : 0) // if precision is 0 there will be no decimal part
296 | + precision
297 | + 1; // This is to account for the default '0' value that comes before the decimal separator
298 |
299 | if (this.state.maskedValue.length == baselength){
300 | // if we are already at base length, position the cursor at the end.
301 | selectionEnd = this.theInput.value.length - this.props.suffix.length;
302 | selectionStart = selectionEnd;
303 | }
304 |
305 | node.setSelectionRange(selectionStart, selectionEnd);
306 | this.inputSelectionStart = selectionStart;
307 | this.inputSelectionEnd = selectionEnd;
308 | };
309 |
310 |
311 | /**
312 | * onChange Event Handler
313 | * @param event
314 | */
315 | CurrencyInput.prototype.handleChange = function handleChange (event) {
316 | var this$1 = this;
317 |
318 | event.preventDefault();
319 | var ref = mask(
320 | event.target.value,
321 | this.props.precision,
322 | this.props.decimalSeparator,
323 | this.props.thousandSeparator,
324 | this.props.allowNegative,
325 | this.props.prefix,
326 | this.props.suffix
327 | );
328 | var maskedValue = ref.maskedValue;
329 | var value = ref.value;
330 |
331 | event.persist(); // fixes issue #23
332 |
333 | this.setState({ maskedValue: maskedValue, value: value }, function () {
334 | this$1.props.onChange(maskedValue, value, event);
335 | this$1.props.onChangeEvent(event, maskedValue, value);
336 | });
337 | };
338 |
339 |
340 | /**
341 | * onFocus Event Handler
342 | * @param event
343 | */
344 | CurrencyInput.prototype.handleFocus = function handleFocus (event) {
345 | if (!this.theInput) { return; }
346 |
347 | //Whenever we receive focus check to see if the position is before the suffix, if not, move it.
348 | var selectionEnd = this.theInput.value.length - this.props.suffix.length;
349 | var isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;
350 | var selectionStart = this.props.prefix.length + (isNegative ? 1 : 0);
351 | this.props.selectAllOnFocus && event.target.setSelectionRange(selectionStart, selectionEnd);
352 | this.inputSelectionStart = selectionStart;
353 | this.inputSelectionEnd = selectionEnd;
354 | };
355 |
356 |
357 | CurrencyInput.prototype.handleBlur = function handleBlur (event) {
358 | this.inputSelectionStart = 0;
359 | this.inputSelectionEnd = 0;
360 | };
361 |
362 |
363 | /**
364 | * Component lifecycle function.
365 | * @returns {XML}
366 | * @see https://facebook.github.io/react/docs/component-specs.html#render
367 | */
368 | CurrencyInput.prototype.render = function render () {
369 | var this$1 = this;
370 |
371 | return (
372 | React__default.createElement( 'input', Object.assign({},
373 | { ref: function (input) { this$1.theInput = input; }, type: this.props.inputType, value: this.state.maskedValue, onChange: this.handleChange, onFocus: this.handleFocus, onMouseUp: this.handleFocus }, this.state.customProps))
374 | )
375 | };
376 |
377 | return CurrencyInput;
378 | }(React.Component));
379 |
380 |
381 |
382 | /**
383 | * Prop validation.
384 | * @see https://facebook.github.io/react/docs/component-specs.html#proptypes
385 | */
386 |
387 | CurrencyInput.propTypes = {
388 | onChange: PropTypes.func,
389 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
390 | decimalSeparator: PropTypes.string,
391 | thousandSeparator: PropTypes.string,
392 | precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
393 | inputType: PropTypes.string,
394 | allowNegative: PropTypes.bool,
395 | allowEmpty: PropTypes.bool,
396 | prefix: PropTypes.string,
397 | suffix: PropTypes.string,
398 | selectAllOnFocus: PropTypes.bool
399 | };
400 |
401 |
402 | CurrencyInput.defaultProps = {
403 | onChange: function(maskValue, value, event) {/*no-op*/},
404 | onChangeEvent: function(event, maskValue, value) {/*no-op*/},
405 | autoFocus: false,
406 | value: '0',
407 | decimalSeparator: '.',
408 | thousandSeparator: ',',
409 | precision: '2',
410 | inputType: 'text',
411 | allowNegative: false,
412 | prefix: '',
413 | suffix: '',
414 | selectAllOnFocus: false
415 | };
416 |
417 | module.exports = CurrencyInput;
418 | //# sourceMappingURL=react-currency-input.cjs.js.map
419 |
--------------------------------------------------------------------------------
/lib/react-currency-input.cjs.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"react-currency-input.cjs.js","sources":["../src/object-assign-polyfill.js","../src/mask.js","../src/index.js"],"sourcesContent":["Object.assign = Object.assign ||\n function(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n","\nexport default function mask(value, precision = 2, decimalSeparator = '.', thousandSeparator = ',', allowNegative = false, prefix = '', suffix = ''){\n // provide some default values and arg validation.\n if (precision < 0) { precision = 0; } // precision cannot be negative\n if (precision > 20) { precision = 20; } // precision cannot be greater than 20\n \n if (value === null || value===undefined) {\n return {\n value: 0,\n maskedValue: ''\n };\n }\n \n value = String(value); //if the given value is a Number, let's convert into String to manipulate that\n\n if (value.length == 0) {\n return {\n value: 0,\n maskedValue: ''\n };\n }\n\n\n // extract digits. if no digits, fill in a zero.\n let digits = value.match(/\\d/g) || ['0'];\n \n let numberIsNegative = false;\n if (allowNegative) {\n let negativeSignCount = (value.match(/-/g) || []).length;\n // number will be negative if we have an odd number of \"-\"\n // ideally, we should only ever have 0, 1 or 2 (positive number, making a number negative\n // and making a negative number positive, respectively)\n numberIsNegative = negativeSignCount % 2 === 1;\n \n // if every digit in the array is '0', then the number should never be negative\n let allDigitsAreZero = true;\n for (let idx=0; idx < digits.length; idx += 1) {\n if(digits[idx] !== '0') {\n allDigitsAreZero = false;\n break;\n }\n }\n if (allDigitsAreZero) {\n numberIsNegative = false;\n }\n }\n\n // zero-pad a input\n while (digits.length <= precision) { digits.unshift('0'); }\n\n if (precision > 0) {\n // add the decimal separator\n digits.splice(digits.length - precision, 0, \".\");\n }\n\n // clean up extraneous digits like leading zeros.\n digits = Number(digits.join('')).toFixed(precision).split('');\n let raw = Number(digits.join(''));\n\n let decimalpos = digits.length - precision - 1; // -1 needed to position the decimal separator before the digits.\n if (precision > 0) {\n // set the final decimal separator\n digits[decimalpos] = decimalSeparator;\n } else {\n // when precision is 0, there is no decimal separator.\n decimalpos = digits.length;\n }\n\n // add in any thousand separators\n for (let x=decimalpos - 3; x > 0; x = x - 3) {\n digits.splice(x, 0, thousandSeparator);\n }\n\n // if we have a prefix or suffix, add them in.\n if (prefix.length > 0) { digits.unshift(prefix); }\n if (suffix.length > 0) { digits.push(suffix); }\n\n // if the number is negative, insert a \"-\" to\n // the front of the array and negate the raw value\n if (allowNegative && numberIsNegative) {\n digits.unshift('-');\n raw = -raw;\n }\n\n return {\n value: raw,\n maskedValue: digits.join('').trim()\n };\n}\n","import './object-assign-polyfill';\n\nimport PropTypes from 'prop-types';\nimport React, { Component } from 'react'\nimport ReactDOM from 'react-dom'\nimport mask from './mask.js'\n\n// IE* parseFloat polyfill\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#Polyfill\nNumber.parseFloat = parseFloat;\n\nclass CurrencyInput extends Component {\n constructor(props) {\n super(props);\n this.prepareProps = this.prepareProps.bind(this);\n this.handleChange = this.handleChange.bind(this);\n this.handleFocus = this.handleFocus.bind(this);\n this.state = this.prepareProps(this.props);\n\n this.inputSelectionStart = 1;\n this.inputSelectionEnd = 1;\n }\n\n\n /**\n * Exposes the current masked value.\n *\n * @returns {String}\n */\n getMaskedValue() {\n return this.state.maskedValue;\n }\n\n\n /**\n * General function used to cleanup and define the final props used for rendering\n * @returns {{ maskedValue: {String}, value: {Number}, customProps: {Object} }}\n */\n prepareProps(props) {\n let customProps = {...props}; // babeljs converts to Object.assign, then polyfills.\n delete customProps.onChange;\n delete customProps.onChangeEvent;\n delete customProps.value;\n delete customProps.decimalSeparator;\n delete customProps.thousandSeparator;\n delete customProps.precision;\n delete customProps.inputType;\n delete customProps.allowNegative;\n delete customProps.allowEmpty;\n delete customProps.prefix;\n delete customProps.suffix;\n delete customProps.selectAllOnFocus;\n delete customProps.autoFocus;\n\n let initialValue = props.value;\n if (initialValue === null) {\n initialValue = props.allowEmpty? null : '';\n }else{\n\n if (typeof initialValue == 'string') {\n // Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\"\n // Now they have two problems.\n\n // Strip out thousand separators, prefix, and suffix, etc.\n if (props.thousandSeparator === \".\"){\n // special handle the . thousand separator\n initialValue = initialValue.replace(/\\./g, '');\n }\n\n if (props.decimalSeparator != \".\"){\n // fix the decimal separator\n initialValue = initialValue.replace(new RegExp(props.decimalSeparator, 'g'), '.');\n }\n\n //Strip out anything that is not a digit, -, or decimal separator\n initialValue = initialValue.replace(/[^0-9-.]/g, '');\n\n // now we can parse.\n initialValue = Number.parseFloat(initialValue);\n }\n initialValue = Number(initialValue).toLocaleString(undefined, {\n style : 'decimal',\n minimumFractionDigits: props.precision,\n maximumFractionDigits: props.precision\n })\n\n }\n\n const { maskedValue, value } = mask(\n initialValue,\n props.precision,\n props.decimalSeparator,\n props.thousandSeparator,\n props.allowNegative,\n props.prefix,\n props.suffix\n );\n\n return { maskedValue, value, customProps };\n }\n\n\n /**\n * Component lifecycle function.\n * Invoked when a component is receiving new props. This method is not called for the initial render.\n *\n * @param nextProps\n * @see https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops\n */\n componentWillReceiveProps(nextProps) {\n this.setState(this.prepareProps(nextProps));\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentdidmount\n */\n componentDidMount(){\n let node = ReactDOM.findDOMNode(this.theInput);\n let selectionStart, selectionEnd;\n\n if (this.props.autoFocus) {\n this.theInput.focus();\n selectionEnd = this.state.maskedValue.length - this.props.suffix.length;\n selectionStart = selectionEnd;\n } else {\n selectionEnd = Math.min(node.selectionEnd, this.theInput.value.length - this.props.suffix.length);\n selectionStart = Math.min(node.selectionStart, selectionEnd);\n }\n\n node.setSelectionRange(selectionStart, selectionEnd);\n }\n\n\n /**\n * Component lifecycle function\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentwillupdate\n */\n componentWillUpdate() {\n let node = ReactDOM.findDOMNode(this.theInput);\n this.inputSelectionStart = node.selectionStart;\n this.inputSelectionEnd = node.selectionEnd;\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentdidupdate\n */\n componentDidUpdate(prevProps, prevState){\n const { decimalSeparator } = this.props;\n let node = ReactDOM.findDOMNode(this.theInput);\n let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;\n let minPos = this.props.prefix.length + (isNegative ? 1 : 0);\n let selectionEnd = Math.max(minPos, Math.min(this.inputSelectionEnd, this.theInput.value.length - this.props.suffix.length));\n let selectionStart = Math.max(minPos, Math.min(this.inputSelectionEnd, selectionEnd));\n\n let regexEscapeRegex = /[-[\\]{}()*+?.,\\\\^$|#\\s]/g;\n let separatorsRegex = new RegExp(decimalSeparator.replace(regexEscapeRegex, '\\\\$&') + '|' + this.props.thousandSeparator.replace(regexEscapeRegex, '\\\\$&'), 'g');\n let currSeparatorCount = (this.state.maskedValue.match(separatorsRegex) || []).length;\n let prevSeparatorCount = (prevState.maskedValue.match(separatorsRegex) || []).length;\n let adjustment = Math.max(currSeparatorCount - prevSeparatorCount, 0);\n\n selectionEnd = selectionEnd + adjustment;\n selectionStart = selectionStart + adjustment;\n\n const precision = Number(this.props.precision);\n\n let baselength = this.props.suffix.length\n + this.props.prefix.length\n + (precision > 0 ? decimalSeparator.length : 0) // if precision is 0 there will be no decimal part\n + precision\n + 1; // This is to account for the default '0' value that comes before the decimal separator\n\n if (this.state.maskedValue.length == baselength){\n // if we are already at base length, position the cursor at the end.\n selectionEnd = this.theInput.value.length - this.props.suffix.length;\n selectionStart = selectionEnd;\n }\n\n node.setSelectionRange(selectionStart, selectionEnd);\n this.inputSelectionStart = selectionStart;\n this.inputSelectionEnd = selectionEnd;\n }\n\n\n /**\n * onChange Event Handler\n * @param event\n */\n handleChange(event) {\n event.preventDefault();\n let { maskedValue, value } = mask(\n event.target.value,\n this.props.precision,\n this.props.decimalSeparator,\n this.props.thousandSeparator,\n this.props.allowNegative,\n this.props.prefix,\n this.props.suffix\n );\n\n event.persist(); // fixes issue #23\n\n this.setState({ maskedValue, value }, () => {\n this.props.onChange(maskedValue, value, event);\n this.props.onChangeEvent(event, maskedValue, value);\n });\n }\n\n\n /**\n * onFocus Event Handler\n * @param event\n */\n handleFocus(event) {\n if (!this.theInput) return;\n\n //Whenever we receive focus check to see if the position is before the suffix, if not, move it.\n let selectionEnd = this.theInput.value.length - this.props.suffix.length;\n let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;\n let selectionStart = this.props.prefix.length + (isNegative ? 1 : 0);\n this.props.selectAllOnFocus && event.target.setSelectionRange(selectionStart, selectionEnd);\n this.inputSelectionStart = selectionStart;\n this.inputSelectionEnd = selectionEnd;\n }\n\n\n handleBlur(event) {\n this.inputSelectionStart = 0;\n this.inputSelectionEnd = 0;\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/component-specs.html#render\n */\n render() {\n return (\n { this.theInput = input; }}\n type={this.props.inputType}\n value={this.state.maskedValue}\n onChange={this.handleChange}\n onFocus={this.handleFocus}\n onMouseUp={this.handleFocus}\n {...this.state.customProps}\n />\n )\n }\n}\n\n\n\n/**\n * Prop validation.\n * @see https://facebook.github.io/react/docs/component-specs.html#proptypes\n */\n\nCurrencyInput.propTypes = {\n onChange: PropTypes.func,\n value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n decimalSeparator: PropTypes.string,\n thousandSeparator: PropTypes.string,\n precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n inputType: PropTypes.string,\n allowNegative: PropTypes.bool,\n allowEmpty: PropTypes.bool,\n prefix: PropTypes.string,\n suffix: PropTypes.string,\n selectAllOnFocus: PropTypes.bool\n};\n\n\nCurrencyInput.defaultProps = {\n onChange: function(maskValue, value, event) {/*no-op*/},\n onChangeEvent: function(event, maskValue, value) {/*no-op*/},\n autoFocus: false,\n value: '0',\n decimalSeparator: '.',\n thousandSeparator: ',',\n precision: '2',\n inputType: 'text',\n allowNegative: false,\n prefix: '',\n suffix: '',\n selectAllOnFocus: false\n};\n\n\nexport default CurrencyInput\n"],"names":["arguments","let","super","const","this","React","Component"],"mappings":";;;;;;;;;AAAA,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;EAC3B,SAAS,MAAM,EAAE;;;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;MACzC,IAAI,MAAM,GAAGA,WAAS,CAAC,CAAC,CAAC,CAAC;MAC1B,KAAK,IAAI,GAAG,IAAI,MAAM,EAAE;QACtB,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;UACrD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;SAC3B;OACF;KACF;IACD,OAAO,MAAM,CAAC;GACf,CAAC;;ACVW,SAAS,IAAI,CAAC,KAAK,EAAE,SAAa,EAAE,gBAAsB,EAAE,iBAAuB,EAAE,aAAqB,EAAE,MAAW,EAAE,MAAW,CAAC;yCAAvG,GAAG,CAAC,CAAkB;uDAAA,GAAG,GAAG,CAAmB;yDAAA,GAAG,GAAG,CAAe;iDAAA,GAAG,KAAK,CAAQ;mCAAA,GAAG,EAAE,CAAQ;mCAAA,GAAG,EAAE;;;IAE/I,IAAI,SAAS,GAAG,CAAC,EAAE,EAAE,SAAS,GAAG,CAAC,CAAC,EAAE;IACrC,IAAI,SAAS,GAAG,EAAE,EAAE,EAAE,SAAS,GAAG,EAAE,CAAC,EAAE;;IAEvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,SAAS,EAAE;UACnC,OAAO;cACH,KAAK,EAAE,CAAC;cACR,WAAW,EAAE,EAAE;WAClB,CAAC;MACN;;IAEF,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;;IAEtB,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE;QACnB,OAAO;YACH,KAAK,EAAE,CAAC;YACR,WAAW,EAAE,EAAE;SAClB,CAAC;KACL;;;;IAIDC,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;IAEzCA,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,aAAa,EAAE;QACfA,IAAI,iBAAiB,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC;;;;QAIzD,gBAAgB,GAAG,iBAAiB,GAAG,CAAC,KAAK,CAAC,CAAC;;;QAG/CA,IAAI,gBAAgB,GAAG,IAAI,CAAC;QAC5B,KAAKA,IAAI,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE;YAC3C,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE;gBACpB,gBAAgB,GAAG,KAAK,CAAC;gBACzB,MAAM;aACT;SACJ;QACD,IAAI,gBAAgB,EAAE;YAClB,gBAAgB,GAAG,KAAK,CAAC;SAC5B;KACJ;;;IAGD,OAAO,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE;;IAE3D,IAAI,SAAS,GAAG,CAAC,EAAE;;QAEf,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;KACpD;;;IAGD,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9DA,IAAI,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;;IAElCA,IAAI,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,SAAS,GAAG,CAAC,CAAC;IAC/C,IAAI,SAAS,GAAG,CAAC,EAAE;;QAEf,MAAM,CAAC,UAAU,CAAC,GAAG,gBAAgB,CAAC;KACzC,MAAM;;QAEH,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;KAC9B;;;IAGD,KAAKA,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,CAAC;KAC1C;;;IAGD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE;IAClD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE;;;;IAI/C,IAAI,aAAa,IAAI,gBAAgB,EAAE;QACnC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpB,GAAG,GAAG,CAAC,GAAG,CAAC;KACd;;IAED,OAAO;QACH,KAAK,EAAE,GAAG;QACV,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE;KACtC,CAAC;CACL;;;;AC/ED,MAAM,CAAC,UAAU,GAAG,UAAU,CAAC;;AAE/B,IAAM,aAAa;IAAmB,sBACvB,CAAC,KAAK,EAAE;QACfC,YAAK,KAAA,CAAC,MAAA,KAAK,CAAC,CAAC;QACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;QAE3C,IAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;KAC9B;;;;wDAAA;;;;;;;;IAQD,wBAAA,cAAc,8BAAG;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;KACjC,CAAA;;;;;;;IAOD,wBAAA,YAAY,0BAAC,KAAK,EAAE;QAChBD,IAAI,WAAW,GAAG,kBAAC,KAAQ,CAAC,CAAC;QAC7B,OAAO,WAAW,CAAC,QAAQ,CAAC;QAC5B,OAAO,WAAW,CAAC,aAAa,CAAC;QACjC,OAAO,WAAW,CAAC,KAAK,CAAC;QACzB,OAAO,WAAW,CAAC,gBAAgB,CAAC;QACpC,OAAO,WAAW,CAAC,iBAAiB,CAAC;QACrC,OAAO,WAAW,CAAC,SAAS,CAAC;QAC7B,OAAO,WAAW,CAAC,SAAS,CAAC;QAC7B,OAAO,WAAW,CAAC,aAAa,CAAC;QACjC,OAAO,WAAW,CAAC,UAAU,CAAC;QAC9B,OAAO,WAAW,CAAC,MAAM,CAAC;QAC1B,OAAO,WAAW,CAAC,MAAM,CAAC;QAC1B,OAAO,WAAW,CAAC,gBAAgB,CAAC;QACpC,OAAO,WAAW,CAAC,SAAS,CAAC;;QAE7BA,IAAI,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC;QAC/B,IAAI,YAAY,KAAK,IAAI,EAAE;YACvB,YAAY,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,GAAG,EAAE,CAAC;SAC9C,IAAI;;YAED,IAAI,OAAO,YAAY,IAAI,QAAQ,EAAE;;;;;gBAKjC,IAAI,KAAK,CAAC,iBAAiB,KAAK,GAAG,CAAC;;oBAEhC,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;iBAClD;;gBAED,IAAI,KAAK,CAAC,gBAAgB,IAAI,GAAG,CAAC;;oBAE9B,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;iBACrF;;;gBAGD,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;;;gBAGrD,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;aAClD;YACD,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE;gBAC1D,KAAK,kBAAkB,SAAS;gBAChC,qBAAqB,EAAE,KAAK,CAAC,SAAS;gBACtC,qBAAqB,EAAE,KAAK,CAAC,SAAS;aACzC,EAAC;;SAEL;;QAED,OAA4B,GAAG,IAAI;YAC/B,YAAY;YACZ,KAAK,CAAC,SAAS;YACf,KAAK,CAAC,gBAAgB;YACtB,KAAK,CAAC,iBAAiB;YACvB,KAAK,CAAC,aAAa;YACnB,KAAK,CAAC,MAAM;YACZ,KAAK,CAAC,MAAM;SACf;QARO,IAAA,WAAW;QAAE,IAAA,KAAK,aAApB;;QAUN,OAAO,EAAE,aAAA,WAAW,EAAE,OAAA,KAAK,EAAE,aAAA,WAAW,EAAE,CAAC;KAC9C,CAAA;;;;;;;;;;IAUD,wBAAA,yBAAyB,uCAAC,SAAS,EAAE;QACjC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC;KAC/C,CAAA;;;;;;;;IAQD,wBAAA,iBAAiB,gCAAE;QACfA,IAAI,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/CA,IAAI,cAAc,EAAE,YAAY,CAAC;;QAEjC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACtB,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YACxE,cAAc,GAAG,YAAY,CAAC;SACjC,MAAM;YACH,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClG,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;SAChE;;QAED,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;KACxD,CAAA;;;;;;;;IAQD,wBAAA,mBAAmB,mCAAG;QAClBA,IAAI,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,cAAc,CAAC;QAC/C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,YAAY,CAAC;KAC9C,CAAA;;;;;;;;IAQD,wBAAA,kBAAkB,gCAAC,SAAS,EAAE,SAAS,CAAC;QACpC,OAA0B,GAAG,IAAI,CAAC,KAAK;QAA/B,IAAA,gBAAgB,wBAAlB;QACNA,IAAI,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/CA,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1EA,IAAI,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7DA,IAAI,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7HA,IAAI,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC,CAAC;;QAEtFA,IAAI,gBAAgB,GAAG,0BAA0B,CAAC;QAClDA,IAAI,eAAe,GAAG,IAAI,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACjKA,IAAI,kBAAkB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC;QACtFA,IAAI,kBAAkB,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC;QACrFA,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,GAAG,kBAAkB,EAAE,CAAC,CAAC,CAAC;;QAEtE,YAAY,GAAG,YAAY,GAAG,UAAU,CAAC;QACzC,cAAc,GAAG,cAAc,GAAG,UAAU,CAAC;;QAE7CE,IAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;;QAE/CF,IAAI,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM;cACnC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM;eACvB,SAAS,GAAG,CAAC,GAAG,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;cAC7C,SAAS;cACT,CAAC,CAAC;;QAER,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,IAAI,UAAU,CAAC;;YAE5C,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YACrE,cAAc,GAAG,YAAY,CAAC;SACjC;;QAED,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QACrD,IAAI,CAAC,mBAAmB,GAAG,cAAc,CAAC;QAC1C,IAAI,CAAC,iBAAiB,GAAG,YAAY,CAAC;KACzC,CAAA;;;;;;;IAOD,wBAAA,YAAY,0BAAC,KAAK,EAAE;;;QAChB,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,OAA0B,GAAG,IAAI;YAC7B,KAAK,CAAC,MAAM,CAAC,KAAK;YAClB,IAAI,CAAC,KAAK,CAAC,SAAS;YACpB,IAAI,CAAC,KAAK,CAAC,gBAAgB;YAC3B,IAAI,CAAC,KAAK,CAAC,iBAAiB;YAC5B,IAAI,CAAC,KAAK,CAAC,aAAa;YACxB,IAAI,CAAC,KAAK,CAAC,MAAM;YACjB,IAAI,CAAC,KAAK,CAAC,MAAM;SACpB;QARK,IAAA,WAAW;QAAE,IAAA,KAAK,aAApB;;QAUJ,KAAK,CAAC,OAAO,EAAE,CAAC;;QAEhB,IAAI,CAAC,QAAQ,CAAC,EAAE,aAAA,WAAW,EAAE,OAAA,KAAK,EAAE,EAAE,YAAG;YACrCG,MAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;YAC/CA,MAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;SACvD,CAAC,CAAC;KACN,CAAA;;;;;;;IAOD,wBAAA,WAAW,yBAAC,KAAK,EAAE;QACf,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAA,OAAO,EAAA;;;QAG3BH,IAAI,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;QACzEA,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1EA,IAAI,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACrE,IAAI,CAAC,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QAC5F,IAAI,CAAC,mBAAmB,GAAG,cAAc,CAAC;QAC1C,IAAI,CAAC,iBAAiB,GAAG,YAAY,CAAC;KACzC,CAAA;;;IAGD,wBAAA,UAAU,wBAAC,KAAK,EAAE;QACd,IAAI,CAAC,mBAAmB,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;KAC9B,CAAA;;;;;;;;IAQD,wBAAA,MAAM,sBAAG;;;QACL;YACII,8BAAC;gBACG,EAAA,KAAI,UAAE,KAAK,EAAE,EAAKD,MAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,EAAE,EAC1C,MAAK,IAAK,CAAC,KAAK,CAAC,SAAS,EAC1B,OAAM,IAAK,CAAC,KAAK,CAAC,WAAW,EAC7B,UAAS,IAAK,CAAC,YAAY,EAC3B,SAAQ,IAAK,CAAC,WAAW,EACzB,WAAU,IAAK,CAAC,WAAW,EAAC,EAC5B,IAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAC7B;SACL;KACJ,CAAA;;;EApPuBE,eAqP3B,GAAA;;;;;;;;;AASD,aAAa,CAAC,SAAS,GAAG;IACtB,QAAQ,EAAE,SAAS,CAAC,IAAI;IACxB,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAChE,gBAAgB,EAAE,SAAS,CAAC,MAAM;IAClC,iBAAiB,EAAE,SAAS,CAAC,MAAM;IACnC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACpE,SAAS,EAAE,SAAS,CAAC,MAAM;IAC3B,aAAa,EAAE,SAAS,CAAC,IAAI;IAC7B,UAAU,EAAE,SAAS,CAAC,IAAI;IAC1B,MAAM,EAAE,SAAS,CAAC,MAAM;IACxB,MAAM,EAAE,SAAS,CAAC,MAAM;IACxB,gBAAgB,EAAE,SAAS,CAAC,IAAI;CACnC,CAAC;;;AAGF,aAAa,CAAC,YAAY,GAAG;IACzB,QAAQ,EAAE,SAAS,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW;IACvD,aAAa,EAAE,SAAS,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW;IAC5D,SAAS,EAAE,KAAK;IAChB,KAAK,EAAE,GAAG;IACV,gBAAgB,EAAE,GAAG;IACrB,iBAAiB,EAAE,GAAG;IACtB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,MAAM;IACjB,aAAa,EAAE,KAAK;IACpB,MAAM,EAAE,EAAE;IACV,MAAM,EAAE,EAAE;IACV,gBAAgB,EAAE,KAAK;CAC1B,CAAC;;;;"}
--------------------------------------------------------------------------------
/lib/react-currency-input.es.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Component } from 'react';
3 | import ReactDOM from 'react-dom';
4 |
5 | Object.assign = Object.assign ||
6 | function(target) {
7 | var arguments$1 = arguments;
8 |
9 | for (var i = 1; i < arguments.length; i++) {
10 | var source = arguments$1[i];
11 | for (var key in source) {
12 | if (Object.prototype.hasOwnProperty.call(source, key)) {
13 | target[key] = source[key];
14 | }
15 | }
16 | }
17 | return target;
18 | };
19 |
20 | function mask(value, precision, decimalSeparator, thousandSeparator, allowNegative, prefix, suffix){
21 | if ( precision === void 0 ) precision = 2;
22 | if ( decimalSeparator === void 0 ) decimalSeparator = '.';
23 | if ( thousandSeparator === void 0 ) thousandSeparator = ',';
24 | if ( allowNegative === void 0 ) allowNegative = false;
25 | if ( prefix === void 0 ) prefix = '';
26 | if ( suffix === void 0 ) suffix = '';
27 |
28 | // provide some default values and arg validation.
29 | if (precision < 0) { precision = 0; } // precision cannot be negative
30 | if (precision > 20) { precision = 20; } // precision cannot be greater than 20
31 |
32 | if (value === null || value===undefined) {
33 | return {
34 | value: 0,
35 | maskedValue: ''
36 | };
37 | }
38 |
39 | value = String(value); //if the given value is a Number, let's convert into String to manipulate that
40 |
41 | if (value.length == 0) {
42 | return {
43 | value: 0,
44 | maskedValue: ''
45 | };
46 | }
47 |
48 |
49 | // extract digits. if no digits, fill in a zero.
50 | var digits = value.match(/\d/g) || ['0'];
51 |
52 | var numberIsNegative = false;
53 | if (allowNegative) {
54 | var negativeSignCount = (value.match(/-/g) || []).length;
55 | // number will be negative if we have an odd number of "-"
56 | // ideally, we should only ever have 0, 1 or 2 (positive number, making a number negative
57 | // and making a negative number positive, respectively)
58 | numberIsNegative = negativeSignCount % 2 === 1;
59 |
60 | // if every digit in the array is '0', then the number should never be negative
61 | var allDigitsAreZero = true;
62 | for (var idx=0; idx < digits.length; idx += 1) {
63 | if(digits[idx] !== '0') {
64 | allDigitsAreZero = false;
65 | break;
66 | }
67 | }
68 | if (allDigitsAreZero) {
69 | numberIsNegative = false;
70 | }
71 | }
72 |
73 | // zero-pad a input
74 | while (digits.length <= precision) { digits.unshift('0'); }
75 |
76 | if (precision > 0) {
77 | // add the decimal separator
78 | digits.splice(digits.length - precision, 0, ".");
79 | }
80 |
81 | // clean up extraneous digits like leading zeros.
82 | digits = Number(digits.join('')).toFixed(precision).split('');
83 | var raw = Number(digits.join(''));
84 |
85 | var decimalpos = digits.length - precision - 1; // -1 needed to position the decimal separator before the digits.
86 | if (precision > 0) {
87 | // set the final decimal separator
88 | digits[decimalpos] = decimalSeparator;
89 | } else {
90 | // when precision is 0, there is no decimal separator.
91 | decimalpos = digits.length;
92 | }
93 |
94 | // add in any thousand separators
95 | for (var x=decimalpos - 3; x > 0; x = x - 3) {
96 | digits.splice(x, 0, thousandSeparator);
97 | }
98 |
99 | // if we have a prefix or suffix, add them in.
100 | if (prefix.length > 0) { digits.unshift(prefix); }
101 | if (suffix.length > 0) { digits.push(suffix); }
102 |
103 | // if the number is negative, insert a "-" to
104 | // the front of the array and negate the raw value
105 | if (allowNegative && numberIsNegative) {
106 | digits.unshift('-');
107 | raw = -raw;
108 | }
109 |
110 | return {
111 | value: raw,
112 | maskedValue: digits.join('').trim()
113 | };
114 | }
115 |
116 | // IE* parseFloat polyfill
117 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#Polyfill
118 | Number.parseFloat = parseFloat;
119 |
120 | var CurrencyInput = (function (Component$$1) {
121 | function CurrencyInput(props) {
122 | Component$$1.call(this, props);
123 | this.prepareProps = this.prepareProps.bind(this);
124 | this.handleChange = this.handleChange.bind(this);
125 | this.handleFocus = this.handleFocus.bind(this);
126 | this.state = this.prepareProps(this.props);
127 |
128 | this.inputSelectionStart = 1;
129 | this.inputSelectionEnd = 1;
130 | }
131 |
132 | if ( Component$$1 ) CurrencyInput.__proto__ = Component$$1;
133 | CurrencyInput.prototype = Object.create( Component$$1 && Component$$1.prototype );
134 | CurrencyInput.prototype.constructor = CurrencyInput;
135 |
136 |
137 | /**
138 | * Exposes the current masked value.
139 | *
140 | * @returns {String}
141 | */
142 | CurrencyInput.prototype.getMaskedValue = function getMaskedValue () {
143 | return this.state.maskedValue;
144 | };
145 |
146 |
147 | /**
148 | * General function used to cleanup and define the final props used for rendering
149 | * @returns {{ maskedValue: {String}, value: {Number}, customProps: {Object} }}
150 | */
151 | CurrencyInput.prototype.prepareProps = function prepareProps (props) {
152 | var customProps = Object.assign({}, props); // babeljs converts to Object.assign, then polyfills.
153 | delete customProps.onChange;
154 | delete customProps.onChangeEvent;
155 | delete customProps.value;
156 | delete customProps.decimalSeparator;
157 | delete customProps.thousandSeparator;
158 | delete customProps.precision;
159 | delete customProps.inputType;
160 | delete customProps.allowNegative;
161 | delete customProps.allowEmpty;
162 | delete customProps.prefix;
163 | delete customProps.suffix;
164 | delete customProps.selectAllOnFocus;
165 | delete customProps.autoFocus;
166 |
167 | var initialValue = props.value;
168 | if (initialValue === null) {
169 | initialValue = props.allowEmpty? null : '';
170 | }else{
171 |
172 | if (typeof initialValue == 'string') {
173 | // Some people, when confronted with a problem, think "I know, I'll use regular expressions."
174 | // Now they have two problems.
175 |
176 | // Strip out thousand separators, prefix, and suffix, etc.
177 | if (props.thousandSeparator === "."){
178 | // special handle the . thousand separator
179 | initialValue = initialValue.replace(/\./g, '');
180 | }
181 |
182 | if (props.decimalSeparator != "."){
183 | // fix the decimal separator
184 | initialValue = initialValue.replace(new RegExp(props.decimalSeparator, 'g'), '.');
185 | }
186 |
187 | //Strip out anything that is not a digit, -, or decimal separator
188 | initialValue = initialValue.replace(/[^0-9-.]/g, '');
189 |
190 | // now we can parse.
191 | initialValue = Number.parseFloat(initialValue);
192 | }
193 | initialValue = Number(initialValue).toLocaleString(undefined, {
194 | style : 'decimal',
195 | minimumFractionDigits: props.precision,
196 | maximumFractionDigits: props.precision
197 | });
198 |
199 | }
200 |
201 | var ref = mask(
202 | initialValue,
203 | props.precision,
204 | props.decimalSeparator,
205 | props.thousandSeparator,
206 | props.allowNegative,
207 | props.prefix,
208 | props.suffix
209 | );
210 | var maskedValue = ref.maskedValue;
211 | var value = ref.value;
212 |
213 | return { maskedValue: maskedValue, value: value, customProps: customProps };
214 | };
215 |
216 |
217 | /**
218 | * Component lifecycle function.
219 | * Invoked when a component is receiving new props. This method is not called for the initial render.
220 | *
221 | * @param nextProps
222 | * @see https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops
223 | */
224 | CurrencyInput.prototype.componentWillReceiveProps = function componentWillReceiveProps (nextProps) {
225 | this.setState(this.prepareProps(nextProps));
226 | };
227 |
228 |
229 | /**
230 | * Component lifecycle function.
231 | * @returns {XML}
232 | * @see https://facebook.github.io/react/docs/react-component.html#componentdidmount
233 | */
234 | CurrencyInput.prototype.componentDidMount = function componentDidMount (){
235 | var node = ReactDOM.findDOMNode(this.theInput);
236 | var selectionStart, selectionEnd;
237 |
238 | if (this.props.autoFocus) {
239 | this.theInput.focus();
240 | selectionEnd = this.state.maskedValue.length - this.props.suffix.length;
241 | selectionStart = selectionEnd;
242 | } else {
243 | selectionEnd = Math.min(node.selectionEnd, this.theInput.value.length - this.props.suffix.length);
244 | selectionStart = Math.min(node.selectionStart, selectionEnd);
245 | }
246 |
247 | node.setSelectionRange(selectionStart, selectionEnd);
248 | };
249 |
250 |
251 | /**
252 | * Component lifecycle function
253 | * @returns {XML}
254 | * @see https://facebook.github.io/react/docs/react-component.html#componentwillupdate
255 | */
256 | CurrencyInput.prototype.componentWillUpdate = function componentWillUpdate () {
257 | var node = ReactDOM.findDOMNode(this.theInput);
258 | this.inputSelectionStart = node.selectionStart;
259 | this.inputSelectionEnd = node.selectionEnd;
260 | };
261 |
262 |
263 | /**
264 | * Component lifecycle function.
265 | * @returns {XML}
266 | * @see https://facebook.github.io/react/docs/react-component.html#componentdidupdate
267 | */
268 | CurrencyInput.prototype.componentDidUpdate = function componentDidUpdate (prevProps, prevState){
269 | var ref = this.props;
270 | var decimalSeparator = ref.decimalSeparator;
271 | var node = ReactDOM.findDOMNode(this.theInput);
272 | var isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;
273 | var minPos = this.props.prefix.length + (isNegative ? 1 : 0);
274 | var selectionEnd = Math.max(minPos, Math.min(this.inputSelectionEnd, this.theInput.value.length - this.props.suffix.length));
275 | var selectionStart = Math.max(minPos, Math.min(this.inputSelectionEnd, selectionEnd));
276 |
277 | var regexEscapeRegex = /[-[\]{}()*+?.,\\^$|#\s]/g;
278 | var separatorsRegex = new RegExp(decimalSeparator.replace(regexEscapeRegex, '\\$&') + '|' + this.props.thousandSeparator.replace(regexEscapeRegex, '\\$&'), 'g');
279 | var currSeparatorCount = (this.state.maskedValue.match(separatorsRegex) || []).length;
280 | var prevSeparatorCount = (prevState.maskedValue.match(separatorsRegex) || []).length;
281 | var adjustment = Math.max(currSeparatorCount - prevSeparatorCount, 0);
282 |
283 | selectionEnd = selectionEnd + adjustment;
284 | selectionStart = selectionStart + adjustment;
285 |
286 | var precision = Number(this.props.precision);
287 |
288 | var baselength = this.props.suffix.length
289 | + this.props.prefix.length
290 | + (precision > 0 ? decimalSeparator.length : 0) // if precision is 0 there will be no decimal part
291 | + precision
292 | + 1; // This is to account for the default '0' value that comes before the decimal separator
293 |
294 | if (this.state.maskedValue.length == baselength){
295 | // if we are already at base length, position the cursor at the end.
296 | selectionEnd = this.theInput.value.length - this.props.suffix.length;
297 | selectionStart = selectionEnd;
298 | }
299 |
300 | node.setSelectionRange(selectionStart, selectionEnd);
301 | this.inputSelectionStart = selectionStart;
302 | this.inputSelectionEnd = selectionEnd;
303 | };
304 |
305 |
306 | /**
307 | * onChange Event Handler
308 | * @param event
309 | */
310 | CurrencyInput.prototype.handleChange = function handleChange (event) {
311 | var this$1 = this;
312 |
313 | event.preventDefault();
314 | var ref = mask(
315 | event.target.value,
316 | this.props.precision,
317 | this.props.decimalSeparator,
318 | this.props.thousandSeparator,
319 | this.props.allowNegative,
320 | this.props.prefix,
321 | this.props.suffix
322 | );
323 | var maskedValue = ref.maskedValue;
324 | var value = ref.value;
325 |
326 | event.persist(); // fixes issue #23
327 |
328 | this.setState({ maskedValue: maskedValue, value: value }, function () {
329 | this$1.props.onChange(maskedValue, value, event);
330 | this$1.props.onChangeEvent(event, maskedValue, value);
331 | });
332 | };
333 |
334 |
335 | /**
336 | * onFocus Event Handler
337 | * @param event
338 | */
339 | CurrencyInput.prototype.handleFocus = function handleFocus (event) {
340 | if (!this.theInput) { return; }
341 |
342 | //Whenever we receive focus check to see if the position is before the suffix, if not, move it.
343 | var selectionEnd = this.theInput.value.length - this.props.suffix.length;
344 | var isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;
345 | var selectionStart = this.props.prefix.length + (isNegative ? 1 : 0);
346 | this.props.selectAllOnFocus && event.target.setSelectionRange(selectionStart, selectionEnd);
347 | this.inputSelectionStart = selectionStart;
348 | this.inputSelectionEnd = selectionEnd;
349 | };
350 |
351 |
352 | CurrencyInput.prototype.handleBlur = function handleBlur (event) {
353 | this.inputSelectionStart = 0;
354 | this.inputSelectionEnd = 0;
355 | };
356 |
357 |
358 | /**
359 | * Component lifecycle function.
360 | * @returns {XML}
361 | * @see https://facebook.github.io/react/docs/component-specs.html#render
362 | */
363 | CurrencyInput.prototype.render = function render () {
364 | var this$1 = this;
365 |
366 | return (
367 | React.createElement( 'input', Object.assign({},
368 | { ref: function (input) { this$1.theInput = input; }, type: this.props.inputType, value: this.state.maskedValue, onChange: this.handleChange, onFocus: this.handleFocus, onMouseUp: this.handleFocus }, this.state.customProps))
369 | )
370 | };
371 |
372 | return CurrencyInput;
373 | }(Component));
374 |
375 |
376 |
377 | /**
378 | * Prop validation.
379 | * @see https://facebook.github.io/react/docs/component-specs.html#proptypes
380 | */
381 |
382 | CurrencyInput.propTypes = {
383 | onChange: PropTypes.func,
384 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
385 | decimalSeparator: PropTypes.string,
386 | thousandSeparator: PropTypes.string,
387 | precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
388 | inputType: PropTypes.string,
389 | allowNegative: PropTypes.bool,
390 | allowEmpty: PropTypes.bool,
391 | prefix: PropTypes.string,
392 | suffix: PropTypes.string,
393 | selectAllOnFocus: PropTypes.bool
394 | };
395 |
396 |
397 | CurrencyInput.defaultProps = {
398 | onChange: function(maskValue, value, event) {/*no-op*/},
399 | onChangeEvent: function(event, maskValue, value) {/*no-op*/},
400 | autoFocus: false,
401 | value: '0',
402 | decimalSeparator: '.',
403 | thousandSeparator: ',',
404 | precision: '2',
405 | inputType: 'text',
406 | allowNegative: false,
407 | prefix: '',
408 | suffix: '',
409 | selectAllOnFocus: false
410 | };
411 |
412 | export default CurrencyInput;
413 | //# sourceMappingURL=react-currency-input.es.js.map
414 |
--------------------------------------------------------------------------------
/lib/react-currency-input.es.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"react-currency-input.es.js","sources":["../src/object-assign-polyfill.js","../src/mask.js","../src/index.js"],"sourcesContent":["Object.assign = Object.assign ||\n function(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n","\nexport default function mask(value, precision = 2, decimalSeparator = '.', thousandSeparator = ',', allowNegative = false, prefix = '', suffix = ''){\n // provide some default values and arg validation.\n if (precision < 0) { precision = 0; } // precision cannot be negative\n if (precision > 20) { precision = 20; } // precision cannot be greater than 20\n \n if (value === null || value===undefined) {\n return {\n value: 0,\n maskedValue: ''\n };\n }\n \n value = String(value); //if the given value is a Number, let's convert into String to manipulate that\n\n if (value.length == 0) {\n return {\n value: 0,\n maskedValue: ''\n };\n }\n\n\n // extract digits. if no digits, fill in a zero.\n let digits = value.match(/\\d/g) || ['0'];\n \n let numberIsNegative = false;\n if (allowNegative) {\n let negativeSignCount = (value.match(/-/g) || []).length;\n // number will be negative if we have an odd number of \"-\"\n // ideally, we should only ever have 0, 1 or 2 (positive number, making a number negative\n // and making a negative number positive, respectively)\n numberIsNegative = negativeSignCount % 2 === 1;\n \n // if every digit in the array is '0', then the number should never be negative\n let allDigitsAreZero = true;\n for (let idx=0; idx < digits.length; idx += 1) {\n if(digits[idx] !== '0') {\n allDigitsAreZero = false;\n break;\n }\n }\n if (allDigitsAreZero) {\n numberIsNegative = false;\n }\n }\n\n // zero-pad a input\n while (digits.length <= precision) { digits.unshift('0'); }\n\n if (precision > 0) {\n // add the decimal separator\n digits.splice(digits.length - precision, 0, \".\");\n }\n\n // clean up extraneous digits like leading zeros.\n digits = Number(digits.join('')).toFixed(precision).split('');\n let raw = Number(digits.join(''));\n\n let decimalpos = digits.length - precision - 1; // -1 needed to position the decimal separator before the digits.\n if (precision > 0) {\n // set the final decimal separator\n digits[decimalpos] = decimalSeparator;\n } else {\n // when precision is 0, there is no decimal separator.\n decimalpos = digits.length;\n }\n\n // add in any thousand separators\n for (let x=decimalpos - 3; x > 0; x = x - 3) {\n digits.splice(x, 0, thousandSeparator);\n }\n\n // if we have a prefix or suffix, add them in.\n if (prefix.length > 0) { digits.unshift(prefix); }\n if (suffix.length > 0) { digits.push(suffix); }\n\n // if the number is negative, insert a \"-\" to\n // the front of the array and negate the raw value\n if (allowNegative && numberIsNegative) {\n digits.unshift('-');\n raw = -raw;\n }\n\n return {\n value: raw,\n maskedValue: digits.join('').trim()\n };\n}\n","import './object-assign-polyfill';\n\nimport PropTypes from 'prop-types';\nimport React, { Component } from 'react'\nimport ReactDOM from 'react-dom'\nimport mask from './mask.js'\n\n// IE* parseFloat polyfill\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#Polyfill\nNumber.parseFloat = parseFloat;\n\nclass CurrencyInput extends Component {\n constructor(props) {\n super(props);\n this.prepareProps = this.prepareProps.bind(this);\n this.handleChange = this.handleChange.bind(this);\n this.handleFocus = this.handleFocus.bind(this);\n this.state = this.prepareProps(this.props);\n\n this.inputSelectionStart = 1;\n this.inputSelectionEnd = 1;\n }\n\n\n /**\n * Exposes the current masked value.\n *\n * @returns {String}\n */\n getMaskedValue() {\n return this.state.maskedValue;\n }\n\n\n /**\n * General function used to cleanup and define the final props used for rendering\n * @returns {{ maskedValue: {String}, value: {Number}, customProps: {Object} }}\n */\n prepareProps(props) {\n let customProps = {...props}; // babeljs converts to Object.assign, then polyfills.\n delete customProps.onChange;\n delete customProps.onChangeEvent;\n delete customProps.value;\n delete customProps.decimalSeparator;\n delete customProps.thousandSeparator;\n delete customProps.precision;\n delete customProps.inputType;\n delete customProps.allowNegative;\n delete customProps.allowEmpty;\n delete customProps.prefix;\n delete customProps.suffix;\n delete customProps.selectAllOnFocus;\n delete customProps.autoFocus;\n\n let initialValue = props.value;\n if (initialValue === null) {\n initialValue = props.allowEmpty? null : '';\n }else{\n\n if (typeof initialValue == 'string') {\n // Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\"\n // Now they have two problems.\n\n // Strip out thousand separators, prefix, and suffix, etc.\n if (props.thousandSeparator === \".\"){\n // special handle the . thousand separator\n initialValue = initialValue.replace(/\\./g, '');\n }\n\n if (props.decimalSeparator != \".\"){\n // fix the decimal separator\n initialValue = initialValue.replace(new RegExp(props.decimalSeparator, 'g'), '.');\n }\n\n //Strip out anything that is not a digit, -, or decimal separator\n initialValue = initialValue.replace(/[^0-9-.]/g, '');\n\n // now we can parse.\n initialValue = Number.parseFloat(initialValue);\n }\n initialValue = Number(initialValue).toLocaleString(undefined, {\n style : 'decimal',\n minimumFractionDigits: props.precision,\n maximumFractionDigits: props.precision\n })\n\n }\n\n const { maskedValue, value } = mask(\n initialValue,\n props.precision,\n props.decimalSeparator,\n props.thousandSeparator,\n props.allowNegative,\n props.prefix,\n props.suffix\n );\n\n return { maskedValue, value, customProps };\n }\n\n\n /**\n * Component lifecycle function.\n * Invoked when a component is receiving new props. This method is not called for the initial render.\n *\n * @param nextProps\n * @see https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops\n */\n componentWillReceiveProps(nextProps) {\n this.setState(this.prepareProps(nextProps));\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentdidmount\n */\n componentDidMount(){\n let node = ReactDOM.findDOMNode(this.theInput);\n let selectionStart, selectionEnd;\n\n if (this.props.autoFocus) {\n this.theInput.focus();\n selectionEnd = this.state.maskedValue.length - this.props.suffix.length;\n selectionStart = selectionEnd;\n } else {\n selectionEnd = Math.min(node.selectionEnd, this.theInput.value.length - this.props.suffix.length);\n selectionStart = Math.min(node.selectionStart, selectionEnd);\n }\n\n node.setSelectionRange(selectionStart, selectionEnd);\n }\n\n\n /**\n * Component lifecycle function\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentwillupdate\n */\n componentWillUpdate() {\n let node = ReactDOM.findDOMNode(this.theInput);\n this.inputSelectionStart = node.selectionStart;\n this.inputSelectionEnd = node.selectionEnd;\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentdidupdate\n */\n componentDidUpdate(prevProps, prevState){\n const { decimalSeparator } = this.props;\n let node = ReactDOM.findDOMNode(this.theInput);\n let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;\n let minPos = this.props.prefix.length + (isNegative ? 1 : 0);\n let selectionEnd = Math.max(minPos, Math.min(this.inputSelectionEnd, this.theInput.value.length - this.props.suffix.length));\n let selectionStart = Math.max(minPos, Math.min(this.inputSelectionEnd, selectionEnd));\n\n let regexEscapeRegex = /[-[\\]{}()*+?.,\\\\^$|#\\s]/g;\n let separatorsRegex = new RegExp(decimalSeparator.replace(regexEscapeRegex, '\\\\$&') + '|' + this.props.thousandSeparator.replace(regexEscapeRegex, '\\\\$&'), 'g');\n let currSeparatorCount = (this.state.maskedValue.match(separatorsRegex) || []).length;\n let prevSeparatorCount = (prevState.maskedValue.match(separatorsRegex) || []).length;\n let adjustment = Math.max(currSeparatorCount - prevSeparatorCount, 0);\n\n selectionEnd = selectionEnd + adjustment;\n selectionStart = selectionStart + adjustment;\n\n const precision = Number(this.props.precision);\n\n let baselength = this.props.suffix.length\n + this.props.prefix.length\n + (precision > 0 ? decimalSeparator.length : 0) // if precision is 0 there will be no decimal part\n + precision\n + 1; // This is to account for the default '0' value that comes before the decimal separator\n\n if (this.state.maskedValue.length == baselength){\n // if we are already at base length, position the cursor at the end.\n selectionEnd = this.theInput.value.length - this.props.suffix.length;\n selectionStart = selectionEnd;\n }\n\n node.setSelectionRange(selectionStart, selectionEnd);\n this.inputSelectionStart = selectionStart;\n this.inputSelectionEnd = selectionEnd;\n }\n\n\n /**\n * onChange Event Handler\n * @param event\n */\n handleChange(event) {\n event.preventDefault();\n let { maskedValue, value } = mask(\n event.target.value,\n this.props.precision,\n this.props.decimalSeparator,\n this.props.thousandSeparator,\n this.props.allowNegative,\n this.props.prefix,\n this.props.suffix\n );\n\n event.persist(); // fixes issue #23\n\n this.setState({ maskedValue, value }, () => {\n this.props.onChange(maskedValue, value, event);\n this.props.onChangeEvent(event, maskedValue, value);\n });\n }\n\n\n /**\n * onFocus Event Handler\n * @param event\n */\n handleFocus(event) {\n if (!this.theInput) return;\n\n //Whenever we receive focus check to see if the position is before the suffix, if not, move it.\n let selectionEnd = this.theInput.value.length - this.props.suffix.length;\n let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;\n let selectionStart = this.props.prefix.length + (isNegative ? 1 : 0);\n this.props.selectAllOnFocus && event.target.setSelectionRange(selectionStart, selectionEnd);\n this.inputSelectionStart = selectionStart;\n this.inputSelectionEnd = selectionEnd;\n }\n\n\n handleBlur(event) {\n this.inputSelectionStart = 0;\n this.inputSelectionEnd = 0;\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/component-specs.html#render\n */\n render() {\n return (\n { this.theInput = input; }}\n type={this.props.inputType}\n value={this.state.maskedValue}\n onChange={this.handleChange}\n onFocus={this.handleFocus}\n onMouseUp={this.handleFocus}\n {...this.state.customProps}\n />\n )\n }\n}\n\n\n\n/**\n * Prop validation.\n * @see https://facebook.github.io/react/docs/component-specs.html#proptypes\n */\n\nCurrencyInput.propTypes = {\n onChange: PropTypes.func,\n value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n decimalSeparator: PropTypes.string,\n thousandSeparator: PropTypes.string,\n precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n inputType: PropTypes.string,\n allowNegative: PropTypes.bool,\n allowEmpty: PropTypes.bool,\n prefix: PropTypes.string,\n suffix: PropTypes.string,\n selectAllOnFocus: PropTypes.bool\n};\n\n\nCurrencyInput.defaultProps = {\n onChange: function(maskValue, value, event) {/*no-op*/},\n onChangeEvent: function(event, maskValue, value) {/*no-op*/},\n autoFocus: false,\n value: '0',\n decimalSeparator: '.',\n thousandSeparator: ',',\n precision: '2',\n inputType: 'text',\n allowNegative: false,\n prefix: '',\n suffix: '',\n selectAllOnFocus: false\n};\n\n\nexport default CurrencyInput\n"],"names":["arguments","let","super","const","this"],"mappings":"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"}
--------------------------------------------------------------------------------
/lib/react-currency-input.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("prop-types"),require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["prop-types","react","react-dom"],t):e["react-currency-input"]=t(e.PropTypes,e.React,e.ReactDOM)}(this,function(e,t,g){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e;var n="default"in t?t.default:t;function o(e,t,n,i,a,o,s){if(void 0===t&&(t=2),void 0===n&&(n="."),void 0===i&&(i=","),void 0===a&&(a=!1),void 0===o&&(o=""),void 0===s&&(s=""),t<0&&(t=0),20 20) { precision = 20; } // precision cannot be greater than 20\n \n if (value === null || value===undefined) {\n return {\n value: 0,\n maskedValue: ''\n };\n }\n \n value = String(value); //if the given value is a Number, let's convert into String to manipulate that\n\n if (value.length == 0) {\n return {\n value: 0,\n maskedValue: ''\n };\n }\n\n\n // extract digits. if no digits, fill in a zero.\n let digits = value.match(/\\d/g) || ['0'];\n \n let numberIsNegative = false;\n if (allowNegative) {\n let negativeSignCount = (value.match(/-/g) || []).length;\n // number will be negative if we have an odd number of \"-\"\n // ideally, we should only ever have 0, 1 or 2 (positive number, making a number negative\n // and making a negative number positive, respectively)\n numberIsNegative = negativeSignCount % 2 === 1;\n \n // if every digit in the array is '0', then the number should never be negative\n let allDigitsAreZero = true;\n for (let idx=0; idx < digits.length; idx += 1) {\n if(digits[idx] !== '0') {\n allDigitsAreZero = false;\n break;\n }\n }\n if (allDigitsAreZero) {\n numberIsNegative = false;\n }\n }\n\n // zero-pad a input\n while (digits.length <= precision) { digits.unshift('0'); }\n\n if (precision > 0) {\n // add the decimal separator\n digits.splice(digits.length - precision, 0, \".\");\n }\n\n // clean up extraneous digits like leading zeros.\n digits = Number(digits.join('')).toFixed(precision).split('');\n let raw = Number(digits.join(''));\n\n let decimalpos = digits.length - precision - 1; // -1 needed to position the decimal separator before the digits.\n if (precision > 0) {\n // set the final decimal separator\n digits[decimalpos] = decimalSeparator;\n } else {\n // when precision is 0, there is no decimal separator.\n decimalpos = digits.length;\n }\n\n // add in any thousand separators\n for (let x=decimalpos - 3; x > 0; x = x - 3) {\n digits.splice(x, 0, thousandSeparator);\n }\n\n // if we have a prefix or suffix, add them in.\n if (prefix.length > 0) { digits.unshift(prefix); }\n if (suffix.length > 0) { digits.push(suffix); }\n\n // if the number is negative, insert a \"-\" to\n // the front of the array and negate the raw value\n if (allowNegative && numberIsNegative) {\n digits.unshift('-');\n raw = -raw;\n }\n\n return {\n value: raw,\n maskedValue: digits.join('').trim()\n };\n}\n","Object.assign = Object.assign ||\n function(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n","import './object-assign-polyfill';\n\nimport PropTypes from 'prop-types';\nimport React, { Component } from 'react'\nimport ReactDOM from 'react-dom'\nimport mask from './mask.js'\n\n// IE* parseFloat polyfill\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#Polyfill\nNumber.parseFloat = parseFloat;\n\nclass CurrencyInput extends Component {\n constructor(props) {\n super(props);\n this.prepareProps = this.prepareProps.bind(this);\n this.handleChange = this.handleChange.bind(this);\n this.handleFocus = this.handleFocus.bind(this);\n this.state = this.prepareProps(this.props);\n\n this.inputSelectionStart = 1;\n this.inputSelectionEnd = 1;\n }\n\n\n /**\n * Exposes the current masked value.\n *\n * @returns {String}\n */\n getMaskedValue() {\n return this.state.maskedValue;\n }\n\n\n /**\n * General function used to cleanup and define the final props used for rendering\n * @returns {{ maskedValue: {String}, value: {Number}, customProps: {Object} }}\n */\n prepareProps(props) {\n let customProps = {...props}; // babeljs converts to Object.assign, then polyfills.\n delete customProps.onChange;\n delete customProps.onChangeEvent;\n delete customProps.value;\n delete customProps.decimalSeparator;\n delete customProps.thousandSeparator;\n delete customProps.precision;\n delete customProps.inputType;\n delete customProps.allowNegative;\n delete customProps.allowEmpty;\n delete customProps.prefix;\n delete customProps.suffix;\n delete customProps.selectAllOnFocus;\n delete customProps.autoFocus;\n\n let initialValue = props.value;\n if (initialValue === null) {\n initialValue = props.allowEmpty? null : '';\n }else{\n\n if (typeof initialValue == 'string') {\n // Some people, when confronted with a problem, think \"I know, I'll use regular expressions.\"\n // Now they have two problems.\n\n // Strip out thousand separators, prefix, and suffix, etc.\n if (props.thousandSeparator === \".\"){\n // special handle the . thousand separator\n initialValue = initialValue.replace(/\\./g, '');\n }\n\n if (props.decimalSeparator != \".\"){\n // fix the decimal separator\n initialValue = initialValue.replace(new RegExp(props.decimalSeparator, 'g'), '.');\n }\n\n //Strip out anything that is not a digit, -, or decimal separator\n initialValue = initialValue.replace(/[^0-9-.]/g, '');\n\n // now we can parse.\n initialValue = Number.parseFloat(initialValue);\n }\n initialValue = Number(initialValue).toLocaleString(undefined, {\n style : 'decimal',\n minimumFractionDigits: props.precision,\n maximumFractionDigits: props.precision\n })\n\n }\n\n const { maskedValue, value } = mask(\n initialValue,\n props.precision,\n props.decimalSeparator,\n props.thousandSeparator,\n props.allowNegative,\n props.prefix,\n props.suffix\n );\n\n return { maskedValue, value, customProps };\n }\n\n\n /**\n * Component lifecycle function.\n * Invoked when a component is receiving new props. This method is not called for the initial render.\n *\n * @param nextProps\n * @see https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops\n */\n componentWillReceiveProps(nextProps) {\n this.setState(this.prepareProps(nextProps));\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentdidmount\n */\n componentDidMount(){\n let node = ReactDOM.findDOMNode(this.theInput);\n let selectionStart, selectionEnd;\n\n if (this.props.autoFocus) {\n this.theInput.focus();\n selectionEnd = this.state.maskedValue.length - this.props.suffix.length;\n selectionStart = selectionEnd;\n } else {\n selectionEnd = Math.min(node.selectionEnd, this.theInput.value.length - this.props.suffix.length);\n selectionStart = Math.min(node.selectionStart, selectionEnd);\n }\n\n node.setSelectionRange(selectionStart, selectionEnd);\n }\n\n\n /**\n * Component lifecycle function\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentwillupdate\n */\n componentWillUpdate() {\n let node = ReactDOM.findDOMNode(this.theInput);\n this.inputSelectionStart = node.selectionStart;\n this.inputSelectionEnd = node.selectionEnd;\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/react-component.html#componentdidupdate\n */\n componentDidUpdate(prevProps, prevState){\n const { decimalSeparator } = this.props;\n let node = ReactDOM.findDOMNode(this.theInput);\n let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;\n let minPos = this.props.prefix.length + (isNegative ? 1 : 0);\n let selectionEnd = Math.max(minPos, Math.min(this.inputSelectionEnd, this.theInput.value.length - this.props.suffix.length));\n let selectionStart = Math.max(minPos, Math.min(this.inputSelectionEnd, selectionEnd));\n\n let regexEscapeRegex = /[-[\\]{}()*+?.,\\\\^$|#\\s]/g;\n let separatorsRegex = new RegExp(decimalSeparator.replace(regexEscapeRegex, '\\\\$&') + '|' + this.props.thousandSeparator.replace(regexEscapeRegex, '\\\\$&'), 'g');\n let currSeparatorCount = (this.state.maskedValue.match(separatorsRegex) || []).length;\n let prevSeparatorCount = (prevState.maskedValue.match(separatorsRegex) || []).length;\n let adjustment = Math.max(currSeparatorCount - prevSeparatorCount, 0);\n\n selectionEnd = selectionEnd + adjustment;\n selectionStart = selectionStart + adjustment;\n\n const precision = Number(this.props.precision);\n\n let baselength = this.props.suffix.length\n + this.props.prefix.length\n + (precision > 0 ? decimalSeparator.length : 0) // if precision is 0 there will be no decimal part\n + precision\n + 1; // This is to account for the default '0' value that comes before the decimal separator\n\n if (this.state.maskedValue.length == baselength){\n // if we are already at base length, position the cursor at the end.\n selectionEnd = this.theInput.value.length - this.props.suffix.length;\n selectionStart = selectionEnd;\n }\n\n node.setSelectionRange(selectionStart, selectionEnd);\n this.inputSelectionStart = selectionStart;\n this.inputSelectionEnd = selectionEnd;\n }\n\n\n /**\n * onChange Event Handler\n * @param event\n */\n handleChange(event) {\n event.preventDefault();\n let { maskedValue, value } = mask(\n event.target.value,\n this.props.precision,\n this.props.decimalSeparator,\n this.props.thousandSeparator,\n this.props.allowNegative,\n this.props.prefix,\n this.props.suffix\n );\n\n event.persist(); // fixes issue #23\n\n this.setState({ maskedValue, value }, () => {\n this.props.onChange(maskedValue, value, event);\n this.props.onChangeEvent(event, maskedValue, value);\n });\n }\n\n\n /**\n * onFocus Event Handler\n * @param event\n */\n handleFocus(event) {\n if (!this.theInput) return;\n\n //Whenever we receive focus check to see if the position is before the suffix, if not, move it.\n let selectionEnd = this.theInput.value.length - this.props.suffix.length;\n let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;\n let selectionStart = this.props.prefix.length + (isNegative ? 1 : 0);\n this.props.selectAllOnFocus && event.target.setSelectionRange(selectionStart, selectionEnd);\n this.inputSelectionStart = selectionStart;\n this.inputSelectionEnd = selectionEnd;\n }\n\n\n handleBlur(event) {\n this.inputSelectionStart = 0;\n this.inputSelectionEnd = 0;\n }\n\n\n /**\n * Component lifecycle function.\n * @returns {XML}\n * @see https://facebook.github.io/react/docs/component-specs.html#render\n */\n render() {\n return (\n { this.theInput = input; }}\n type={this.props.inputType}\n value={this.state.maskedValue}\n onChange={this.handleChange}\n onFocus={this.handleFocus}\n onMouseUp={this.handleFocus}\n {...this.state.customProps}\n />\n )\n }\n}\n\n\n\n/**\n * Prop validation.\n * @see https://facebook.github.io/react/docs/component-specs.html#proptypes\n */\n\nCurrencyInput.propTypes = {\n onChange: PropTypes.func,\n value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n decimalSeparator: PropTypes.string,\n thousandSeparator: PropTypes.string,\n precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),\n inputType: PropTypes.string,\n allowNegative: PropTypes.bool,\n allowEmpty: PropTypes.bool,\n prefix: PropTypes.string,\n suffix: PropTypes.string,\n selectAllOnFocus: PropTypes.bool\n};\n\n\nCurrencyInput.defaultProps = {\n onChange: function(maskValue, value, event) {/*no-op*/},\n onChangeEvent: function(event, maskValue, value) {/*no-op*/},\n autoFocus: false,\n value: '0',\n decimalSeparator: '.',\n thousandSeparator: ',',\n precision: '2',\n inputType: 'text',\n allowNegative: false,\n prefix: '',\n suffix: '',\n selectAllOnFocus: false\n};\n\n\nexport default CurrencyInput\n"],"names":["mask","value","precision","decimalSeparator","thousandSeparator","allowNegative","prefix","suffix","maskedValue","String","length","let","digits","match","numberIsNegative","allDigitsAreZero","idx","unshift","splice","Number","join","toFixed","split","raw","decimalpos","x","push","trim","Object","assign","target","i","arguments","source","key","prototype","hasOwnProperty","call","parseFloat","CurrencyInput","props","super","this","prepareProps","bind","handleChange","handleFocus","state","inputSelectionStart","inputSelectionEnd","getMaskedValue","customProps","onChange","onChangeEvent","inputType","allowEmpty","selectAllOnFocus","autoFocus","initialValue","replace","RegExp","toLocaleString","undefined","style","minimumFractionDigits","maximumFractionDigits","componentWillReceiveProps","nextProps","setState","componentDidMount","selectionStart","selectionEnd","node","ReactDOM","findDOMNode","theInput","focus","Math","min","setSelectionRange","componentWillUpdate","componentDidUpdate","prevProps","prevState","isNegative","minPos","max","regexEscapeRegex","separatorsRegex","currSeparatorCount","prevSeparatorCount","adjustment","const","baselength","event","preventDefault","persist","handleBlur","render","React","ref","input","type","onFocus","onMouseUp","Component","propTypes","PropTypes","func","oneOfType","number","string","bool","defaultProps","maskValue"],"mappings":"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"}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-currency-input",
3 | "version": "1.3.7",
4 | "description": "React component for inputing currency amounts",
5 | "main": "lib/react-currency-input.cjs.js",
6 | "jsnext:main": "lib/react-currency-input.es.js",
7 | "module": "lib/react-currency-input.es.js",
8 | "scripts": {
9 | "lint": "eslint src/**",
10 | "build:umd": "cross-env NODE_ENV=umd rollup -c",
11 | "build:cjs": "cross-env NODE_ENV=cjs rollup -c",
12 | "build:es": "cross-env NODE_ENV=es rollup -c",
13 | "build": "rimraf lib && npm run build:umd && npm run build:cjs && npm run build:es",
14 | "prepublish": "npm run build",
15 | "build-example": "browserify examples/index.js -o examples/bundle.js -t [ babelify --presets [ es2015 react ] ]",
16 | "test": "mocha --compilers js:babel-register",
17 | "webpack": "webpack",
18 | "webpack-dev": "webpack-dev-server"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/jsillitoe/react-currency-input.git"
23 | },
24 | "keywords": [
25 | "react",
26 | "es6",
27 | "javascript",
28 | "money",
29 | "currency",
30 | "i18n",
31 | "react-component"
32 | ],
33 | "author": "Joe Sillitoe ",
34 | "license": "MIT",
35 | "bugs": {
36 | "url": "https://github.com/jsillitoe/react-currency-input/issues"
37 | },
38 | "homepage": "https://github.com/jsillitoe/react-currency-input#readme",
39 | "peerDependencies": {
40 | "prop-types": "^15.6.0",
41 | "react": "^16.0.0",
42 | "react-dom": "^16.0.0"
43 | },
44 | "devDependencies": {
45 | "babel-cli": "^6.18.0",
46 | "babel-loader": "6.4.0",
47 | "babel-preset-es2015": "^6.9.0",
48 | "babel-preset-react": "^6.5.0",
49 | "babel-preset-stage-3": "6.22.0",
50 | "babel-register": "^6.9.0",
51 | "babelify": "^7.3.0",
52 | "browserify": "^13.0.1",
53 | "chai": "^3.5.0",
54 | "cross-env": "^5.1.0",
55 | "eslint": "^2.13.1",
56 | "mocha": "^2.5.3",
57 | "rimraf": "^2.5.2",
58 | "rollup": "^0.50.0",
59 | "rollup-plugin-buble": "^0.16.0",
60 | "rollup-plugin-commonjs": "^8.2.1",
61 | "rollup-plugin-filesize": "^1.4.2",
62 | "rollup-plugin-node-resolve": "^3.0.0",
63 | "rollup-plugin-peer-deps-external": "^1.0.0",
64 | "rollup-plugin-uglify": "^2.0.1",
65 | "sinon": "^1.17.4",
66 | "sinon-chai": "^2.8.0",
67 | "webpack": "2.2.1",
68 | "webpack-dev-server": "2.4.1"
69 | },
70 | "dependencies": {
71 | "jsdom": "^9.12.0",
72 | "prop-types": "^15.6.0",
73 | "react": "^16.4.1",
74 | "react-dom": "^16.4.1"
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import buble from 'rollup-plugin-buble';
2 | import resolve from 'rollup-plugin-node-resolve';
3 | import commonjs from 'rollup-plugin-commonjs';
4 | import uglify from 'rollup-plugin-uglify';
5 | import filesize from 'rollup-plugin-filesize';
6 | import peerDeps from 'rollup-plugin-peer-deps-external';
7 |
8 | const format = process.env.NODE_ENV;
9 | const isUmd = format === 'umd';
10 | const file = `lib/react-currency-input.${isUmd ? 'min' : format}.js`
11 |
12 | const config = {
13 | input: './src/index.js',
14 | name: 'react-currency-input',
15 | sourcemap: true,
16 | output: {
17 | file,
18 | format,
19 | },
20 | plugins: [
21 | peerDeps(),
22 | resolve({
23 | jsnext: true,
24 | main: true,
25 | browser: true,
26 | }),
27 | buble({
28 | objectAssign: 'Object.assign',
29 | exclude: ['node_modules/**'],
30 | }),
31 | commonjs(),
32 | ],
33 | };
34 |
35 | isUmd && config.plugins.push(uglify(), filesize());
36 |
37 | export default config;
38 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './object-assign-polyfill';
2 |
3 | import PropTypes from 'prop-types';
4 | import React, { Component } from 'react'
5 | import ReactDOM from 'react-dom'
6 | import mask from './mask.js'
7 |
8 | // IE* parseFloat polyfill
9 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#Polyfill
10 | Number.parseFloat = parseFloat;
11 |
12 | class CurrencyInput extends Component {
13 | constructor(props) {
14 | super(props);
15 | this.prepareProps = this.prepareProps.bind(this);
16 | this.handleChange = this.handleChange.bind(this);
17 | this.handleFocus = this.handleFocus.bind(this);
18 | this.setSelectionRange = this.setSelectionRange.bind(this);
19 | this.state = this.prepareProps(this.props);
20 |
21 | this.inputSelectionStart = 1;
22 | this.inputSelectionEnd = 1;
23 | }
24 |
25 |
26 | /**
27 | * Exposes the current masked value.
28 | *
29 | * @returns {String}
30 | */
31 | getMaskedValue() {
32 | return this.state.maskedValue;
33 | }
34 |
35 |
36 | /**
37 | * General function used to cleanup and define the final props used for rendering
38 | * @returns {{ maskedValue: {String}, value: {Number}, customProps: {Object} }}
39 | */
40 | prepareProps(props) {
41 | let customProps = {...props}; // babeljs converts to Object.assign, then polyfills.
42 | delete customProps.onChange;
43 | delete customProps.onChangeEvent;
44 | delete customProps.value;
45 | delete customProps.decimalSeparator;
46 | delete customProps.thousandSeparator;
47 | delete customProps.precision;
48 | delete customProps.inputType;
49 | delete customProps.allowNegative;
50 | delete customProps.allowEmpty;
51 | delete customProps.prefix;
52 | delete customProps.suffix;
53 | delete customProps.selectAllOnFocus;
54 | delete customProps.autoFocus;
55 |
56 | let initialValue = props.value;
57 | if (initialValue === null) {
58 | initialValue = props.allowEmpty? null : '';
59 | }else{
60 |
61 | if (typeof initialValue == 'string') {
62 | // Some people, when confronted with a problem, think "I know, I'll use regular expressions."
63 | // Now they have two problems.
64 |
65 | // Strip out thousand separators, prefix, and suffix, etc.
66 | if (props.thousandSeparator === "."){
67 | // special handle the . thousand separator
68 | initialValue = initialValue.replace(/\./g, '');
69 | }
70 |
71 | if (props.decimalSeparator != "."){
72 | // fix the decimal separator
73 | initialValue = initialValue.replace(new RegExp(props.decimalSeparator, 'g'), '.');
74 | }
75 |
76 | //Strip out anything that is not a digit, -, or decimal separator
77 | initialValue = initialValue.replace(/[^0-9-.]/g, '');
78 |
79 | // now we can parse.
80 | initialValue = Number.parseFloat(initialValue);
81 | }
82 | initialValue = Number(initialValue).toLocaleString(undefined, {
83 | style : 'decimal',
84 | minimumFractionDigits: props.precision,
85 | maximumFractionDigits: props.precision
86 | })
87 |
88 | }
89 |
90 | const { maskedValue, value } = mask(
91 | initialValue,
92 | props.precision,
93 | props.decimalSeparator,
94 | props.thousandSeparator,
95 | props.allowNegative,
96 | props.prefix,
97 | props.suffix
98 | );
99 |
100 | return { maskedValue, value, customProps };
101 | }
102 |
103 |
104 | /**
105 | * Component lifecycle function.
106 | * Invoked when a component is receiving new props. This method is not called for the initial render.
107 | *
108 | * @param nextProps
109 | * @see https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops
110 | */
111 | componentWillReceiveProps(nextProps) {
112 | this.setState(this.prepareProps(nextProps));
113 | }
114 |
115 |
116 | /**
117 | * Component lifecycle function.
118 | * @returns {XML}
119 | * @see https://facebook.github.io/react/docs/react-component.html#componentdidmount
120 | */
121 | componentDidMount(){
122 | let node = ReactDOM.findDOMNode(this.theInput);
123 | let selectionStart, selectionEnd;
124 |
125 | if (this.props.autoFocus) {
126 | this.theInput.focus();
127 | selectionEnd = this.state.maskedValue.length - this.props.suffix.length;
128 | selectionStart = selectionEnd;
129 | } else {
130 | selectionEnd = Math.min(node.selectionEnd, this.theInput.value.length - this.props.suffix.length);
131 | selectionStart = Math.min(node.selectionStart, selectionEnd);
132 | }
133 |
134 | this.setSelectionRange(node, selectionStart, selectionEnd);
135 | }
136 |
137 |
138 | /**
139 | * Component lifecycle function
140 | * @returns {XML}
141 | * @see https://facebook.github.io/react/docs/react-component.html#componentwillupdate
142 | */
143 | componentWillUpdate() {
144 | let node = ReactDOM.findDOMNode(this.theInput);
145 | this.inputSelectionStart = node.selectionStart;
146 | this.inputSelectionEnd = node.selectionEnd;
147 | }
148 |
149 |
150 | /**
151 | * Component lifecycle function.
152 | * @returns {XML}
153 | * @see https://facebook.github.io/react/docs/react-component.html#componentdidupdate
154 | */
155 | componentDidUpdate(prevProps, prevState){
156 | const { decimalSeparator } = this.props;
157 | let node = ReactDOM.findDOMNode(this.theInput);
158 | let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;
159 | let minPos = this.props.prefix.length + (isNegative ? 1 : 0);
160 | let selectionEnd = Math.max(minPos, Math.min(this.inputSelectionEnd, this.theInput.value.length - this.props.suffix.length));
161 | let selectionStart = Math.max(minPos, Math.min(this.inputSelectionEnd, selectionEnd));
162 |
163 | let regexEscapeRegex = /[-[\]{}()*+?.,\\^$|#\s]/g;
164 | let separatorsRegex = new RegExp(decimalSeparator.replace(regexEscapeRegex, '\\$&') + '|' + this.props.thousandSeparator.replace(regexEscapeRegex, '\\$&'), 'g');
165 | let currSeparatorCount = (this.state.maskedValue.match(separatorsRegex) || []).length;
166 | let prevSeparatorCount = (prevState.maskedValue.match(separatorsRegex) || []).length;
167 | let adjustment = Math.max(currSeparatorCount - prevSeparatorCount, 0);
168 |
169 | selectionEnd = selectionEnd + adjustment;
170 | selectionStart = selectionStart + adjustment;
171 |
172 | const precision = Number(this.props.precision);
173 |
174 | let baselength = this.props.suffix.length
175 | + this.props.prefix.length
176 | + (precision > 0 ? decimalSeparator.length : 0) // if precision is 0 there will be no decimal part
177 | + precision
178 | + 1; // This is to account for the default '0' value that comes before the decimal separator
179 |
180 | if (this.state.maskedValue.length == baselength){
181 | // if we are already at base length, position the cursor at the end.
182 | selectionEnd = this.theInput.value.length - this.props.suffix.length;
183 | selectionStart = selectionEnd;
184 | }
185 |
186 | this.setSelectionRange(node, selectionStart, selectionEnd);
187 | this.inputSelectionStart = selectionStart;
188 | this.inputSelectionEnd = selectionEnd;
189 | }
190 |
191 | /**
192 | * Set selection range only if input is in focused state
193 | * @param node DOMElement
194 | * @param start number
195 | * @param end number
196 | */
197 | setSelectionRange(node, start, end) {
198 | if (document.activeElement === node) {
199 | node.setSelectionRange(start, end);
200 | }
201 | }
202 |
203 |
204 | /**
205 | * onChange Event Handler
206 | * @param event
207 | */
208 | handleChange(event) {
209 | event.preventDefault();
210 | let { maskedValue, value } = mask(
211 | event.target.value,
212 | this.props.precision,
213 | this.props.decimalSeparator,
214 | this.props.thousandSeparator,
215 | this.props.allowNegative,
216 | this.props.prefix,
217 | this.props.suffix
218 | );
219 |
220 | event.persist(); // fixes issue #23
221 |
222 | this.setState({ maskedValue, value }, () => {
223 | this.props.onChange(maskedValue, value, event);
224 | this.props.onChangeEvent(event, maskedValue, value);
225 | });
226 | }
227 |
228 |
229 | /**
230 | * onFocus Event Handler
231 | * @param event
232 | */
233 | handleFocus(event) {
234 | if (!this.theInput) return;
235 |
236 | //Whenever we receive focus check to see if the position is before the suffix, if not, move it.
237 | let selectionEnd = this.theInput.value.length - this.props.suffix.length;
238 | let isNegative = (this.theInput.value.match(/-/g) || []).length % 2 === 1;
239 | let selectionStart = this.props.prefix.length + (isNegative ? 1 : 0);
240 | this.props.selectAllOnFocus && event.target.setSelectionRange(selectionStart, selectionEnd);
241 | this.inputSelectionStart = selectionStart;
242 | this.inputSelectionEnd = selectionEnd;
243 | }
244 |
245 |
246 | handleBlur(event) {
247 | this.inputSelectionStart = 0;
248 | this.inputSelectionEnd = 0;
249 | }
250 |
251 |
252 | /**
253 | * Component lifecycle function.
254 | * @returns {XML}
255 | * @see https://facebook.github.io/react/docs/component-specs.html#render
256 | */
257 | render() {
258 | return (
259 | { this.theInput = input; }}
261 | type={this.props.inputType}
262 | value={this.state.maskedValue}
263 | onChange={this.handleChange}
264 | onFocus={this.handleFocus}
265 | onMouseUp={this.handleFocus}
266 | {...this.state.customProps}
267 | />
268 | )
269 | }
270 | }
271 |
272 |
273 |
274 | /**
275 | * Prop validation.
276 | * @see https://facebook.github.io/react/docs/component-specs.html#proptypes
277 | */
278 |
279 | CurrencyInput.propTypes = {
280 | onChange: PropTypes.func,
281 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
282 | decimalSeparator: PropTypes.string,
283 | thousandSeparator: PropTypes.string,
284 | precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
285 | inputType: PropTypes.string,
286 | allowNegative: PropTypes.bool,
287 | allowEmpty: PropTypes.bool,
288 | prefix: PropTypes.string,
289 | suffix: PropTypes.string,
290 | selectAllOnFocus: PropTypes.bool
291 | };
292 |
293 |
294 | CurrencyInput.defaultProps = {
295 | onChange: function(maskValue, value, event) {/*no-op*/},
296 | onChangeEvent: function(event, maskValue, value) {/*no-op*/},
297 | autoFocus: false,
298 | value: '0',
299 | decimalSeparator: '.',
300 | thousandSeparator: ',',
301 | precision: '2',
302 | inputType: 'text',
303 | allowNegative: false,
304 | prefix: '',
305 | suffix: '',
306 | selectAllOnFocus: false
307 | };
308 |
309 |
310 | export default CurrencyInput
311 |
--------------------------------------------------------------------------------
/src/mask.js:
--------------------------------------------------------------------------------
1 |
2 | export default function mask(value, precision = 2, decimalSeparator = '.', thousandSeparator = ',', allowNegative = false, prefix = '', suffix = ''){
3 | // provide some default values and arg validation.
4 | if (precision < 0) { precision = 0; } // precision cannot be negative
5 | if (precision > 20) { precision = 20; } // precision cannot be greater than 20
6 |
7 | if (value === null || value===undefined) {
8 | return {
9 | value: 0,
10 | maskedValue: ''
11 | };
12 | }
13 |
14 | value = String(value); //if the given value is a Number, let's convert into String to manipulate that
15 |
16 | if (value.length == 0) {
17 | return {
18 | value: 0,
19 | maskedValue: ''
20 | };
21 | }
22 |
23 |
24 | // extract digits. if no digits, fill in a zero.
25 | let digits = value.match(/\d/g) || ['0'];
26 |
27 | let numberIsNegative = false;
28 | if (allowNegative) {
29 | let negativeSignCount = (value.match(/-/g) || []).length;
30 | // number will be negative if we have an odd number of "-"
31 | // ideally, we should only ever have 0, 1 or 2 (positive number, making a number negative
32 | // and making a negative number positive, respectively)
33 | numberIsNegative = negativeSignCount % 2 === 1;
34 |
35 | // if every digit in the array is '0', then the number should never be negative
36 | let allDigitsAreZero = true;
37 | for (let idx=0; idx < digits.length; idx += 1) {
38 | if(digits[idx] !== '0') {
39 | allDigitsAreZero = false;
40 | break;
41 | }
42 | }
43 | if (allDigitsAreZero) {
44 | numberIsNegative = false;
45 | }
46 | }
47 |
48 | // zero-pad a input
49 | while (digits.length <= precision) { digits.unshift('0'); }
50 |
51 | if (precision > 0) {
52 | // add the decimal separator
53 | digits.splice(digits.length - precision, 0, ".");
54 | }
55 |
56 | // clean up extraneous digits like leading zeros.
57 | digits = Number(digits.join('')).toFixed(precision).split('');
58 | let raw = Number(digits.join(''));
59 |
60 | let decimalpos = digits.length - precision - 1; // -1 needed to position the decimal separator before the digits.
61 | if (precision > 0) {
62 | // set the final decimal separator
63 | digits[decimalpos] = decimalSeparator;
64 | } else {
65 | // when precision is 0, there is no decimal separator.
66 | decimalpos = digits.length;
67 | }
68 |
69 | // add in any thousand separators
70 | for (let x=decimalpos - 3; x > 0; x = x - 3) {
71 | digits.splice(x, 0, thousandSeparator);
72 | }
73 |
74 | // if we have a prefix or suffix, add them in.
75 | if (prefix.length > 0) { digits.unshift(prefix); }
76 | if (suffix.length > 0) { digits.push(suffix); }
77 |
78 | // if the number is negative, insert a "-" to
79 | // the front of the array and negate the raw value
80 | if (allowNegative && numberIsNegative) {
81 | digits.unshift('-');
82 | raw = -raw;
83 | }
84 |
85 | return {
86 | value: raw,
87 | maskedValue: digits.join('').trim()
88 | };
89 | }
90 |
--------------------------------------------------------------------------------
/src/object-assign-polyfill.js:
--------------------------------------------------------------------------------
1 | Object.assign = Object.assign ||
2 | function(target) {
3 | for (var i = 1; i < arguments.length; i++) {
4 | var source = arguments[i];
5 | for (var key in source) {
6 | if (Object.prototype.hasOwnProperty.call(source, key)) {
7 | target[key] = source[key];
8 | }
9 | }
10 | }
11 | return target;
12 | };
13 |
--------------------------------------------------------------------------------
/test/index.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import chai, {expect} from 'chai'
3 | import sinon from 'sinon'
4 | import sinonChai from 'sinon-chai'
5 | import CurrencyInput from '../src/index'
6 | import ReactDOM from 'react-dom';
7 | import ReactTestUtils from 'react-dom/test-utils';
8 | import setup from './setup'
9 |
10 | chai.use(sinonChai);
11 |
12 | describe('react-currency-input', function(){
13 |
14 | before('setup dom', function(){
15 | setup(); // setup the jsdom
16 | });
17 |
18 | describe('default arguments', function(){
19 |
20 | before('render and locate element', function() {
21 | this.renderedComponent = ReactTestUtils.renderIntoDocument(
22 |
23 | );
24 |
25 | this.inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
26 | this.renderedComponent,
27 | 'input'
28 | );
29 | });
30 |
31 | it(' should have masked value of "0.00"', function() {
32 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00')
33 | });
34 |
35 |
36 | it(' should be of type "text"', function() {
37 | expect(this.inputComponent.getAttribute('type')).to.equal('text')
38 | });
39 |
40 | it('does not auto-focus by default', function() {
41 | expect(this.renderedComponent.props.autoFocus).to.be.false
42 | });
43 | });
44 |
45 | describe('custom arguments', function(){
46 |
47 | before('render and locate element', function() {
48 | this.renderedComponent = ReactTestUtils.renderIntoDocument(
49 |
57 | );
58 |
59 | this.inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
60 | this.renderedComponent,
61 | 'input'
62 | );
63 | });
64 |
65 | it(' should have masked value of "123.456,789"', function() {
66 | expect(this.renderedComponent.getMaskedValue()).to.equal('123.456.789,000')
67 | });
68 |
69 | it(' should be of type "tel"', function() {
70 | expect(this.inputComponent.getAttribute('type')).to.equal('tel')
71 | });
72 |
73 | it('should be auto focused', function() {
74 | var focusedElement = document.activeElement;
75 | expect(focusedElement.getAttribute('id')).to.equal("currencyInput");
76 | });
77 | });
78 |
79 |
80 | describe('properly convert number value props into display values', function(){
81 |
82 | it('adds decimals to whole numbers to match precision', function() {
83 | var renderedComponent = ReactTestUtils.renderIntoDocument(
84 |
85 | );
86 | expect (renderedComponent.getMaskedValue()).to.equal('123,456,789.00')
87 | });
88 |
89 | it('Does not change value when precision matches', function() {
90 | var renderedComponent = ReactTestUtils.renderIntoDocument(
91 |
92 | );
93 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.89')
94 | });
95 |
96 |
97 | it('Rounds down properly when an number with extra decimals is passed in', function() {
98 | var renderedComponent = ReactTestUtils.renderIntoDocument(
99 |
100 | );
101 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.89')
102 | });
103 |
104 |
105 | it('Rounds up properly when an number with extra decimals is passed in', function() {
106 | var renderedComponent = ReactTestUtils.renderIntoDocument(
107 |
108 | );
109 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.90')
110 | });
111 |
112 | it('Rounds up the whole number when an number with extra decimals is passed in', function() {
113 | var renderedComponent = ReactTestUtils.renderIntoDocument(
114 |
115 | );
116 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,568')
117 | });
118 |
119 | it('it handles initial value as the integer 0,', function() {
120 | var renderedComponent = ReactTestUtils.renderIntoDocument(
121 |
122 | );
123 | expect (renderedComponent.getMaskedValue()).to.equal('0.00')
124 | });
125 |
126 | it('it handles initial value as the float 0.00,', function() {
127 | var renderedComponent = ReactTestUtils.renderIntoDocument(
128 |
129 | );
130 | expect (renderedComponent.getMaskedValue()).to.equal('0.00')
131 | });
132 |
133 | });
134 |
135 |
136 | describe('properly convert string value props into display values', function(){
137 |
138 | it('adds decimals to whole numbers to match precision', function() {
139 | var renderedComponent = ReactTestUtils.renderIntoDocument(
140 |
141 | );
142 | expect (renderedComponent.getMaskedValue()).to.equal('6,300.00')
143 | });
144 |
145 |
146 | it('Does not change value when precision matches', function() {
147 | var renderedComponent = ReactTestUtils.renderIntoDocument(
148 |
149 | );
150 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.89')
151 | });
152 |
153 |
154 | it('Rounds down properly when an number with extra decimals is passed in', function() {
155 | var renderedComponent = ReactTestUtils.renderIntoDocument(
156 |
157 | );
158 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.89')
159 | });
160 |
161 |
162 | it('Rounds up properly when an number with extra decimals is passed in', function() {
163 | var renderedComponent = ReactTestUtils.renderIntoDocument(
164 |
165 | );
166 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.90')
167 | });
168 |
169 |
170 | it('Rounds up the whole number when an number with extra decimals is passed in', function() {
171 | var renderedComponent = ReactTestUtils.renderIntoDocument(
172 |
173 | );
174 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,568')
175 | });
176 |
177 |
178 | it('Handles strings with separators', function() {
179 | var renderedComponent = ReactTestUtils.renderIntoDocument(
180 |
181 | );
182 | expect (renderedComponent.getMaskedValue()).to.equal('1,000.01')
183 | });
184 |
185 |
186 | it('Handles strings with prefixes', function() {
187 | var renderedComponent = ReactTestUtils.renderIntoDocument(
188 |
189 | );
190 | expect (renderedComponent.getMaskedValue()).to.equal('$10.01')
191 | });
192 |
193 | it('Handles strings with suffixes', function() {
194 | var renderedComponent = ReactTestUtils.renderIntoDocument(
195 |
196 | );
197 | expect (renderedComponent.getMaskedValue()).to.equal('10.01 kr')
198 | });
199 |
200 |
201 | it('Handles strings with custom separators', function() {
202 | var renderedComponent = ReactTestUtils.renderIntoDocument(
203 |
204 | );
205 | expect (renderedComponent.getMaskedValue()).to.equal('123.456.789,12')
206 | });
207 |
208 |
209 | it("Handles 1,234,567.89 format", function() {
210 | var renderedComponent = ReactTestUtils.renderIntoDocument(
211 |
212 | );
213 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567.89')
214 | });
215 |
216 |
217 | it("Handles 1 234 567.89 format", function() {
218 | var renderedComponent = ReactTestUtils.renderIntoDocument(
219 |
220 | );
221 | expect (renderedComponent.getMaskedValue()).to.equal('1 234 567.89')
222 | });
223 |
224 | it("Handles 1 234 567,89 format", function() {
225 | var renderedComponent = ReactTestUtils.renderIntoDocument(
226 |
227 | );
228 | expect (renderedComponent.getMaskedValue()).to.equal('1 234 567,89')
229 | });
230 |
231 | it("Handles 1,234,567·89 format", function() {
232 | var renderedComponent = ReactTestUtils.renderIntoDocument(
233 |
234 | );
235 | expect (renderedComponent.getMaskedValue()).to.equal('1,234,567·89')
236 | });
237 |
238 | it("Handles 1.234.567,89 format", function() {
239 | var renderedComponent = ReactTestUtils.renderIntoDocument(
240 |
241 | );
242 | expect (renderedComponent.getMaskedValue()).to.equal('1.234.567,89')
243 | });
244 |
245 | it("Handles 1˙234˙567,89 format", function() {
246 | var renderedComponent = ReactTestUtils.renderIntoDocument(
247 |
248 | );
249 | expect (renderedComponent.getMaskedValue()).to.equal('1˙234˙567,89')
250 | });
251 |
252 |
253 | it("Handles 1'234'567.89 format", function() {
254 | var renderedComponent = ReactTestUtils.renderIntoDocument(
255 |
256 | );
257 | expect (renderedComponent.getMaskedValue()).to.equal("1'234'567.89")
258 | });
259 |
260 |
261 |
262 | });
263 |
264 | describe('change events', function(){
265 |
266 | before('render and locate element', function() {
267 | this.handleChange = sinon.spy();
268 |
269 | this.renderedComponent = ReactTestUtils.renderIntoDocument(
270 |
271 | );
272 |
273 | this.inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
274 | this.renderedComponent,
275 | 'input'
276 | );
277 | });
278 |
279 | it('should call onChange', function() {
280 | this.inputComponent.value=123456789;
281 | ReactTestUtils.Simulate.change(this.inputComponent);
282 | expect(this.handleChange).to.have.been.calledWith("1,234,567.89", 1234567.89);
283 | });
284 |
285 |
286 | it('should change the masked value', function() {
287 | this.inputComponent.value=123456789;
288 | ReactTestUtils.Simulate.change(this.inputComponent);
289 | expect(this.renderedComponent.getMaskedValue()).to.equal("1,234,567.89");
290 | });
291 |
292 |
293 | it('should change the component value', function() {
294 | this.inputComponent.value=123456789;
295 | ReactTestUtils.Simulate.change(this.inputComponent);
296 | expect(this.inputComponent.value).to.equal("1,234,567.89");
297 | });
298 |
299 |
300 | });
301 |
302 |
303 | describe('negative numbers', function() {
304 |
305 | before('render and locate element', function() {
306 | this.renderedComponent = ReactTestUtils.renderIntoDocument(
307 |
308 | );
309 |
310 | this.inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
311 | this.renderedComponent,
312 | 'input'
313 | );
314 | });
315 |
316 | beforeEach('reset value to 0', function() {
317 | this.inputComponent.value = "0";
318 | ReactTestUtils.Simulate.change(this.inputComponent);
319 | });
320 |
321 | it('should render 0 without negative sign', function() {
322 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00');
323 | this.inputComponent.value = "-0"; ReactTestUtils.Simulate.change(this.inputComponent);
324 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00');
325 | });
326 |
327 | it('should render number with no or even number of "-" as positive', function() {
328 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00');
329 | this.inputComponent.value = "123456"; ReactTestUtils.Simulate.change(this.inputComponent);
330 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
331 | this.inputComponent.value = "--123456"; ReactTestUtils.Simulate.change(this.inputComponent);
332 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
333 | this.inputComponent.value = "123--456"; ReactTestUtils.Simulate.change(this.inputComponent);
334 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
335 | this.inputComponent.value = "123456--"; ReactTestUtils.Simulate.change(this.inputComponent);
336 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
337 | this.inputComponent.value = "--123--456--"; ReactTestUtils.Simulate.change(this.inputComponent);
338 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
339 | this.inputComponent.value = "123456----"; ReactTestUtils.Simulate.change(this.inputComponent);
340 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
341 | });
342 |
343 | it('should render number with odd number of "-" as negative', function() {
344 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00');
345 | this.inputComponent.value = "-123456"; ReactTestUtils.Simulate.change(this.inputComponent);
346 | expect(this.renderedComponent.getMaskedValue()).to.equal('-1,234.56');
347 | this.inputComponent.value = "123-456"; ReactTestUtils.Simulate.change(this.inputComponent);
348 | expect(this.renderedComponent.getMaskedValue()).to.equal('-1,234.56');
349 | this.inputComponent.value = "123456-"; ReactTestUtils.Simulate.change(this.inputComponent);
350 | expect(this.renderedComponent.getMaskedValue()).to.equal('-1,234.56');
351 | this.inputComponent.value = "-123-456-"; ReactTestUtils.Simulate.change(this.inputComponent);
352 | expect(this.renderedComponent.getMaskedValue()).to.equal('-1,234.56');
353 | });
354 |
355 | it('should correctly change between negative and positive numbers', function() {
356 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00');
357 | this.inputComponent.value = "123456"; ReactTestUtils.Simulate.change(this.inputComponent);
358 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
359 | this.inputComponent.value = "1,234.56-"; ReactTestUtils.Simulate.change(this.inputComponent);
360 | expect(this.renderedComponent.getMaskedValue()).to.equal('-1,234.56');
361 | this.inputComponent.value = "-1,234.56-"; ReactTestUtils.Simulate.change(this.inputComponent);
362 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
363 | this.inputComponent.value = "1-,234.56"; ReactTestUtils.Simulate.change(this.inputComponent);
364 | expect(this.renderedComponent.getMaskedValue()).to.equal('-1,234.56');
365 | this.inputComponent.value = "-1,234.-56"; ReactTestUtils.Simulate.change(this.inputComponent);
366 | expect(this.renderedComponent.getMaskedValue()).to.equal('1,234.56');
367 | });
368 |
369 | });
370 |
371 |
372 | describe('currency prefix', function() {
373 |
374 | before('render and locate element', function () {
375 | this.renderedComponent = ReactTestUtils.renderIntoDocument(
376 |
377 | );
378 |
379 | this.inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
380 | this.renderedComponent,
381 | 'input'
382 | );
383 | });
384 |
385 | it('should render the prefix', function() {
386 | expect(this.renderedComponent.getMaskedValue()).to.equal('$0.00');
387 | });
388 |
389 | });
390 |
391 | describe('currency suffix', function() {
392 |
393 | before('render and locate element', function () {
394 | this.renderedComponent = ReactTestUtils.renderIntoDocument(
395 |
396 | );
397 |
398 | this.inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
399 | this.renderedComponent,
400 | 'input'
401 | );
402 | });
403 |
404 | it('should render the suffix', function() {
405 | expect(this.renderedComponent.getMaskedValue()).to.equal('0.00 kr');
406 | });
407 |
408 | });
409 |
410 | describe('input selection', function() {
411 | let defaultProps = {
412 | allowNegative: true,
413 | handleChange: () => {},
414 | value: '0',
415 | prefix: '$',
416 | suffix: ' s'
417 | };
418 | let divElem;
419 | let renderComponent = function(props = {}) {
420 | divElem = document.createElement('div');
421 | document.body.appendChild(divElem);
422 |
423 | const componentProps = Object.assign({}, defaultProps, props);
424 |
425 | const renderedComponent = ReactDOM.render(
426 | ,
427 | divElem
428 | );
429 | const inputComponent = ReactTestUtils.findRenderedDOMComponentWithTag(
430 | renderedComponent,
431 | 'input'
432 | );
433 |
434 | inputComponent.value = "0";
435 | ReactTestUtils.Simulate.change(inputComponent);
436 |
437 | return { renderedComponent, inputComponent };
438 | };
439 |
440 | after('clean up dom', function() {
441 | document.body.removeChild(divElem);
442 | });
443 |
444 | it('sanity - renders "$0.00 s"', function() {
445 | const { renderedComponent } = renderComponent();
446 | expect(renderedComponent.getMaskedValue()).to.equal('$0.00 s');
447 | });
448 |
449 | it('should consider precision absence', function() {
450 | const { inputComponent } = renderComponent({ precision: 0 });
451 |
452 | expect(inputComponent.selectionStart).to.equal(2);
453 | expect(inputComponent.selectionEnd).to.equal(2);
454 | });
455 |
456 | xit('should highlight number on focus', function() {
457 | const { inputComponent } = renderComponent();
458 | ReactTestUtils.Simulate.focus(inputComponent);
459 | expect(inputComponent.selectionStart).to.equal(1);
460 | expect(inputComponent.selectionEnd).to.equal(5);
461 | });
462 |
463 | xit('should consider the negative sign when highlighting', function() {
464 | const { inputComponent } = renderComponent();
465 |
466 | inputComponent.value = '-4.35';
467 | ReactTestUtils.Simulate.change(inputComponent);
468 |
469 | ReactTestUtils.Simulate.focus(inputComponent);
470 | expect(inputComponent.selectionStart).to.equal(2);
471 | expect(inputComponent.selectionEnd).to.equal(6);
472 | });
473 |
474 | xit('should adjust start/end by 1 when entering a number', function() {
475 | const { inputComponent } = renderComponent();
476 |
477 | inputComponent.value = '134';
478 | ReactTestUtils.Simulate.change(inputComponent);
479 | ReactTestUtils.Simulate.focus(inputComponent);
480 |
481 | inputComponent.setSelectionRange(1, 1);
482 | inputComponent.value = '1234';
483 | ReactTestUtils.Simulate.change(inputComponent);
484 |
485 | expect(inputComponent.selectionStart).to.equal(2);
486 | expect(inputComponent.selectionEnd).to.equal(2);
487 | });
488 |
489 | });
490 |
491 | });
492 |
--------------------------------------------------------------------------------
/test/mask.spec.js:
--------------------------------------------------------------------------------
1 | import {expect} from 'chai'
2 | import mask from '../src/mask'
3 |
4 |
5 | describe('mask', function(){
6 |
7 | it('should return empty strings when value is not set"', function(){
8 | const {maskedValue, value} = mask();
9 |
10 | expect(maskedValue).to.equal("");
11 | expect(value).to.equal(0);
12 | });
13 |
14 | it('should return empty strings when value is empty string"', function(){
15 | const {maskedValue, value} = mask("");
16 |
17 | expect(maskedValue).to.equal("");
18 | expect(value).to.equal(0);
19 | });
20 |
21 | it('should return empty strings when value is null"', function(){
22 | const {maskedValue, value} = mask(null);
23 |
24 | expect(maskedValue).to.equal("");
25 | expect(value).to.equal(0);
26 | });
27 |
28 | it('should change "0" to "0.00"', function(){
29 | const {maskedValue, value} = mask("0");
30 |
31 | expect(maskedValue).to.equal("0.00");
32 | expect(value).to.equal(0);
33 | });
34 |
35 | it('should change "00" to "0.00"', function(){
36 | const {maskedValue, value} = mask("00");
37 |
38 | expect(maskedValue).to.equal("0.00");
39 | expect(value).to.equal(0);
40 | });
41 |
42 | it('should change "000" to "0.00"', function(){
43 | const {maskedValue, value} = mask("000");
44 | expect(maskedValue).to.equal("0.00");
45 | expect(value).to.equal(0);
46 | });
47 |
48 | it('should change "0000" to "0.00"', function(){
49 | const {maskedValue, value} = mask("0000");
50 | expect(maskedValue).to.equal("0.00");
51 | expect(value).to.equal(0);
52 | });
53 |
54 | it('should change "0001" to "0.01"', function(){
55 | const {maskedValue, value} = mask("0001");
56 | expect(maskedValue).to.equal("0.01");
57 | expect(value).to.equal(0.01);
58 | });
59 |
60 | it('should change "1001" to "10.01"', function(){
61 | const {maskedValue, value} = mask("1001");
62 | expect(maskedValue).to.equal("10.01");
63 | expect(value).to.equal(10.01);
64 | });
65 |
66 | it('should change "123456789" to "1,234,567.89"', function(){
67 | const {maskedValue, value} = mask("123456789");
68 | expect(maskedValue).to.equal("1,234,567.89");
69 | expect(value).to.equal(1234567.89);
70 | });
71 |
72 |
73 | describe('with separators', function(){
74 |
75 | it('decimal:"," thousand:"." should change "123456789" to "1.234.567,89"', function(){
76 | const {maskedValue, value} = mask("123456789", 2, ",", ".");
77 | expect(maskedValue).to.equal("1.234.567,89");
78 | expect(value).to.equal(1234567.89);
79 | });
80 |
81 | it('zero length thousand separator should change "123456789" to "1234567.89"', function(){
82 | const {maskedValue, value} = mask("123456789", 2, ".", "");
83 | expect(maskedValue).to.equal("1234567.89");
84 | expect(value).to.equal(1234567.89);
85 | });
86 |
87 | it('zero length decimal separator should change "123456789" to "1,234,56789"', function(){
88 | const {maskedValue, value} = mask("123456789", 2, "", ",");
89 | expect(maskedValue).to.equal("1,234,56789");
90 | expect(value).to.equal(1234567.89);
91 | });
92 |
93 | });
94 |
95 |
96 | describe('with precision', function(){
97 |
98 | it('set to string value "3" should change "123456789" to "123,456.789"', function(){
99 | const {maskedValue, value} = mask("123456789", "3");
100 | expect(maskedValue).to.equal("123,456.789");
101 | expect(value).to.equal(123456.789)
102 | });
103 |
104 | it('set to 3 should change "123456789" to "123,456.789"', function(){
105 | const {maskedValue, value} = mask("123456789", 3);
106 | expect(maskedValue).to.equal("123,456.789");
107 | expect(value).to.equal(123456.789);
108 | });
109 |
110 | it('set to 0 should change "123456789" to "123,456,789"', function(){
111 | const {maskedValue, value} = mask("123456789", 0);
112 | expect(maskedValue).to.equal("123,456,789");
113 | expect(value).to.equal(123456789);
114 | });
115 |
116 | });
117 |
118 |
119 | describe('negative numbers', function(){
120 |
121 | it('all "-" should be stripped out if allowNegative is false', function(){
122 | expect(mask("123456").maskedValue).to.equal("1,234.56");
123 | expect(mask("-123456").maskedValue).to.equal("1,234.56");
124 | expect(mask("--123456").maskedValue).to.equal("1,234.56");
125 | expect(mask("--123--456").maskedValue).to.equal("1,234.56");
126 | expect(mask("--123--456--").maskedValue).to.equal("1,234.56");
127 | });
128 |
129 | it('single "-" anywhere in the string should result in a negative masked number', function(){
130 | expect(mask("-123456", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
131 | expect(mask("123-456", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
132 | expect(mask("123456-", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
133 | });
134 |
135 | it('single "-" anywhere in the string should result in a negative unmasked number', function(){
136 | expect(mask("-123456", "2", ".", ",", true).value).to.equal(-1234.56);
137 | expect(mask("123-456", "2", ".", ",", true).value).to.equal(-1234.56);
138 | expect(mask("123456-", "2", ".", ",", true).value).to.equal(-1234.56);
139 | });
140 |
141 | it('no or even number of "-" should result in a positive number', function(){
142 | expect(mask("123456", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
143 | expect(mask("--123456", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
144 | expect(mask("123--456", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
145 | expect(mask("123456--", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
146 | expect(mask("--123456--", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
147 | expect(mask("--123--456--", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
148 | expect(mask("--1--234--56--", "2", ".", ",", true).maskedValue).to.equal("1,234.56");
149 | });
150 |
151 | it('odd number of "-" should result in a negative number', function(){
152 | expect(mask("-123456", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
153 | expect(mask("123-456", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
154 | expect(mask("123456-", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
155 | expect(mask("-123-456-", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
156 | expect(mask("-1-23-45-6-", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
157 | expect(mask("-1-2-3-4-5-6-", "2", ".", ",", true).maskedValue).to.equal("-1,234.56");
158 | });
159 |
160 | it('0 is never negative', function(){
161 | expect(mask("0", "2", ".", ",", true).maskedValue).to.equal("0.00");
162 | expect(mask("-0", "2", ".", ",", true).maskedValue).to.equal("0.00");
163 | expect(mask("-0-", "2", ".", ",", true).maskedValue).to.equal("0.00");
164 | expect(mask("--0-", "2", ".", ",", true).maskedValue).to.equal("0.00");
165 | });
166 |
167 | it('just "-" should result in 0.00', function(){
168 | expect(mask("-", "2", ".", ",", true).maskedValue).to.equal("0.00");
169 | });
170 |
171 | });
172 |
173 |
174 |
175 | describe('with currency symbol', function(){
176 |
177 | it('"$" prefix should change "0" to "$0.00"', function(){
178 | expect(mask("0","2",".",",",true,"$","").maskedValue).to.equal("$0.00");
179 | });
180 |
181 | it('"kr" suffix should change "0" to "0.00kr"', function(){
182 | expect(mask("0","2",".",",",true,"","kr").maskedValue).to.equal("0.00kr");
183 | });
184 |
185 | it('can have both a prefix and a suffix', function(){
186 | expect(mask("0","2",".",",",true,"$","kr").maskedValue).to.equal("$0.00kr");
187 | });
188 |
189 | it('does not strip whitespaces between amount and symbol', function(){
190 | expect(mask("0","2",".",",",true,"$ ","").maskedValue).to.equal("$ 0.00");
191 | expect(mask("0","2",".",",",true,""," kr").maskedValue).to.equal("0.00 kr");
192 | });
193 |
194 | it('strips whitespaces before and after value', function(){
195 | expect(mask("0","2",".",",",true," $ ","").maskedValue).to.equal("$ 0.00");
196 | expect(mask("0","2",".",",",true,""," kr ").maskedValue).to.equal("0.00 kr");
197 | });
198 |
199 |
200 | it('"-" should come before the prefix', function(){
201 | expect(mask("-20.00","2",".",",",true,"$","").maskedValue).to.equal("-$20.00");
202 | });
203 |
204 | });
205 |
206 |
207 |
208 | });
209 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | import jsdom from 'jsdom'
2 |
3 | export default function setup(markup){
4 |
5 |
6 | global.document = jsdom.jsdom(markup || '');
7 | global.window = document.defaultView;
8 |
9 | }
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: './examples/index.js',
6 | output: {
7 | path: path.resolve(__dirname, 'examples'),
8 | filename: 'bundle.js'
9 | },
10 | module: {
11 | loaders: [
12 | {
13 | test: /\.js$/,
14 | loader: 'babel-loader',
15 | query: {
16 | presets: ["es2015", "react", "stage-3"]
17 | }
18 | }
19 | ]
20 | },
21 | plugins: [
22 | new webpack.HotModuleReplacementPlugin()
23 | ],
24 | devServer: {
25 | hot: true,
26 | contentBase: './examples'
27 | },
28 | stats: {
29 | colors: true
30 | },
31 | devtool: 'source-map'
32 | };
33 |
--------------------------------------------------------------------------------