react-widgets-up
Version:
An à la carte set of polished, extensible, and accessible inputs built for React
154 lines (151 loc) • 5.46 kB
JavaScript
const _excluded = ["disabled", "readOnly", "placeholder", "innerRef", "min", "max", "localizer", "editing", "parse"];
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
import activeElement from 'dom-helpers/activeElement';
import canUseDOM from 'dom-helpers/canUseDOM';
import * as React from 'react';
import { createRef } from 'react';
import Input from './Input';
import { mergeRefs } from '@restart/hooks/useMergedRefs';
import { omitUndefined } from './omitUndefined';
let isSign = val => (val || '').trim() === '-';
function isPaddedZeros(str, localizer) {
let localeChar = localizer.decimalCharacter();
let [_, decimals] = str.split(localeChar);
return !!(decimals && decimals.match(/0+$/));
}
function isAtDelimiter(str, localizer) {
let localeChar = localizer.decimalCharacter();
let lastIndex = str.length - 1;
if (str.length < 1) return false;
let char = str[lastIndex];
return !!(char === localeChar && str.indexOf(char) === lastIndex);
}
const defaultProps = {
value: null,
editing: false
};
class NumberPickerInput extends React.Component {
constructor(...args) {
super(...args);
this.state = {};
this.input = /*#__PURE__*/createRef();
this.handleBlur = event => {
let str = this.state.stringValue;
let number = this.parseNumber(str);
// if number is below the min
// we need to flush low values and decimal stops, onBlur means i'm done inputing
if (this.isIntermediateValue(number, str)) {
if (isNaN(number)) {
number = null;
}
this.props.onChange(number, event);
}
};
this.handleChange = event => {
let {
value,
onChange
} = Object.assign({}, defaultProps, omitUndefined(this.props));
let stringValue = event.target.value,
numberValue = this.parseNumber(stringValue);
let isIntermediate = this.isIntermediateValue(numberValue, stringValue);
if (stringValue == null || stringValue.trim() === '') {
this.setStringValue('');
onChange(null, event);
return;
}
// order here matters a lot
if (isIntermediate) {
this.setStringValue(stringValue);
} else if (numberValue !== value) {
onChange(numberValue, event);
} else if (stringValue != this.state.stringValue) {
this.setStringValue(stringValue);
}
};
}
getSnapshotBeforeUpdate({
editing
}) {
return {
reselectText: !editing && this.props.editing && this.isSelectingAllText()
};
}
static getDerivedStateFromProps(nextProps, prevState) {
let {
value,
editing,
localizer
} = Object.assign({}, defaultProps, nextProps);
let decimal = localizer.decimalCharacter();
const stringValue = value == null || isNaN(value) ? '' : editing ? ('' + value).replace('.', decimal) : localizer.formatNumber(value /*, 'default'*/);
if (prevState.lastValueFromProps !== stringValue) return {
stringValue,
lastValueFromProps: stringValue
};
return null;
}
componentDidUpdate(_, __, {
reselectText
}) {
var _this$input$current;
if (reselectText) (_this$input$current = this.input.current) == null || _this$input$current.select();
}
// this intermediate state is for when one runs into
// the decimal or are typing the number
setStringValue(stringValue) {
this.setState({
stringValue
});
}
isIntermediateValue(num, str) {
let {
localizer,
min
} = this.props;
return !!(num < min || isSign(str) || isAtDelimiter(str, localizer) || isPaddedZeros(str, localizer));
}
isSelectingAllText() {
const node = canUseDOM && this.input.current;
return !!node && activeElement() === node && node.selectionStart === 0 && node.selectionEnd === node.value.length;
}
parseNumber(strVal) {
let {
localizer,
parse: userParse
} = this.props;
if (userParse) return userParse(strVal, localizer);
return localizer.parseNumber(strVal);
}
render() {
let _defaultProps$omitUnd = Object.assign({}, defaultProps, omitUndefined(this.props)),
{
disabled,
readOnly,
placeholder,
// eslint-disable-next-line react/prop-types
innerRef,
min,
max
// eslint-disable-next-line no-unused-vars
} = _defaultProps$omitUnd,
props = _objectWithoutPropertiesLoose(_defaultProps$omitUnd, _excluded);
let value = this.state.stringValue;
return /*#__PURE__*/React.createElement(Input, _extends({}, props, {
ref: mergeRefs(innerRef, this.input),
inputMode: "numeric",
className: "rw-widget-input",
onChange: this.handleChange,
onBlur: this.handleBlur,
"aria-valuenow": value /*HACK*/,
"aria-valuemin": isFinite(min) ? min : undefined,
"aria-valuemax": isFinite(max) ? max : undefined,
disabled: disabled,
readOnly: readOnly,
placeholder: placeholder,
value: value
}));
}
}
export default NumberPickerInput;