UNPKG

@massds/mayflower-react

Version:

React versions of Mayflower design system UI components

283 lines 10.7 kB
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;