UNPKG

5.6 kBJavaScriptView Raw
1const _excluded = ["disabled", "readOnly", "placeholder", "innerRef", "min", "max", "localizer", "editing"];
2
3function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
4
5function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
6
7import activeElement from 'dom-helpers/activeElement';
8import canUseDOM from 'dom-helpers/canUseDOM';
9import PropTypes from 'prop-types';
10import React from 'react';
11import { findDOMNode } from 'react-dom';
12import Input from './Input';
13import * as CustomPropTypes from './PropTypes';
14
15let isSign = val => (val || '').trim() === '-';
16
17function isPaddedZeros(str, localizer) {
18 let localeChar = localizer.decimalCharacter();
19 let [_, decimals] = str.split(localeChar);
20 return !!(decimals && decimals.match(/0+$/));
21}
22
23function isAtDelimiter(str, localizer) {
24 let localeChar = localizer.decimalCharacter();
25 let lastIndex = str.length - 1;
26 if (str.length < 1) return false;
27 let char = str[lastIndex];
28 return !!(char === localeChar && str.indexOf(char) === lastIndex);
29}
30
31class NumberPickerInput extends React.Component {
32 constructor(...args) {
33 super(...args);
34 this.state = {};
35
36 this.handleBlur = event => {
37 let str = this.state.stringValue;
38 let number = this.parseNumber(str); // if number is below the min
39 // we need to flush low values and decimal stops, onBlur means i'm done inputing
40
41 if (this.isIntermediateValue(number, str)) {
42 if (isNaN(number)) {
43 number = null;
44 }
45
46 this.props.onChange(number, event);
47 }
48 };
49
50 this.handleChange = event => {
51 let {
52 value,
53 onChange
54 } = this.props;
55 let stringValue = event.target.value,
56 numberValue = this.parseNumber(stringValue);
57 let isIntermediate = this.isIntermediateValue(numberValue, stringValue);
58
59 if (stringValue == null || stringValue.trim() === '') {
60 this.setStringValue('');
61 onChange(null, event);
62 return;
63 } // order here matters a lot
64
65
66 if (isIntermediate) {
67 this.setStringValue(stringValue);
68 } else if (numberValue !== value) {
69 onChange(numberValue, event);
70 } else if (stringValue != this.state.stringValue) {
71 this.setStringValue(stringValue);
72 }
73 };
74 }
75
76 getSnapshotBeforeUpdate({
77 editing
78 }) {
79 return {
80 reselectText: !editing && this.props.editing && this.isSelectingAllText()
81 };
82 }
83
84 static getDerivedStateFromProps(nextProps, prevState) {
85 let {
86 value,
87 editing,
88 localizer
89 } = nextProps;
90 let decimal = localizer.decimalCharacter();
91 const stringValue = value == null || isNaN(value) ? '' : editing ? ('' + value).replace('.', decimal) : localizer.formatNumber(value
92 /*, 'default'*/
93 );
94 if (prevState.lastValueFromProps !== stringValue) return {
95 stringValue,
96 lastValueFromProps: stringValue
97 };
98 return null;
99 }
100
101 componentDidUpdate(_, __, {
102 reselectText
103 }) {
104 if (reselectText) findDOMNode(this).select();
105 } // this intermediate state is for when one runs into
106 // the decimal or are typing the number
107
108
109 setStringValue(stringValue) {
110 this.setState({
111 stringValue
112 });
113 }
114
115 isIntermediateValue(num, str) {
116 let {
117 localizer,
118 min
119 } = this.props;
120 return !!(num < min || isSign(str) || isAtDelimiter(str, localizer) || isPaddedZeros(str, localizer));
121 }
122
123 isSelectingAllText() {
124 const node = canUseDOM && findDOMNode(this);
125 return canUseDOM && activeElement() === node && node.selectionStart === 0 && node.selectionEnd === node.value.length;
126 }
127
128 parseNumber(strVal) {
129 let {
130 localizer,
131 parse: userParse
132 } = this.props;
133 if (userParse) return userParse(strVal, localizer);
134 return localizer.parseNumber(strVal);
135 }
136
137 render() {
138 let _this$props = this.props,
139 {
140 disabled,
141 readOnly,
142 placeholder,
143 // eslint-disable-next-line react/prop-types
144 innerRef,
145 min,
146 max
147 } = _this$props,
148 props = _objectWithoutPropertiesLoose(_this$props, _excluded);
149
150 let value = this.state.stringValue;
151 return /*#__PURE__*/React.createElement(Input, _extends({}, props, {
152 ref: innerRef,
153 inputMode: "numeric",
154 className: "rw-widget-input",
155 onChange: this.handleChange,
156 onBlur: this.handleBlur,
157 "aria-valuenow": value
158 /*HACK*/
159 ,
160 "aria-valuemin": isFinite(min) ? min : undefined,
161 "aria-valuemax": isFinite(max) ? max : undefined,
162 disabled: disabled,
163 readOnly: readOnly,
164 placeholder: placeholder,
165 value: value
166 }));
167 }
168
169}
170
171NumberPickerInput.defaultProps = {
172 value: null,
173 editing: false
174};
175NumberPickerInput.propTypes = {
176 value: PropTypes.number,
177 editing: PropTypes.bool,
178 placeholder: PropTypes.string,
179 localizer: PropTypes.object.isRequired,
180 parse: PropTypes.func,
181 min: PropTypes.number,
182 max: PropTypes.number,
183 disabled: CustomPropTypes.disabled,
184 readOnly: CustomPropTypes.disabled,
185 onChange: PropTypes.func.isRequired
186};
187export default NumberPickerInput;
\No newline at end of file