diff --git a/.gitignore b/.gitignore index e51faa6..55238c1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /umd npm-debug.log* package-lock.json +/.vscode diff --git a/CHANGES.md b/CHANGES.md index c7a2410..1e250dd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 4.0.2 / 2018-02-08 + +* Prevent paste in disabled state + ## 4.0.1 / 2018-01-26 🇦🇺 * Fix auto-fill scenarios by using data from `onChange` events [[#112](https://github.com/insin/react-maskedinput/pull/112)] diff --git a/README.md b/README.md index 7ed1291..46cca07 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,10 @@ A default `placeholder` will be generated from the mask's pattern, but you can p By default, the rendered ``'s `size` will be the length of the pattern, but you can pass a `size` prop to override this. +### `onBadInput` : `() => void` + +A callback which will be called any time the user inputs or pastes invalid character(s).**** + ### Other props Any other props passed in will be passed as props to the rendered ``, except for the following, which are managed by the component: diff --git a/demo/src/index.js b/demo/src/index.js index 54a38d6..f9e7ddf 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -1,7 +1,7 @@ import './style.css' import React from 'react' -import {render} from 'react-dom' +import {render, findDOMNode} from 'react-dom' import MaskedInput from '../../src' @@ -12,7 +12,7 @@ const PATTERNS = [ '1 1', ] -class App extends React.Component { +class App extends React.PureComponent { state = { card: '', expiry: '', @@ -43,6 +43,24 @@ class App extends React.Component { } } + _onBadInput = () => { + if (this.timerId) { + return; + } + this.inputCardNode.classList.add('is-invalid'); + this.timerId = setTimeout(() => { + delete this.timerId; + this.inputCardNode.classList.remove('is-invalid'); + }, 400); + }; + + componentWillUnmount() { + if (this.timerId) { + clearTimeout(this.timerId); + delete this.timerId; + } + }; + render() { return

@@ -51,7 +69,8 @@ class App extends React.Component {

A React component which creates a masked <input/>

- + this.inputCardNode = findDOMNode(input)} onBadInput={this._onBadInput} />

You can even externally update the card state like a standard input element:

diff --git a/demo/src/style.css b/demo/src/style.css index 0c54f0c..5e6dc8a 100644 --- a/demo/src/style.css +++ b/demo/src/style.css @@ -39,3 +39,8 @@ input { footer { text-align: center; } +.is-invalid { + outline-color: #efa2a9; + -webkit-transition: outline-color 0.2s; + transition: outline-color 0.2s; +} \ No newline at end of file diff --git a/package.json b/package.json index 635086f..aa110b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-maskedinput", - "version": "4.0.1", + "version": "4.0.2", "description": "Masked React component", "main": "lib/index.js", "module": "es/index.js", diff --git a/src/index.js b/src/index.js index 80ebb94..4cbef01 100644 --- a/src/index.js +++ b/src/index.js @@ -55,12 +55,16 @@ function setSelection(el, selection) { catch (e) { /* not focused or not visible */ } } -class MaskedInput extends React.Component { +class MaskedInput extends React.PureComponent { static propTypes = { mask: PropTypes.string.isRequired, - + onChange: PropTypes.func, formatCharacters: PropTypes.object, - placeholderChar: PropTypes.string + placeholderChar: PropTypes.string, + value: PropTypes.any, + placeholder: PropTypes.string, + size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + onBadInput: PropTypes.func, } static defaultProps = { @@ -202,15 +206,19 @@ class MaskedInput extends React.Component { this.props.onChange(e) } } + else { + this._fireBadInput() + } } _onPaste = (e) => { // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value) + const {disabled} = this.props e.preventDefault() this._updateMaskSelection() // getData value needed for IE also works in FF & Chrome - if (this.mask.paste(e.clipboardData.getData('Text'))) { + if (!disabled && this.mask.paste(e.clipboardData.getData('Text'))) { e.target.value = this.mask.getValue() // Timeout needed for IE setTimeout(() => this._updateInputSelection(), 0) @@ -243,6 +251,13 @@ class MaskedInput extends React.Component { } } + _fireBadInput () { + const {onBadInput} = this.props + if (typeof onBadInput === 'function') { + onBadInput() + } + } + focus() { this.input.focus() } @@ -258,7 +273,7 @@ class MaskedInput extends React.Component { let eventHandlers = this._getEventHandlers() let { size = maxLength, placeholder = this.mask.emptyValue } = this.props - let { placeholderChar, formatCharacters, ...cleanedProps } = this.props // eslint-disable-line no-unused-vars + let { placeholderChar, formatCharacters, onBadInput, ...cleanedProps } = this.props // eslint-disable-line no-unused-vars let inputProps = { ...cleanedProps, ...eventHandlers, ref, maxLength, value, size, placeholder } return }