1 | import React, {Component} from 'react';
|
2 | import classnames from 'classnames';
|
3 | import InputGroup from 'bee-input-group';
|
4 | import FormControl from 'bee-form-control';
|
5 | import Message from 'bee-message';
|
6 | import PropTypes from 'prop-types';
|
7 | import i18n from './i18n';
|
8 | import { getComponentLocale } from 'bee-locale/build/tool';
|
9 |
|
10 | const propTypes = {
|
11 | max: PropTypes.number,
|
12 | min: PropTypes.number,
|
13 | step: PropTypes.number,
|
14 | autoWidth: PropTypes.bool,
|
15 | precision: PropTypes.number,
|
16 | format: PropTypes.func,
|
17 | delay: PropTypes.number,
|
18 | disabled:PropTypes.bool,
|
19 | toThousands:PropTypes.bool,
|
20 | locale:PropTypes.object,
|
21 | toNumber:PropTypes.bool,
|
22 | displayCheckPrompt:PropTypes.bool,
|
23 | minusRight:PropTypes.bool,
|
24 | handleBtnClick:PropTypes.func,
|
25 | };
|
26 |
|
27 | const defaultProps = {
|
28 | value: "",
|
29 | step: 1,
|
30 | clsPrefix: 'u-input-number',
|
31 | iconStyle: 'double',
|
32 | autoWidth: false,
|
33 | delay: 300,
|
34 | toNumber:false,
|
35 | displayCheckPrompt:false,
|
36 | locale:{},
|
37 | handleBtnClick:()=>{}
|
38 | };
|
39 |
|
40 |
|
41 |
|
42 | function prompt (content) {
|
43 | Message.destroy();
|
44 | Message.create({content: content, color: 'warninglight'});
|
45 | }
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | function toThousands(number) {
|
54 | if(number==='')return '';
|
55 | if(number==='0')return '0';
|
56 | let num = (number || 0).toString();
|
57 | let integer = num.split('.')[0];
|
58 | let decimal = num.split('.')[1]||'';
|
59 | let result = '';
|
60 | while (integer.length > 3) {
|
61 | result = ',' + integer.slice(-3) + result;
|
62 | integer = integer.slice(0, integer.length - 3);
|
63 | }
|
64 | if (integer) {
|
65 | result = integer + result ;
|
66 | if(num=='.'||num.indexOf('.')==num.length-1){
|
67 | result = result + '.'+decimal;
|
68 | }else if (decimal){
|
69 | result = result + '.'+decimal;
|
70 | }
|
71 | }
|
72 | if(result[0]=='-'){
|
73 | result = result.replace('-,','-')
|
74 | }
|
75 | return result;
|
76 | }
|
77 |
|
78 |
|
79 | function setCaretPosition(ctrl,pos,need) {
|
80 |
|
81 | if(ctrl&&need){
|
82 | if(ctrl.setSelectionRange) {
|
83 | ctrl.focus();
|
84 | ctrl.setSelectionRange(pos,pos);
|
85 |
|
86 | } else if(ctrl.createTextRange) {
|
87 | var range = ctrl.createTextRange();
|
88 | range.collapse(true);
|
89 | range.moveEnd('character', pos);
|
90 | range.moveStart('character', pos);
|
91 | range.select();
|
92 | }
|
93 |
|
94 | }
|
95 |
|
96 | }
|
97 |
|
98 |
|
99 |
|
100 | class InputNumber extends Component {
|
101 |
|
102 | constructor(props) {
|
103 | super(props);
|
104 |
|
105 |
|
106 | let data = this.judgeValue(props);
|
107 | this.state = {
|
108 | value:data.value,
|
109 | minusDisabled: data.minusDisabled,
|
110 | plusDisabled: data.plusDisabled,
|
111 | showValue:toThousands(data.value)
|
112 | }
|
113 |
|
114 | this.timer = null;
|
115 | this.focus = false;
|
116 | this.selectionStart = 0;
|
117 | }
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | |
127 |
|
128 |
|
129 |
|
130 |
|
131 | judgeValue = (props,oldValue)=> {
|
132 | let currentValue;
|
133 | let currentMinusDisabled = false;
|
134 | let currentPlusDisabled = false;
|
135 | let { value,min,max,precision,onChange,displayCheckPrompt } = props;
|
136 | if(props.minusRight){
|
137 | value = value.toString();
|
138 | if(value.indexOf('-')!=-1){
|
139 | value = value.replace('-','');
|
140 | value = '-'+value;
|
141 | }
|
142 | value = Number(value);
|
143 | }
|
144 | if ((value!=undefined)&&(value!=null)) {
|
145 | if(value===''){
|
146 | currentValue='';
|
147 | return {
|
148 | value: '',
|
149 | minusDisabled: false,
|
150 | plusDisabled: false
|
151 | }
|
152 | }else{
|
153 | currentValue = Number(value) ||0;
|
154 | }
|
155 | }
|
156 |
|
157 |
|
158 | else if(value==='0'||value===0){
|
159 | currentValue = 0;
|
160 | }else{
|
161 | if(oldValue||(oldValue===0)||(oldValue==='0')){
|
162 | currentValue = oldValue;
|
163 | }else{
|
164 | return {
|
165 | value: '',
|
166 | minusDisabled: false,
|
167 | plusDisabled: false
|
168 | }
|
169 | }
|
170 | }
|
171 | if(currentValue==-Infinity){
|
172 | return {
|
173 | value: min,
|
174 | minusDisabled: true,
|
175 | plusDisabled: false
|
176 | }
|
177 | }
|
178 | if(currentValue==Infinity){
|
179 | return {
|
180 | value: max,
|
181 | minusDisabled: false,
|
182 | plusDisabled: true
|
183 | }
|
184 | }
|
185 | const local = getComponentLocale(props, this.context, 'InputNumber', () => i18n);
|
186 | if (currentValue <= min) {
|
187 | if(displayCheckPrompt)prompt(local['msgMin']);
|
188 | currentMinusDisabled = true;
|
189 | currentValue=min;
|
190 | }
|
191 | if (currentValue >= max) {
|
192 | if(displayCheckPrompt)prompt(local['msgMax']);
|
193 | currentPlusDisabled = true;
|
194 | currentValue=max;
|
195 | }
|
196 |
|
197 | if(props.hasOwnProperty('precision')){
|
198 |
|
199 | currentValue = this.getPrecision(currentValue);
|
200 | }
|
201 | if(props.minusRight){
|
202 | currentValue = currentValue.toString();
|
203 | if(currentValue.indexOf('-')!=-1){
|
204 | currentValue = currentValue.replace('-','');
|
205 | currentValue = currentValue+'-';
|
206 | }
|
207 | }
|
208 |
|
209 | return {
|
210 | value: currentValue,
|
211 | minusDisabled: currentMinusDisabled,
|
212 | plusDisabled: currentPlusDisabled
|
213 | }
|
214 | }
|
215 |
|
216 | componentDidMount(){
|
217 | this.setState({
|
218 | value: this.props.value,
|
219 | showValue:toThousands(this.props.value)
|
220 | });
|
221 | }
|
222 | componentWillReceiveProps(nextProps){
|
223 | if(this.focus){
|
224 | if(nextProps.value==Infinity||nextProps.value==-Infinity){
|
225 |
|
226 | }else{
|
227 | this.setState({
|
228 | value: nextProps.value,
|
229 | showValue:toThousands(nextProps.value),
|
230 | });
|
231 | }
|
232 |
|
233 | }else{
|
234 | let data = this.judgeValue(nextProps,this.state.value);
|
235 | this.setState({
|
236 | value: data.value,
|
237 | showValue:toThousands(data.value),
|
238 | minusDisabled: data.minusDisabled,
|
239 | plusDisabled: data.plusDisabled
|
240 | });
|
241 | }
|
242 | }
|
243 |
|
244 | ComponentWillUnMount() {
|
245 | this.clear();
|
246 | }
|
247 |
|
248 | |
249 |
|
250 |
|
251 |
|
252 | numToFixed = (value,fixed,type) => {
|
253 | value = String(value);
|
254 | if(!value && value !== "0")return value;
|
255 | if(!fixed && String(fixed) !== "0")return value;
|
256 | let preIndex = value.indexOf(".");
|
257 | if(value.indexOf(".") === -1)return value;
|
258 | preIndex++;
|
259 | let endIndex = (preIndex+fixed);
|
260 | let precValue = value.substr(preIndex,endIndex)+"0000000000";
|
261 | if(type){
|
262 | return Number(value).toFixed(fixed);
|
263 | }
|
264 | return value.split(".")[0] +"."+ precValue.substr(0,fixed);
|
265 | }
|
266 |
|
267 | handleChange = (value) => {
|
268 | let selectionStart = this.input.selectionStart==undefined?this.input.input.selectionStart:this.input.selectionStart;
|
269 | this.selectionStart = selectionStart;
|
270 | const { onChange,toNumber,minusRight} = this.props;
|
271 | if(value===''){
|
272 | onChange && onChange(value);
|
273 | this.setState({
|
274 | value,
|
275 | showValue:''
|
276 | })
|
277 | return;
|
278 | }
|
279 |
|
280 | if(minusRight){
|
281 | if(value.match(/-/g)&&value.match(/-/g).length>1)return
|
282 | }
|
283 | if(isNaN(value)&&(value!=='.')&&(value!=='-'))return;
|
284 | if(value.indexOf(".") !== -1){
|
285 | let prec = String(value.split(".")[1]).replace("-","");
|
286 | if(this.props.precision === 0 && (prec ==="" || prec !=""))return;
|
287 | if(this.props.precision && prec.length > this.props.precision)return;
|
288 | if(prec.length > 8)return;
|
289 | }
|
290 | this.setState({
|
291 | value,
|
292 | showValue:toThousands(value),
|
293 | });
|
294 | if(value==='-'){
|
295 | onChange && onChange(value);
|
296 | }else if(value=='.'||value.indexOf('.')==value.length-1){
|
297 | onChange && onChange(value);
|
298 | }else if(value[value.indexOf('.')+1]==0){
|
299 | onChange && onChange(value);
|
300 | }else{
|
301 | toNumber?onChange && onChange(Number(value)):onChange && onChange(value);
|
302 | }
|
303 | if(this.props.toThousands){
|
304 | let stateShowValue = toThousands(this.state.value);
|
305 | let showValue = toThousands(value)
|
306 | let addNumber = 0;
|
307 | let delNumber = 0;
|
308 | let reg = /[0-9]/
|
309 | for(let i =0;i<selectionStart;i++){
|
310 | if(!reg.test(showValue[i]))addNumber+=1;
|
311 | }
|
312 | for(let j= 0;j<selectionStart;j++){
|
313 | if(stateShowValue[j]){
|
314 | if(!reg.test(stateShowValue[j]))delNumber+=1;
|
315 | }
|
316 | }
|
317 | let position = selectionStart+addNumber-delNumber;
|
318 | setCaretPosition(this.input&&this.input.input,position,true)
|
319 | }
|
320 | }
|
321 |
|
322 |
|
323 | handleFocus = (value,e) => {
|
324 | this.focus = true;
|
325 | let {onFocus, min, max } = this.props;
|
326 | onFocus && onFocus(this.getPrecision(this.state.value), e);
|
327 | }
|
328 | |
329 |
|
330 |
|
331 | getFullNum = (num)=>{
|
332 |
|
333 | if(isNaN(num)){return num};
|
334 |
|
335 |
|
336 | var str = ''+num;
|
337 | if(!/e/i.test(str)){return num;};
|
338 | let _precision = this.props.precision?this.props.precision:18;
|
339 | return (Number(num)).toFixed(_precision).replace(/\.?0+$/, "");
|
340 | }
|
341 |
|
342 | handleBlur = (v,e) => {
|
343 | this.focus = false;
|
344 | const {onBlur,precision,onChange,toNumber,max,min,displayCheckPrompt,minusRight,round } = this.props;
|
345 | const local = getComponentLocale(this.props, this.context, 'InputNumber', () => i18n);
|
346 | v = this.state.value;
|
347 | if(v==='' || !v){
|
348 | this.setState({
|
349 | value:v
|
350 | })
|
351 | onChange && onChange(v);
|
352 | onBlur && onBlur(v,e);
|
353 | return;
|
354 | }
|
355 |
|
356 | let value = this.numToFixed(v,precision,round);
|
357 | if(minusRight){
|
358 | if(value.indexOf('-')!=-1){
|
359 | value = value.replace('-','');
|
360 | value = '-'+value;
|
361 | }
|
362 | }
|
363 | value = isNaN(Number(value)) ? 0 : Number(value);
|
364 | if(value>max){
|
365 | if(displayCheckPrompt)prompt(local['msgMax']);
|
366 | value = max;
|
367 | }
|
368 | if(value<min){
|
369 | if(displayCheckPrompt)prompt(local['msgMin']);
|
370 | value = min;
|
371 | }
|
372 | if(this.props.hasOwnProperty('precision')){
|
373 |
|
374 | value = this.getPrecision(value);
|
375 | }
|
376 | value = value.toString();
|
377 | if(minusRight&&(value.indexOf('-')!=-1)){
|
378 | value = value.replace('-','');
|
379 | value = value+'-';
|
380 | }
|
381 | this.setState({
|
382 | value,
|
383 | showValue:toThousands(value)
|
384 | });
|
385 | this.detailDisable(value);
|
386 | if(toNumber&&(!minusRight)){
|
387 | onChange && onChange(value);
|
388 | onBlur && onBlur(value,e);
|
389 | }else{
|
390 | onChange && onChange(value);
|
391 | onBlur && onBlur(value,e);
|
392 | }
|
393 | }
|
394 | |
395 |
|
396 |
|
397 | detailDisable = (value) => {
|
398 | const { max, min, step } = this.props;
|
399 |
|
400 | if(value >= max || Number(value) + Number(step) > max){
|
401 | this.setState({
|
402 | plusDisabled: true
|
403 | })
|
404 | }else{
|
405 | this.setState({
|
406 | plusDisabled: false
|
407 | })
|
408 | }
|
409 | if(value <= min || value -step < min){
|
410 | this.setState({
|
411 | minusDisabled: true
|
412 | })
|
413 | }else{
|
414 | this.setState({
|
415 | minusDisabled: false
|
416 | })
|
417 | }
|
418 |
|
419 | }
|
420 | |
421 |
|
422 |
|
423 | minus = (value) => {
|
424 | const {min, max, step, onChange, toNumber} = this.props;
|
425 | value = (value === '-') ? 0 : value;
|
426 | if(typeof min === "undefined"){
|
427 | value = this.detail(value, step, 'reduce');
|
428 | }else{
|
429 | if(value < min){
|
430 | value = min;
|
431 | }else{
|
432 | let reducedValue = this.detail(value, step, 'reduce');
|
433 | if(reducedValue >= min){
|
434 | value = reducedValue;
|
435 | }
|
436 | }
|
437 | }
|
438 |
|
439 | if(value > max){
|
440 | value = max;
|
441 | }
|
442 |
|
443 | this.setState({
|
444 | value,
|
445 | showValue:toThousands(value)
|
446 | },()=>{
|
447 | this.input.input.focus&&this.input.input.focus()
|
448 | });
|
449 | toNumber?onChange && onChange(Number(value)):onChange && onChange(value);
|
450 | this.handleBtnClick('down',value);
|
451 | this.detailDisable(value);
|
452 | }
|
453 | |
454 |
|
455 |
|
456 | plus = (value) => {
|
457 | const {max, min, step, onChange, toNumber} = this.props;
|
458 | value = (value === '-') ? 0 : value;
|
459 | if(typeof max === "undefined"){
|
460 | value = this.detail(value, step, 'add');
|
461 | }else{
|
462 | if(value > max){
|
463 | value = max;
|
464 | }else{
|
465 | let addedValue = this.detail(value, step, 'add');
|
466 | if(addedValue <= max){
|
467 | value = addedValue;
|
468 | }
|
469 | }
|
470 | }
|
471 | if(value < min){
|
472 | value = min;
|
473 | }
|
474 | this.setState({
|
475 | value,
|
476 | showValue:toThousands(value)
|
477 | },()=>{
|
478 | this.input.input.focus&&this.input.input.focus()
|
479 | });
|
480 | toNumber?onChange && onChange(Number(value)):onChange && onChange(value);
|
481 | this.handleBtnClick('up',value);
|
482 | this.detailDisable(value);
|
483 | }
|
484 |
|
485 |
|
486 | detail = (value, step, type) => {
|
487 | let {precision} = this.props;
|
488 |
|
489 | let valueFloat = this.separate(value);
|
490 | let stepFloat = this.separate(step);
|
491 |
|
492 | let ans;
|
493 | let stepFloatLength = stepFloat.toString().length;
|
494 | let valueFloatLength = valueFloat.toString().length;
|
495 |
|
496 | if (typeof precision === 'undefined') {
|
497 | precision = Math.max(stepFloatLength, valueFloatLength);
|
498 | }
|
499 | let coefficient = Math.pow(10, Math.abs(stepFloatLength - valueFloatLength));
|
500 | if (type === 'add') {
|
501 | ans = (value * coefficient + step * coefficient) / coefficient;
|
502 | } else {
|
503 | ans = (value * coefficient - step * coefficient) / coefficient;
|
504 | }
|
505 |
|
506 | return ans.toFixed(precision);
|
507 |
|
508 | }
|
509 |
|
510 | |
511 |
|
512 |
|
513 |
|
514 |
|
515 | separate = (value) => {
|
516 | if(value==null||value==undefined){
|
517 | return ""
|
518 | }else{
|
519 | value = value.toString();
|
520 | if(value.indexOf('.') > -1){
|
521 | return value.split('.')[1];
|
522 | }else{
|
523 | return ""
|
524 | }
|
525 | }
|
526 | }
|
527 |
|
528 |
|
529 |
|
530 | clear = () => {
|
531 | if (this.timer) {
|
532 | clearTimeout(this.timer);
|
533 | }
|
534 | }
|
535 |
|
536 | handlePlusMouseDown = (e) => {
|
537 | e.preventDefault && e.preventDefault();
|
538 | let {delay,disabled} = this.props;
|
539 | let {value} = this.state;
|
540 | if(disabled)return;
|
541 | this.plus(value);
|
542 | this.clear();
|
543 | this.timer = setTimeout(() => {
|
544 | this.handlePlusMouseDown(e);
|
545 | }, delay);
|
546 | }
|
547 |
|
548 | handleReduceMouseDown = (e) => {
|
549 | e.preventDefault && e.preventDefault();
|
550 | let {delay,disabled} = this.props;
|
551 | let {value} = this.state;
|
552 | if(disabled)return;
|
553 | this.minus(value);
|
554 | this.clear();
|
555 | this.timer = setTimeout(() => {
|
556 | this.handleReduceMouseDown(e);
|
557 | }, delay);
|
558 | }
|
559 |
|
560 | getPrecision = (value)=>{
|
561 | if(value==null||value==undefined)return value;
|
562 | if(!value && value === "")return value;
|
563 | value = String(value);
|
564 | value = value.indexOf("e") !== -1?this.getFullNum(value):value;
|
565 | const {precision} = this.props;
|
566 | if(precision === 0)return value;
|
567 | if (precision == undefined || (value.indexOf(".") !== -1 && String(value.split(".")[1]).length === precision)) {
|
568 | return value;
|
569 | }
|
570 | let before = value.substring(0,1),len = value.length,
|
571 | after = value.substring(len-1,len);
|
572 | before = before === "-"?before:"";
|
573 | after = after === "-"?after:"";
|
574 |
|
575 | if(before)value = value.substring(1,len-1);
|
576 | if(after)value = value.substring(0,len-1);
|
577 |
|
578 | let precV = "000000000000000000000000000000000000000000000000000000000000000000000000";
|
579 | if(value.indexOf(".") === -1){
|
580 | precV = precV.substr(0,precision);
|
581 | precV = precV?"."+precV:precV;
|
582 | if((!isNaN(value))&&(value.indexOf('-')!=-1||value.indexOf('+')!=-1)&&(value.indexOf('e')!=-1)){
|
583 |
|
584 | }else{
|
585 | value = value + precV;
|
586 | }
|
587 | }
|
588 | return before+Number(value).toFixed(precision)+after;
|
589 | }
|
590 |
|
591 | handleBtnClick = (type,value)=>{
|
592 | this.props.handleBtnClick(type,value)
|
593 | }
|
594 |
|
595 | render() {
|
596 | const {toThousands,minusRight, max, min, step,disabled, clsPrefix, className, delay, onBlur, onFocus, iconStyle, autoWidth, onChange, format, precision,toNumber, ...others} = this.props;
|
597 | let classes = {
|
598 | [`${clsPrefix}-auto`]: autoWidth,
|
599 | [`${clsPrefix}`]: true,
|
600 | [`${clsPrefix}-lg`]: others.size === "lg",
|
601 | [`${clsPrefix}-sm`]: others.size === "sm",
|
602 | };
|
603 |
|
604 | let {value, minusDisabled, plusDisabled, showValue} = this.state;
|
605 | value = precision != null && !this.focus?this.getPrecision(value):value;
|
606 | value = format && !this.focus? format(value) : value;
|
607 | value = String(value).indexOf("e") !== -1?this.getFullNum(value):value;
|
608 | if(minusRight && String(value).indexOf('-')!=-1){
|
609 | value = String(value).replace("-","")+"-";
|
610 | }
|
611 | let disabledCursor = disabled? ' disabled-cursor':'';
|
612 | let disabledCon = disabled? ' disabled-con':'';
|
613 | return (
|
614 | <div className={`${clsPrefix}-out`}>
|
615 | {
|
616 | iconStyle === 'double' ? (
|
617 | <InputGroup className={classnames(className, classes,disabledCon)}>
|
618 | <InputGroup.Addon
|
619 |
|
620 | className={(minusDisabled && 'disabled' ) + disabledCursor}
|
621 | onMouseDown={ this.handleReduceMouseDown}
|
622 | onMouseLeave={ this.clear }
|
623 | onMouseUp={ this.clear }>
|
624 | -
|
625 | </InputGroup.Addon>
|
626 | <FormControl
|
627 | {...others}
|
628 | value={toThousands?showValue:value}
|
629 | disabled={disabled}
|
630 | onBlur={ this.handleBlur }
|
631 | onFocus={this.handleFocus}
|
632 | onChange={ this.handleChange }
|
633 | ref={ref=>this.input = ref}
|
634 | />
|
635 | <InputGroup.Addon
|
636 |
|
637 | className={(plusDisabled && 'disabled' ) + disabledCursor}
|
638 | onMouseDown={ this.handlePlusMouseDown}
|
639 | onMouseLeave={ this.clear }
|
640 | onMouseUp={ this.clear }>
|
641 | +
|
642 | </InputGroup.Addon>
|
643 | </InputGroup>
|
644 | ) : (
|
645 | <InputGroup
|
646 | className={classnames(className, classes,disabledCon)}
|
647 | simple
|
648 | >
|
649 | <FormControl
|
650 | {...others}
|
651 | value={toThousands?showValue:value}
|
652 | disabled={disabled}
|
653 | onBlur={ this.handleBlur }
|
654 | onFocus={this.handleFocus}
|
655 | onChange={ this.handleChange }
|
656 | ref={ref=>this.input = ref}
|
657 | />
|
658 | <InputGroup.Button>
|
659 | <div className={classnames("icon-group")}>
|
660 | <span
|
661 |
|
662 | onMouseDown={ this.handlePlusMouseDown}
|
663 | onMouseLeave={ this.clear }
|
664 | onMouseUp={ this.clear }
|
665 | className={classnames('plus',{'disabled': plusDisabled,'disabled-cursor':disabledCursor})}>
|
666 | <span className="uf uf-arrow-up"/>
|
667 | </span>
|
668 | <span
|
669 |
|
670 | onMouseDown={ this.handleReduceMouseDown}
|
671 | onMouseLeave={ this.clear }
|
672 | onMouseUp={ this.clear }
|
673 | className={classnames("reduce",{'disabled': minusDisabled,'disabled-cursor':disabledCursor})}>
|
674 | <span className=" uf uf-arrow-down"/>
|
675 | </span>
|
676 | </div>
|
677 | </InputGroup.Button>
|
678 | </InputGroup>
|
679 | )
|
680 | }
|
681 | </div>
|
682 | );
|
683 | }
|
684 | }
|
685 | ;
|
686 |
|
687 | InputNumber.defaultProps = defaultProps;
|
688 | InputNumber.propTypes = propTypes;
|
689 | InputNumber.contextTypes = {
|
690 | beeLocale: PropTypes.object
|
691 | };
|
692 | export default InputNumber;
|