├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── dist
└── index.js
├── example
├── example.bundle.js
├── example.js
└── index.html
├── karma.conf.babel.js
├── karma.conf.js
├── package.json
├── src
├── __tests__
│ └── react-numeral-input-test.js
├── index.js
└── react-numeral-input.js
├── tests.webpack.js
├── webpack.config.js
└── webpack.example.config.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | // I want to use babel-eslint for parsing!
3 | "parser": "babel-eslint",
4 |
5 | "env": {
6 | "browser": true,
7 | "node": true,
8 | "jasmine": true
9 | },
10 |
11 | // To give you an idea how to override rule options:
12 | "rules": {
13 | "quotes": [2, "single"],
14 | "strict": [2, "never"],
15 | "eol-last": [0],
16 | "no-mixed-requires": [0],
17 | "no-underscore-dangle": [0],
18 | "no-use-before-define": [0],
19 |
20 | "no-alert": 0,
21 |
22 | // react
23 | "react/display-name": 0,
24 | "react/jsx-boolean-value": 1,
25 | "react/jsx-quotes": 0,
26 | "react/jsx-no-undef": 1,
27 | "react/jsx-sort-props": 0,
28 | "react/jsx-sort-prop-types": 0,
29 | "react/jsx-uses-react": 1,
30 | "react/jsx-uses-vars": 1,
31 | "react/no-did-mount-set-state": 1,
32 | "react/no-did-update-set-state": 1,
33 | "react/no-multi-comp": 0,
34 | "react/no-unknown-property": 1,
35 | "react/prop-types": 0,
36 | "react/react-in-jsx-scope": 1,
37 | "react/self-closing-comp": 1,
38 | "react/wrap-multilines": 1
39 | },
40 |
41 | "ecmaFeatures": {
42 | "jsx": true
43 | },
44 |
45 | "plugins": [
46 | "react"
47 | ]
48 | }
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Compiled binary addons (http://nodejs.org/api/addons.html)
6 | build/Release
7 | node_modules
8 | /coverage
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: node_js
4 | node_js:
5 | - stable
6 |
7 | env:
8 | - BROWSER=ChromeCi
9 | - BROWSER=Firefox
10 |
11 | before_install:
12 | - export CHROME_BIN=chromium-browser
13 | - export DISPLAY=:99.0
14 | - sh -e /etc/init.d/xvfb start
15 |
16 | script:
17 | - npm test
18 | - npm run build
19 |
20 | branches:
21 | only:
22 | - master
23 |
24 | notifications:
25 | email:
26 | - blackbing@gmail.com
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Bingo Yang
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.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-numeral-input [](https://badge.fury.io/js/react-numeral-input) [](https://travis-ci.org/blackbing/react-numeral-input)
2 |
3 | It is a very tiny component which is a replacement of HTML input element for post-editing format of number values.
4 |
5 | ex. 1000000 -> 1,000,000
6 |
7 | [](http://i.imgur.com/7eUVb7z.gif)
8 |
9 | use Number Keyboard on device
10 |
11 | [](https://d.pr/free/i/xfL3Ok.png)
12 |
13 | # Live Demo
14 |
15 | [react-numeral-input](http://blackbing.github.io/react-numeral-input/)
16 |
17 | # Dependency
18 |
19 | * React.js
20 | * [Numeral.js](http://numeraljs.com/)
21 |
22 | # install
23 |
24 | ```shell
25 | npm install react-numeral-input
26 | ```
27 |
28 | # Usage
29 |
30 | ```jsx
31 | // replace original input from
32 |
33 |
34 | // like this
35 |
36 | ```
37 |
38 | # Example
39 |
40 | ```jsx
41 | let NumeralInput = require('react-numeral-input');
42 |
43 | module.exports = React.createClass({
44 | getInitialState() {
45 | return {
46 | numeralVal: 1000000
47 | }
48 | },
49 | onChange(val){
50 | this.setState( {numeralVal:val});
51 | },
52 | render() {
53 | return (
54 |
59 | )
60 | }
61 | });
62 | ```
63 |
64 |
65 | # Options
66 | You can set any original input props. such as minlength, maxlength. For example:
67 |
68 | ```jsx
69 |
70 | ```
71 |
72 | ### fmt(:string)
73 |
74 | Default: "0,0"
75 |
76 | It is passed to configure numeral format, You can find more information from [Numeral.js](http://numeraljs.com/).
77 |
78 | ### onChange(:function)
79 |
80 | Callback when value is changed, you will receieve unformated number (1000000 instead of 1,000,000).
81 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | !function(root,factory){"object"==typeof exports&&"object"==typeof module?module.exports=factory(require("react"),require("react-dom"),require("numeral")):"function"==typeof define&&define.amd?define(["react","react-dom","numeral"],factory):"object"==typeof exports?exports.NumeralInput=factory(require("react"),require("react-dom"),require("numeral")):root.NumeralInput=factory(root.react,root["react-dom"],root.numeral)}(this,function(__WEBPACK_EXTERNAL_MODULE_20__,__WEBPACK_EXTERNAL_MODULE_21__,__WEBPACK_EXTERNAL_MODULE_22__){return function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={exports:{},id:moduleId,loaded:!1};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.loaded=!0,module.exports}var installedModules={};return __webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.p="",__webpack_require__(0)}([function(module,exports,__webpack_require__){"use strict";module.exports=__webpack_require__(1)},function(module,exports,__webpack_require__){"use strict";var _objectWithoutProperties=__webpack_require__(2)["default"],_extends=__webpack_require__(3)["default"],_interopRequireDefault=__webpack_require__(19)["default"];Object.defineProperty(exports,"__esModule",{value:!0});var _react=__webpack_require__(20),_react2=_interopRequireDefault(_react),_reactDom=__webpack_require__(21),_reactDom2=_interopRequireDefault(_reactDom),_numeral=__webpack_require__(22),_numeral2=_interopRequireDefault(_numeral),reg=/[^0-9km,]+/g,default_fmt="0,0",getCaretPosition=function(oField){var fmt=arguments.length<=1||void 0===arguments[1]?default_fmt:arguments[1],iCaretPos=0,prefix=reg.exec(fmt);return prefix&&prefix.length&&(iCaretPos+=prefix[0].length),document.selection?(oField.focus(),oSel=document.selection.createRange(),oSel.moveStart("character",-oField.value.length),iCaretPos=oSel.text.length):(oField.selectionStart||"0"==oField.selectionStart)&&(iCaretPos=oField.selectionStart),iCaretPos},setCaretPosition=function(oField,index){oField.setSelectionRange?oField.setSelectionRange(index,index):(range=oField.createTextRange(),range.collapse(!0),range.moveEnd("character",index),range.moveStart("character",index),range.select())},NumeralInput=_react2["default"].createClass({displayName:"NumeralInput",propTypes:{onChange:_react2["default"].PropTypes.func,fmt:_react2["default"].PropTypes.string},getDefaultProps:function(){return{fmt:default_fmt}},formatPos:function(val,index){val=_numeral2["default"]().unformat(val),val=_numeral2["default"](val).format(this.props.fmt);var sub=val.substr(0,index-1),dotCount=sub.split(",").length,pos=index-dotCount;return 0>pos&&(pos=0),pos},focusOnChar:function(val,index){for(var formatVal=_numeral2["default"](val).format(this.props.fmt),dotCount=0,i=0,finalIndex=formatVal.length;i(val+"").length?this.focusOnChar(val,--pos):this.focusOnChar(val,pos)}val=_numeral2["default"](val).value(),this.setState({pos:pos,value:val||""},function(){_this2.props.onChange&&_this2.props.onChange(val)})},render:function(){var _props=this.props,rest=(_props.fmt,_objectWithoutProperties(_props,["fmt"]));return _react2["default"].createElement("input",_extends({type:"tel"},rest,{value:this.state.value,onChange:this.changeHandler}))}});exports["default"]=NumeralInput,module.exports=exports["default"]},function(module,exports){"use strict";exports["default"]=function(obj,keys){var target={};for(var i in obj)keys.indexOf(i)>=0||Object.prototype.hasOwnProperty.call(obj,i)&&(target[i]=obj[i]);return target},exports.__esModule=!0},function(module,exports,__webpack_require__){"use strict";var _Object$assign=__webpack_require__(4)["default"];exports["default"]=_Object$assign||function(target){for(var i=1;iindex;)for(var key,S=IObject($$[index++]),keys=getSymbols?getKeys(S).concat(getSymbols(S)):getKeys(S),length=keys.length,j=0;length>j;)isEnum.call(S,key=keys[j++])&&(T[key]=S[key]);return T}:Object.assign},function(module,exports){var $Object=Object;module.exports={create:$Object.create,getProto:$Object.getPrototypeOf,isEnum:{}.propertyIsEnumerable,getDesc:$Object.getOwnPropertyDescriptor,setDesc:$Object.defineProperty,setDescs:$Object.defineProperties,getKeys:$Object.keys,getNames:$Object.getOwnPropertyNames,getSymbols:$Object.getOwnPropertySymbols,each:[].forEach}},function(module,exports,__webpack_require__){var defined=__webpack_require__(15);module.exports=function(it){return Object(defined(it))}},function(module,exports){module.exports=function(it){if(void 0==it)throw TypeError("Can't call method on "+it);return it}},function(module,exports,__webpack_require__){var cof=__webpack_require__(17);module.exports=Object("z").propertyIsEnumerable(0)?Object:function(it){return"String"==cof(it)?it.split(""):Object(it)}},function(module,exports){var toString={}.toString;module.exports=function(it){return toString.call(it).slice(8,-1)}},function(module,exports){module.exports=function(exec){try{return!!exec()}catch(e){return!0}}},function(module,exports){"use strict";exports["default"]=function(obj){return obj&&obj.__esModule?obj:{"default":obj}},exports.__esModule=!0},function(module,exports){module.exports=__WEBPACK_EXTERNAL_MODULE_20__},function(module,exports){module.exports=__WEBPACK_EXTERNAL_MODULE_21__},function(module,exports){module.exports=__WEBPACK_EXTERNAL_MODULE_22__}])});
--------------------------------------------------------------------------------
/example/example.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 | var ReactDOM = require('react-dom');
3 | var numeral = require('numeral');
4 | var NumeralInput = React.createFactory(require('../src'));
5 | /* jshint undef:false */
6 | var Forms = React.createClass({
7 | getInitialState: function(){
8 | return {
9 | numeralVal: 1000000,
10 | numeralVal2: 2000000
11 | };
12 | },
13 | onChange: function(val){
14 | this.setState({numeralVal: val})
15 | },
16 | onChange2: function(val){
17 | this.setState({numeralVal2: val})
18 | },
19 | render: function(){
20 | return React.DOM.div( {},
21 | NumeralInput({
22 | value: this.state.numeralVal,
23 | className: "form-control input-lg",
24 | placeholder: "feed me number",
25 | onChange:this.onChange
26 | }),
27 | NumeralInput({
28 | value: this.state.numeralVal2,
29 | className: "form-control input-lg",
30 | placeholder: "feed me number",
31 | onChange:this.onChange2
32 | })
33 | )
34 | }
35 | });
36 | //React.render(React.createElement(ReactNumeralInput), document.getElementById('main'));
37 | ReactDOM.render( React.createElement(Forms) , document.querySelector('#app'));
38 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | react numeral input Demo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/karma.conf.babel.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import webpack from 'webpack'
3 |
4 | export default config => {
5 | const { env } = process
6 |
7 | const isCi = env.CONTINUOUS_INTEGRATION === 'true'
8 | //FIXME: temp remove coverage setting
9 | const runCoverage = false; //env.COVERAGE === 'true' || isCi
10 |
11 | const coverageLoaders = []
12 | const coverageReporters = []
13 |
14 | if (runCoverage) {
15 | coverageLoaders.push({
16 | test: /\.js$/,
17 | include: path.resolve('src/'),
18 | exclude: /__tests__/,
19 | loader: 'isparta'
20 | })
21 |
22 | coverageReporters.push('coverage')
23 |
24 | if (isCi) {
25 | coverageReporters.push('coveralls')
26 | }
27 | }
28 |
29 | config.set({
30 | frameworks: [ 'mocha' ],
31 |
32 | files: [ 'tests.webpack.js' ],
33 |
34 | preprocessors: {
35 | 'tests.webpack.js': [ 'webpack', 'sourcemap' ]
36 | },
37 |
38 | webpack: {
39 | module: {
40 | loaders: [
41 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel' },
42 | ...coverageLoaders
43 | ]
44 | },
45 | plugins: [
46 | new webpack.DefinePlugin({
47 | 'process.env.NODE_ENV': JSON.stringify('test')
48 | })
49 | ],
50 | devtool: 'inline-source-map'
51 | },
52 |
53 | webpackMiddleware: {
54 | noInfo: true
55 | },
56 |
57 | reporters: [ 'mocha', ...coverageReporters ],
58 |
59 | coverageReporter: {
60 | type: 'lcov',
61 | dir: 'coverage'
62 | },
63 |
64 | customLaunchers: {
65 | ChromeCi: {
66 | base: 'Chrome',
67 | flags: [ '--no-sandbox' ]
68 | }
69 | },
70 |
71 | browsers: isCi ? [ env.BROWSER ] : [ 'Chrome', 'Firefox' ],
72 |
73 | singleRun: isCi
74 | })
75 | }
76 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-core/register')
2 | module.exports = require('./karma.conf.babel.js')
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-numeral-input",
3 | "version": "0.4.3",
4 | "description": "react numeral input",
5 | "author": "Bingo Yang ",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/blackbing/react-numeral-input.git"
9 | },
10 | "homepage": "https://github.com/blackbing/react-numeral-input",
11 | "bugs": {
12 | "url": "https://github.com/blackbing/react-numeral-input/issues"
13 | },
14 | "dependencies": {},
15 | "devDependencies": {
16 | "babel": "^5.8.23",
17 | "babel-core": "~5.8.22",
18 | "babel-eslint": "^4.0.10",
19 | "babel-loader": "~5.3.2",
20 | "babel-runtime": "~5.8.20",
21 | "es5-shim": "~4.1.10",
22 | "eslint": "~2.2.0",
23 | "eslint-plugin-react": "^5.1.1",
24 | "expect": "^1.20.2",
25 | "karma": "^0.13.22",
26 | "karma-chrome-launcher": "^2.0.0",
27 | "karma-coverage": "^1.1.1",
28 | "karma-coveralls": "^1.1.2",
29 | "karma-firefox-launcher": "^1.0.0",
30 | "karma-mocha": "^1.2.0",
31 | "karma-mocha-reporter": "^2.2.0",
32 | "karma-phantomjs-launcher": "^1.0.0",
33 | "karma-sourcemap-loader": "~0.3.5",
34 | "karma-webpack": "~1.7.0",
35 | "mocha": "^3.1.0",
36 | "numeral": "^1.5.3",
37 | "phantomjs-prebuilt": "^2.1.7",
38 | "react": "^15.2.0",
39 | "react-addons-test-utils": "^15.2.0",
40 | "react-dom": "^15.2.0",
41 | "webpack": "~1.11.0",
42 | "webpack-dev-server": "~1.10.1"
43 | },
44 | "keywords": [
45 | "react",
46 | "react-component",
47 | "react-numeral-input",
48 | "numeral-input"
49 | ],
50 | "license": "MIT",
51 | "main": "dist/index.js",
52 | "peerDependencies": {
53 | "react": ">=15.0.0",
54 | "react-dom": "^15.0.0",
55 | "numeral": "^1.5"
56 | },
57 | "scripts": {
58 | "test": "eslint src/ && karma start --single-run",
59 | "watch-test": "karma start --auto-watch --no-single-run",
60 | "example": "webpack --config webpack.example.config.js && open example/index.html",
61 | "build": "webpack",
62 | "start": "webpack-dev-server --inline --hot"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/__tests__/react-numeral-input-test.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import TestUtils from 'react-addons-test-utils';
5 | import NumeralInput from '../react-numeral-input';
6 |
7 | describe('ReactNumeralInput', function() {
8 | var component;
9 | var component2;
10 |
11 | beforeEach(function() {
12 | component = TestUtils.renderIntoDocument(
13 |
14 | );
15 | component2 = TestUtils.renderIntoDocument(
16 |
17 | );
18 | });
19 |
20 | it('should render', function() {
21 | expect(ReactDOM.findDOMNode(component).type).toBe('tel');
22 | });
23 |
24 | it('should formatPos', function() {
25 | const value = '1,234,567';
26 | expect(component.formatPos(value, 0)).toBe(0);
27 | expect(component.formatPos(value, 1)).toBe(0);
28 | expect(component.formatPos(value, 2)).toBe(1);
29 | expect(component.formatPos(value, 3)).toBe(1);
30 | expect(component.formatPos(value, 4)).toBe(2);
31 | expect(component.formatPos(value, 5)).toBe(3);
32 | expect(component.formatPos(value, 6)).toBe(4);
33 | });
34 |
35 | it('should focusOnChar', function() {
36 | const value = '1234567';
37 | expect(component.focusOnChar(value, 0)).toBe(1);
38 | expect(component.focusOnChar(value, 1)).toBe(1);
39 | expect(component.focusOnChar(value, 3)).toBe(4);
40 | expect(component.focusOnChar(value, 4)).toBe(5);
41 | expect(component.focusOnChar(value, 5)).toBe(7);
42 | expect(component.focusOnChar(value, 6)).toBe(8);
43 | });
44 |
45 | it('should formatPos with fmt', function() {
46 | const value = '$1,234,567';
47 | expect(component2.formatPos(value, 0)).toBe(0);
48 | expect(component2.formatPos(value, 1)).toBe(0);
49 | expect(component2.formatPos(value, 2)).toBe(1);
50 | expect(component2.formatPos(value, 3)).toBe(2);
51 | expect(component2.formatPos(value, 4)).toBe(2);
52 | expect(component2.formatPos(value, 5)).toBe(3);
53 | expect(component2.formatPos(value, 6)).toBe(4);
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./react-numeral-input.js');
2 |
--------------------------------------------------------------------------------
/src/react-numeral-input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import numeral from 'numeral';
5 |
6 | const reg = /[^0-9km,]+/g;
7 | const default_fmt = '0,0';
8 |
9 | const getCaretPosition = function(oField, fmt = default_fmt) {
10 | let iCaretPos = 0;
11 | const prefix = reg.exec(fmt);
12 | if (prefix && prefix.length) {
13 | iCaretPos += prefix[0].length;
14 | }
15 | if(document.selection){
16 | oField.focus();
17 | oSel = document.selection.createRange();
18 | oSel.moveStart( 'character', -oField.value.length)
19 | iCaretPos = oSel.text.length;
20 | }else if (oField.selectionStart || oField.selectionStart == '0') {
21 | iCaretPos = oField.selectionStart;
22 | }
23 | return iCaretPos;
24 | };
25 |
26 | const setCaretPosition = function(oField, index) {
27 | if (oField.setSelectionRange) {
28 | oField.setSelectionRange(index, index);
29 | } else {
30 | range = oField.createTextRange();
31 | range.collapse(true);
32 | range.moveEnd('character', index);
33 | range.moveStart('character', index);
34 | range.select();
35 | }
36 | };
37 |
38 | const NumeralInput = React.createClass({
39 | displayName: 'NumeralInput',
40 | propTypes: {
41 | onChange: React.PropTypes.func,
42 | fmt: React.PropTypes.string
43 | },
44 | getDefaultProps: function() {
45 | return {
46 | fmt: default_fmt
47 | };
48 | },
49 | formatPos: function(val, index) {
50 | //unformat
51 | val = numeral().unformat(val);
52 | //format
53 | //
54 | val = numeral(val).format(this.props.fmt);
55 | let sub = val.substr(0, index-1);
56 | let dotCount = sub.split(',').length;
57 | let pos = index-dotCount;
58 | if (pos<0) {
59 | pos = 0;
60 | }
61 | return pos;
62 | },
63 | focusOnChar: function(val, index) {
64 | let formatVal = numeral(val).format(this.props.fmt);
65 | let dotCount=0;
66 |
67 | let i = 0;
68 | let finalIndex = formatVal.length;
69 | while( i < formatVal.length) {
70 | let char = formatVal[i];
71 | if( i === (index + dotCount)) {
72 | finalIndex = i;
73 | break
74 | }
75 | if( char === ','){
76 | dotCount++;
77 | }
78 | i++;
79 | }
80 | if (!finalIndex) {
81 | finalIndex = 1;
82 | }
83 | return finalIndex;
84 | },
85 | getInitialState: function() {
86 | return {
87 | inputStyle: this.props.inputStyle,
88 | placeholder: this.props.placeholder,
89 | value: this.getNumeralValue(this.props.value)
90 | };
91 | },
92 | getNumeralValue: function(val) {
93 | if (val) {
94 | return numeral(val).format(this.props.fmt);
95 | }
96 | return '';
97 | },
98 | componentWillReceiveProps: function(nextProps) {
99 | if( this.props.value === nextProps.value){
100 | return;
101 | }
102 | let val = nextProps.value;
103 | let formatVal = '';
104 |
105 | if (!reg.test(val)) {
106 | formatVal = this.getNumeralValue(val);
107 | }
108 | // formatVal = this.getNumeralValue(val);
109 |
110 | this.setState( {
111 | value: formatVal
112 | }, () => {
113 | const node = ReactDOM.findDOMNode(this);
114 | setCaretPosition(node, this.state.pos, this.props.fmt);
115 | });
116 | },
117 | changeHandler: function() {
118 | const node = ReactDOM.findDOMNode(this);
119 | let pos = getCaretPosition(node, this.props.fmt);
120 | let val = node.value;
121 | pos = this.formatPos(this.state.value, pos);
122 |
123 |
124 | //1,000,000 -> 1000000
125 | const reTest = reg.test(val);
126 | if (!reTest) {
127 | val = numeral(val).value();
128 | let oVal = numeral(this.state.val);
129 | if ((oVal+'').length < (val+'').length) {
130 | pos = this.focusOnChar(val, ++pos);
131 | } else if ((oVal+'').length > (val+'').length) {
132 | pos = this.focusOnChar(val, --pos);
133 | } else {
134 | pos = this.focusOnChar(val, pos);
135 | }
136 | }
137 | val = numeral(val).value();
138 |
139 | //parentNode onChange function
140 | this.setState( {
141 | pos: pos,
142 | value: val || ''
143 | }, () => {
144 | if (this.props.onChange) {
145 | this.props.onChange(val);
146 | }
147 | })
148 | },
149 | render: function() {
150 | const { fmt, ...rest} = this.props;
151 | return (
152 |
156 | );
157 | }
158 | });
159 |
160 | export default NumeralInput;
161 |
--------------------------------------------------------------------------------
/tests.webpack.js:
--------------------------------------------------------------------------------
1 | const context = require.context('./src', true, /-test\.js$/)
2 | context.keys().forEach(context)
3 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | var path = require('path');
3 | var webpack = require('webpack');
4 |
5 |
6 | module.exports = {
7 | context: path.join(__dirname),
8 | entry: './src/index.js',
9 |
10 | output: {
11 | path: path.join(__dirname),
12 | filename: 'dist/index.js',
13 | libraryTarget: 'umd',
14 | library: 'NumeralInput'
15 | },
16 |
17 | externals: {
18 | 'react': 'react',
19 | 'react-dom': 'react-dom',
20 | 'numeral': 'numeral'
21 | },
22 |
23 | module: {
24 | loaders: [
25 | {
26 | test: /(\.js)|(\.jsx)$/,
27 | exclude: /node_modules/,
28 | loader: 'babel',
29 | query: {
30 | optional: ['runtime'],
31 | stage: 0
32 | }
33 | }
34 | ]
35 | },
36 | plugins: [
37 | new webpack.optimize.UglifyJsPlugin({
38 | sourceMap: false,
39 | mangle: false
40 | })
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/webpack.example.config.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | var path = require('path');
3 | var webpack = require('webpack');
4 |
5 |
6 | module.exports = {
7 | context: path.join(__dirname),
8 | entry: './example/example.js',
9 |
10 | output: {
11 | path: path.join(__dirname),
12 | filename: './example/example.bundle.js',
13 | },
14 |
15 | module: {
16 | loaders: [
17 | {
18 | test: /(\.js)|(\.jsx)$/,
19 | exclude: /node_modules/,
20 | loader: 'babel',
21 | query: {
22 | optional: ['runtime'],
23 | stage: 0
24 | }
25 | }
26 | ]
27 | },
28 | plugins: [
29 | new webpack.optimize.UglifyJsPlugin({
30 | sourceMap: false,
31 | mangle: false
32 | })
33 | ]
34 | };
35 |
--------------------------------------------------------------------------------