@massds/mayflower-react
Version:
React versions of Mayflower design system UI components
283 lines • 10.7 kB
JavaScript
const _excluded = ["max", "min", "step", "name", "onChange", "onBlur", "placeholder", "width", "maxlength", "showButtons"];
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; }
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); }
/**
* InputNumber module.
* @module @massds/mayflower-react/InputNumber
* @requires module:@massds/mayflower-assets/scss/01-atoms/_input--button
* @requires module:@massds/mayflower-assets/scss/01-atoms/01-atoms/helper-text
* @requires ma__input--button('number');
*/
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import is from "is";
import Input from "../Input/index.mjs";
import Error from "../Input/error.mjs";
import { InputContext } from "../Input/context.mjs";
import { validNumber } from "../Input/validate.mjs";
import { countDecimals } from "../Input/utility.mjs";
import { numberCharacterPropTypeCheck } from "../utilities/componentPropTypeCheck.mjs";
const NumberInput = props => {
const ref = /*#__PURE__*/React.createRef();
const upRef = /*#__PURE__*/React.createRef();
const downRef = /*#__PURE__*/React.createRef();
return /*#__PURE__*/React.createElement(InputContext.Consumer, null, context => {
const hasValue = is.number(context.getValue());
const inputClasses = classNames({
'ma__input-number__control': true,
'js-is-required': props.required,
'ma__input-number__control--showButtons': props.showButtons || props.unit && hasValue
});
const unitClasses = classNames({
'ma__input-number-unit': true,
'ma__input-number-unit--disabled': props.disabled,
'ma__input-number-unit--showButtons': props.showButtons
});
const displayErrorMessage = val => {
const min = props.min,
max = props.max,
required = props.required;
if (required && !is.number(val)) {
const errorMsg = 'Please enter a value.';
return {
showError: true,
errorMsg: errorMsg
};
}
if (is.number(val)) {
const _validNumber = validNumber(val, min, max),
showError = _validNumber.showError,
errorMsg = _validNumber.errorMsg;
return {
showError: showError,
errorMsg: errorMsg
};
}
return {
showError: false,
errorMsg: ''
};
};
const hasNumberProperty = (obj, property) => Object.prototype.hasOwnProperty.call(obj, property) && is.number(obj[property]);
const handleOnBlur = e => {
e.persist();
const inputEl = ref.current;
let newValue = inputEl.value ? Number(inputEl.value) : inputEl.value;
if (hasNumberProperty(props, 'max') && newValue > props.max) {
newValue = props.max;
}
if (hasNumberProperty(props, 'min') && newValue < props.min) {
newValue = props.min;
}
if (is.number(newValue)) {
// Since to Fixed returns a string, we have to cast it back to a Number
newValue = Number(newValue.toFixed(countDecimals(props.step)));
const updateError = displayErrorMessage(newValue);
context.updateState(_extends({
value: newValue
}, updateError), () => {
if (is.fn(props.onBlur)) {
props.onBlur(e, newValue);
}
});
}
};
const handleChange = e => {
e.persist();
const inputEl = ref.current;
const newValue = inputEl.value ? Number(inputEl.value) : inputEl.value;
const updateError = displayErrorMessage(newValue);
context.updateState(_extends({
value: newValue
}, updateError), () => {
if (is.fn(props.onChange)) {
props.onChange(e, newValue, props.id);
}
});
};
const handleAdjust = e => {
e.persist();
let direction;
if (e.currentTarget === upRef.current) {
direction = 'up';
} else {
direction = 'down';
}
const inputEl = ref.current;
let newValue = inputEl.value ? Number(inputEl.value) : inputEl.value;
if (direction === 'up' && (!hasNumberProperty(props, 'max') || newValue < props.max)) {
// Since to Fixed returns a string, we have to cast it back to a Number
newValue = newValue ? Number((newValue + props.step).toFixed(countDecimals(props.step))) : props.step;
const updateError = displayErrorMessage(newValue);
context.updateState(_extends({
value: newValue
}, updateError), () => {
if (is.fn(props.onChange)) {
props.onChange(e, newValue, props.id);
}
});
} else if (direction === 'down' && (!hasNumberProperty(props, 'min') || newValue > props.min)) {
// Since to Fixed returns a string, we have to cast it back to a Number
newValue = newValue ? Number((newValue + props.step * -1).toFixed(countDecimals(props.step))) : props.step * -1;
const updateError = displayErrorMessage(newValue);
context.updateState(_extends({
value: newValue
}, updateError), () => {
if (is.fn(props.onChange)) {
props.onChange(e, newValue, props.id);
}
});
}
};
const inputAttr = {
className: inputClasses,
name: props.name,
id: props.id,
type: 'number',
placeholder: props.placeholder,
maxLength: Number(props.maxlength),
style: props.width ? {
width: props.width + "px"
} : null,
onChange: handleChange,
onBlur: handleOnBlur,
required: props.required,
disabled: props.disabled,
step: props.step,
ref: ref
};
inputAttr.value = context.getValue();
if (is.number(props.max)) {
inputAttr.max = props.max;
}
if (is.number(props.min)) {
inputAttr.min = props.min;
}
return /*#__PURE__*/React.createElement("div", {
className: "ma__input-number"
}, /*#__PURE__*/React.createElement("input", inputAttr), props.unit && hasValue ? /*#__PURE__*/React.createElement("span", {
className: unitClasses
}, props.unit) : null, props.showButtons && /*#__PURE__*/React.createElement("div", {
className: "ma__input-number__control-buttons"
}, /*#__PURE__*/React.createElement("button", {
type: "button",
"aria-label": "increase value",
className: "ma__input-number__control-plus",
"data-direction": "up",
onClick: handleAdjust,
disabled: props.disabled,
tabIndex: -1,
ref: upRef
}), /*#__PURE__*/React.createElement("button", {
type: "button",
"aria-label": "decrease value",
className: "ma__input-number__control-minus",
"data-direction": "down",
onClick: handleAdjust,
disabled: props.disabled,
tabIndex: -1,
ref: downRef
})));
});
};
NumberInput.propTypes = process.env.NODE_ENV !== "production" ? {
required: PropTypes.bool,
showButtons: PropTypes.bool,
unit: PropTypes.string,
disabled: PropTypes.bool,
min: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
max: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
step: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
onBlur: PropTypes.func,
onChange: PropTypes.func,
name: PropTypes.string,
id: PropTypes.string,
maxlength: PropTypes.number,
placeholder: PropTypes.string,
width: PropTypes.number
} : {};
const InputNumber = props => {
const max = props.max,
min = props.min,
step = props.step,
name = props.name,
onChange = props.onChange,
onBlur = props.onBlur,
placeholder = props.placeholder,
width = props.width,
maxlength = props.maxlength,
showButtons = props.showButtons,
inputProps = _objectWithoutPropertiesLoose(props, _excluded);
// Input and Number share the props.required, props.id and props.disabled values.
const numberProps = {
max: max,
min: min,
step: step,
name: name,
placeholder: placeholder,
width: width,
maxlength: maxlength,
required: props.required,
id: props.id,
onChange: onChange,
onBlur: onBlur,
disabled: props.disabled,
unit: props.unit,
showButtons: showButtons
};
return /*#__PURE__*/React.createElement(Input, inputProps, /*#__PURE__*/React.createElement(NumberInput, numberProps), /*#__PURE__*/React.createElement(Error, {
id: props.id
}));
};
InputNumber.propTypes = process.env.NODE_ENV !== "production" ? {
/** Whether the label should be hidden or not */
hiddenLabel: PropTypes.bool,
/** The label text for the input field, can be a string or a component */
labelText: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
/** Whether the field is required or not */
required: PropTypes.bool,
/** Whether the field is disabled or not */
disabled: PropTypes.bool,
/** The unique ID for the input field */
id: PropTypes.string.isRequired,
/** The name for the input field */
name: PropTypes.string.isRequired,
/** The max acceptable input length */
maxlength: PropTypes.number,
/** The pattern to filter input against, e.g. "[0-9]" for numbers only */
pattern: PropTypes.string,
/** The number of characters wide to make the input field */
width: PropTypes.number,
/** The placeholder text for the input field */
placeholder: PropTypes.string,
/** The message to be displayed in the event of an error. */
errorMsg: PropTypes.string,
/** Custom change function */
onChange: PropTypes.func,
/** Custom onBlur function */
onBlur: PropTypes.func,
/** Default input value */
defaultValue: PropTypes.number,
/** Max value for the field. */
max: PropTypes.number,
/** Min value for the field. */
min: PropTypes.number,
/** Using the up/down arrow keys will increment/decrement the input value by this number. */
step: PropTypes.number,
/** Inline label and input field */
inline: PropTypes.bool,
/** A unit that is a string of no more than 2 characters renders in the input after the value, e.g. % */
unit: (props, propName) => numberCharacterPropTypeCheck(props, propName, 2),
/** Whether to render up/down buttons */
showButtons: PropTypes.bool
} : {};
InputNumber.defaultProps = {
hiddenLabel: false,
required: false,
onChange: null,
step: 1,
showButtons: true,
unit: ''
};
export default InputNumber;