Include React 16 in peerDependencies (#115)
Fix wrong scope in paste event (#108)
Fix autofill scenarios by using data from onchange events (#112) Side effects: 1) Delete behavior now allows removal of more than one character 2) Cut now shifts remaining characters left instead of leaving "hole" Also fix binding of this._updateInputSelection (cf. https://github.com/insin/react-maskedinput/pull/110)
Update devDependencies - nwb@0.21 fixes the UMD build
Update CHANGES [ci skip]
Add Node.js 8 to test matrix
Fix a typo [ci skip]
Update syntax in component and examples
Release v4.0.1
@@ -2,4 +2,5 @@ language: node_js
|
|
2 |
node_js:
|
3 |
- 4
|
4 |
- 6
|
|
|
5 |
script: npm test
|
2 |
node_js:
|
3 |
- 4
|
4 |
- 6
|
5 |
+
- 8
|
6 |
script: npm test
|
@@ -1,3 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
## 4.0.0 / 2017-07-04
|
2 |
|
3 |
* Potential breaking change as the `peerDependencies` range has been changed from `"0.14.x || 15.x"` to `"^0.14.9 || ^15.3.0"`.
|
1 |
+
## 4.0.1 / 2018-01-26 🇦🇺
|
2 |
+
|
3 |
+
* Fix auto-fill scenarios by using data from `onChange` events [[#112]((https://github.com/insin/react-maskedinput/pull/112)]
|
4 |
+
|
5 |
+
* Fix wrong scope in `onPaste` event [[#108]((https://github.com/insin/react-maskedinput/pull/108)]
|
6 |
+
|
7 |
+
* Include React 16 in `peerDependencies` [[#115]((https://github.com/insin/react-maskedinput/pull/115)]
|
8 |
+
|
9 |
+
* Update nwb to 0.21.x to fix UMD build, which was exporting an object with a `default` property
|
10 |
+
|
11 |
## 4.0.0 / 2017-07-04
|
12 |
|
13 |
* Potential breaking change as the `peerDependencies` range has been changed from `"0.14.x || 15.x"` to `"^0.14.9 || ^15.3.0"`.
|
@@ -4,7 +4,7 @@
|
|
4 |
|
5 |
## Installation
|
6 |
|
7 |
-
* Running `npm install` in the
|
8 |
|
9 |
## Demo Development Server
|
10 |
|
4 |
|
5 |
## Installation
|
6 |
|
7 |
+
* Running `npm install` in the component's root directory will install everything you need for development.
|
8 |
|
9 |
## Demo Development Server
|
10 |
|
@@ -10,15 +10,13 @@ A [React](http://facebook.github.io/react/) component for `<input>` masking, bui
|
|
10 |
|
11 |
### npm
|
12 |
|
13 |
-
`MaskedInput` can be used on the server, or bundled for the client using an npm-compatible packaging system such as [Browserify](http://browserify.org/) or [webpack](http://webpack.github.io/).
|
14 |
-
|
15 |
```
|
16 |
npm install react-maskedinput --save
|
17 |
```
|
18 |
|
19 |
### Browser bundle
|
20 |
|
21 |
-
The browser bundle exposes a global `MaskedInput` variable and expects to find a global `React`
|
22 |
|
23 |
* [react-maskedinput.js](https://unpkg.com/react-maskedinput/umd/react-maskedinput.js) (development version)
|
24 |
* [react-maskedinput.min.js](https://unpkg.com/react-maskedinput/umd/react-maskedinput.min.js) (compressed production version)
|
@@ -28,21 +26,19 @@ The browser bundle exposes a global `MaskedInput` variable and expects to find a
|
|
28 |
Give `MaskedInput` a [`mask`](#mask-string) and an `onChange` callback:
|
29 |
|
30 |
```javascript
|
31 |
-
|
32 |
-
|
33 |
|
34 |
-
|
35 |
-
state
|
36 |
card: '',
|
37 |
expiry: '',
|
38 |
ccv: ''
|
39 |
-
}
|
40 |
|
41 |
-
_onChange(e) {
|
42 |
-
|
43 |
-
|
44 |
-
this.setState(stateChange)
|
45 |
-
},
|
46 |
|
47 |
render() {
|
48 |
return <div className="CreditCardDetails">
|
@@ -60,28 +56,26 @@ var CreditCardDetails = React.createClass({
|
|
60 |
</label>
|
61 |
</div>
|
62 |
}
|
63 |
-
}
|
64 |
```
|
65 |
|
66 |
Create some wrapper components if you have a masking configuration which will be reused:
|
67 |
|
68 |
```javascript
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
transform(char) { return char.toUpperCase() }
|
80 |
-
}
|
81 |
}
|
82 |
-
}
|
83 |
-
|
84 |
-
}
|
85 |
```
|
86 |
|
87 |
## Props
|
10 |
|
11 |
### npm
|
12 |
|
|
|
|
|
13 |
```
|
14 |
npm install react-maskedinput --save
|
15 |
```
|
16 |
|
17 |
### Browser bundle
|
18 |
|
19 |
+
The browser bundle exposes a global `MaskedInput` variable and expects to find a global `React` variable to work with.
|
20 |
|
21 |
* [react-maskedinput.js](https://unpkg.com/react-maskedinput/umd/react-maskedinput.js) (development version)
|
22 |
* [react-maskedinput.min.js](https://unpkg.com/react-maskedinput/umd/react-maskedinput.min.js) (compressed production version)
|
26 |
Give `MaskedInput` a [`mask`](#mask-string) and an `onChange` callback:
|
27 |
|
28 |
```javascript
|
29 |
+
import React from 'react'
|
30 |
+
import MaskedInput from 'react-maskedinput'
|
31 |
|
32 |
+
class CreditCardDetails extends React.Component {
|
33 |
+
state = {
|
34 |
card: '',
|
35 |
expiry: '',
|
36 |
ccv: ''
|
37 |
+
}
|
38 |
|
39 |
+
_onChange = (e) => {
|
40 |
+
this.setState({[e.target.name]: e.target.value})
|
41 |
+
}
|
|
|
|
|
42 |
|
43 |
render() {
|
44 |
return <div className="CreditCardDetails">
|
56 |
</label>
|
57 |
</div>
|
58 |
}
|
59 |
+
}
|
60 |
```
|
61 |
|
62 |
Create some wrapper components if you have a masking configuration which will be reused:
|
63 |
|
64 |
```javascript
|
65 |
+
function CustomInput(props) {
|
66 |
+
return <MaskedInput
|
67 |
+
mask="1111-WW-11"
|
68 |
+
placeholder="1234-WW-12"
|
69 |
+
size="11"
|
70 |
+
{...props}
|
71 |
+
formatCharacters={{
|
72 |
+
'W': {
|
73 |
+
validate(char) { return /\w/.test(char ) },
|
74 |
+
transform(char) { return char.toUpperCase() }
|
|
|
|
|
75 |
}
|
76 |
+
}}
|
77 |
+
/>
|
78 |
+
}
|
79 |
```
|
80 |
|
81 |
## Props
|
@@ -27,9 +27,7 @@ class App extends React.Component {
|
|
27 |
}
|
28 |
|
29 |
_onChange = (e) => {
|
30 |
-
|
31 |
-
stateChange[e.target.name] = e.target.value
|
32 |
-
this.setState(stateChange)
|
33 |
}
|
34 |
|
35 |
_changePattern = (e) => {
|
@@ -99,7 +97,7 @@ class App extends React.Component {
|
|
99 |
<label htmlFor="changing">Credit Card:</label>
|
100 |
<MaskedInput mask={this.state.cardPattern} name="creditCard" id="creditCard" onChange={this._onCardChange}/>
|
101 |
</div>
|
102 |
-
<p>Custom format character (W=[a-zA-Z0-9_]
|
103 |
<div className="form-field">
|
104 |
<label htmlFor="custom">Custom:</label>
|
105 |
<CustomInput name="custom" id="custom" onChange={this._onChange}/>
|
@@ -112,7 +110,7 @@ class App extends React.Component {
|
|
112 |
}
|
113 |
}
|
114 |
|
115 |
-
|
116 |
<MaskedInput
|
117 |
mask="1111-WW-11"
|
118 |
placeholder="1234-WW-12"
|
@@ -121,8 +119,8 @@ const CustomInput = (props) =>
|
|
121 |
{...props}
|
122 |
formatCharacters={{
|
123 |
'W': {
|
124 |
-
validate(char)
|
125 |
-
transform(char)
|
126 |
}
|
127 |
}}
|
128 |
/>
|
27 |
}
|
28 |
|
29 |
_onChange = (e) => {
|
30 |
+
this.setState({[e.target.name]: e.target.value})
|
|
|
|
|
31 |
}
|
32 |
|
33 |
_changePattern = (e) => {
|
97 |
<label htmlFor="changing">Credit Card:</label>
|
98 |
<MaskedInput mask={this.state.cardPattern} name="creditCard" id="creditCard" onChange={this._onCardChange}/>
|
99 |
</div>
|
100 |
+
<p>Custom format character (<code>W=[a-zA-Z0-9_]</code>, transformed to uppercase) and placeholder character (en space):</p>
|
101 |
<div className="form-field">
|
102 |
<label htmlFor="custom">Custom:</label>
|
103 |
<CustomInput name="custom" id="custom" onChange={this._onChange}/>
|
110 |
}
|
111 |
}
|
112 |
|
113 |
+
let CustomInput = (props) =>
|
114 |
<MaskedInput
|
115 |
mask="1111-WW-11"
|
116 |
placeholder="1234-WW-12"
|
119 |
{...props}
|
120 |
formatCharacters={{
|
121 |
'W': {
|
122 |
+
validate: (char) => /\w/.test(char),
|
123 |
+
transform: (char) => char.toUpperCase()
|
124 |
}
|
125 |
}}
|
126 |
/>
|
@@ -14,15 +14,6 @@ module.exports = function(build) {
|
|
14 |
if (/^build/.test(build.command)) {
|
15 |
// Don't include default polyfills in the demo build
|
16 |
config.polyfill = false
|
17 |
-
// Prevent React 15.x triggering inclusion of the Node.js process shim in the
|
18 |
-
// demo build.
|
19 |
-
config.webpack = {
|
20 |
-
extra: {
|
21 |
-
node: {
|
22 |
-
process: false
|
23 |
-
}
|
24 |
-
}
|
25 |
-
}
|
26 |
}
|
27 |
|
28 |
return config
|
14 |
if (/^build/.test(build.command)) {
|
15 |
// Don't include default polyfills in the demo build
|
16 |
config.polyfill = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
}
|
18 |
|
19 |
return config
|
@@ -1,6 +1,6 @@
|
|
1 |
{
|
2 |
"name": "react-maskedinput",
|
3 |
-
"version": "4.0.
|
4 |
"description": "Masked <input/> React component",
|
5 |
"main": "lib/index.js",
|
6 |
"module": "es/index.js",
|
@@ -23,13 +23,13 @@
|
|
23 |
"prop-types": "^15.5.7"
|
24 |
},
|
25 |
"peerDependencies": {
|
26 |
-
"react": "^0.14.9 || ^15.3.0"
|
27 |
},
|
28 |
"devDependencies": {
|
29 |
"eslint-config-jonnybuchanan": "5.0.x",
|
30 |
-
"nwb": "0.
|
31 |
-
"react": "
|
32 |
-
"react-dom": "
|
33 |
},
|
34 |
"author": "Jonny Buchanan <jonathan.buchanan@gmail.com>",
|
35 |
"homepage": "https://github.com/insin/react-maskedinput",
|
1 |
{
|
2 |
"name": "react-maskedinput",
|
3 |
+
"version": "4.0.1",
|
4 |
"description": "Masked <input/> React component",
|
5 |
"main": "lib/index.js",
|
6 |
"module": "es/index.js",
|
23 |
"prop-types": "^15.5.7"
|
24 |
},
|
25 |
"peerDependencies": {
|
26 |
+
"react": "^0.14.9 || ^15.3.0 || ^16.0.0"
|
27 |
},
|
28 |
"devDependencies": {
|
29 |
"eslint-config-jonnybuchanan": "5.0.x",
|
30 |
+
"nwb": "0.21.x",
|
31 |
+
"react": "16.x",
|
32 |
+
"react-dom": "16.x"
|
33 |
},
|
34 |
"author": "Jonny Buchanan <jonathan.buchanan@gmail.com>",
|
35 |
"homepage": "https://github.com/insin/react-maskedinput",
|
@@ -2,8 +2,8 @@ import React from 'react'
|
|
2 |
import PropTypes from 'prop-types'
|
3 |
import InputMask from 'inputmask-core'
|
4 |
|
5 |
-
|
6 |
-
|
7 |
|
8 |
function isUndo(e) {
|
9 |
return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
|
@@ -14,17 +14,16 @@ function isRedo(e) {
|
|
14 |
}
|
15 |
|
16 |
function getSelection (el) {
|
17 |
-
|
18 |
-
|
19 |
if (el.selectionStart !== undefined) {
|
20 |
start = el.selectionStart
|
21 |
end = el.selectionEnd
|
22 |
}
|
23 |
else {
|
24 |
try {
|
25 |
el.focus()
|
26 |
-
rangeEl = el.createTextRange()
|
27 |
-
clone = rangeEl.duplicate()
|
28 |
|
29 |
rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
|
30 |
clone.setEndPoint('EndToStart', rangeEl)
|
@@ -39,16 +38,14 @@ function getSelection (el) {
|
|
39 |
}
|
40 |
|
41 |
function setSelection(el, selection) {
|
42 |
-
var rangeEl
|
43 |
-
|
44 |
try {
|
45 |
if (el.selectionStart !== undefined) {
|
46 |
el.focus()
|
47 |
el.setSelectionRange(selection.start, selection.end)
|
48 |
}
|
49 |
else {
|
50 |
el.focus()
|
51 |
-
rangeEl = el.createTextRange()
|
52 |
rangeEl.collapse(true)
|
53 |
rangeEl.moveStart('character', selection.start)
|
54 |
rangeEl.moveEnd('character', selection.end - selection.start)
|
@@ -59,17 +56,19 @@ function setSelection(el, selection) {
|
|
59 |
}
|
60 |
|
61 |
class MaskedInput extends React.Component {
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
64 |
|
65 |
-
|
66 |
-
|
67 |
-
this._onPaste = this._onPaste.bind(this)
|
68 |
-
this._onKeyPress = this._onKeyPress.bind(this)
|
69 |
}
|
70 |
|
71 |
componentWillMount() {
|
72 |
-
|
73 |
pattern: this.props.mask,
|
74 |
value: this.props.value,
|
75 |
formatCharacters: this.props.formatCharacters
|
@@ -128,30 +127,24 @@ class MaskedInput extends React.Component {
|
|
128 |
setSelection(this.input, this.mask.selection)
|
129 |
}
|
130 |
|
131 |
-
_onChange(e) {
|
132 |
// console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)
|
133 |
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
this.mask.backspace()
|
142 |
-
}
|
143 |
-
var value = this._getDisplayValue()
|
144 |
-
e.target.value = value
|
145 |
-
if (value) {
|
146 |
-
this._updateInputSelection()
|
147 |
-
}
|
148 |
}
|
|
|
149 |
if (this.props.onChange) {
|
150 |
this.props.onChange(e)
|
151 |
}
|
152 |
}
|
153 |
|
154 |
-
_onKeyDown(e) {
|
155 |
// console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
|
156 |
|
157 |
if (isUndo(e)) {
|
@@ -181,7 +174,7 @@ class MaskedInput extends React.Component {
|
|
181 |
e.preventDefault()
|
182 |
this._updateMaskSelection()
|
183 |
if (this.mask.backspace()) {
|
184 |
-
|
185 |
e.target.value = value
|
186 |
if (value) {
|
187 |
this._updateInputSelection()
|
@@ -193,7 +186,7 @@ class MaskedInput extends React.Component {
|
|
193 |
}
|
194 |
}
|
195 |
|
196 |
-
_onKeyPress(e) {
|
197 |
// console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
|
198 |
|
199 |
// Ignore modified key presses
|
@@ -211,7 +204,7 @@ class MaskedInput extends React.Component {
|
|
211 |
}
|
212 |
}
|
213 |
|
214 |
-
_onPaste(e) {
|
215 |
// console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)
|
216 |
|
217 |
e.preventDefault()
|
@@ -220,15 +213,15 @@ class MaskedInput extends React.Component {
|
|
220 |
if (this.mask.paste(e.clipboardData.getData('Text'))) {
|
221 |
e.target.value = this.mask.getValue()
|
222 |
// Timeout needed for IE
|
223 |
-
setTimeout(this._updateInputSelection, 0)
|
224 |
if (this.props.onChange) {
|
225 |
this.props.onChange(e)
|
226 |
}
|
227 |
}
|
228 |
}
|
229 |
|
230 |
_getDisplayValue() {
|
231 |
-
|
232 |
return value === this.mask.emptyValue ? '' : value
|
233 |
}
|
234 |
|
@@ -259,27 +252,16 @@ class MaskedInput extends React.Component {
|
|
259 |
}
|
260 |
|
261 |
render() {
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
return <input {...inputProps} />
|
271 |
}
|
272 |
}
|
273 |
|
274 |
-
MaskedInput.propTypes = {
|
275 |
-
mask: PropTypes.string.isRequired,
|
276 |
-
|
277 |
-
formatCharacters: PropTypes.object,
|
278 |
-
placeholderChar: PropTypes.string
|
279 |
-
}
|
280 |
-
|
281 |
-
MaskedInput.defaultProps = {
|
282 |
-
value: ''
|
283 |
-
}
|
284 |
-
|
285 |
export default MaskedInput
|
2 |
import PropTypes from 'prop-types'
|
3 |
import InputMask from 'inputmask-core'
|
4 |
|
5 |
+
let KEYCODE_Z = 90
|
6 |
+
let KEYCODE_Y = 89
|
7 |
|
8 |
function isUndo(e) {
|
9 |
return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
|
14 |
}
|
15 |
|
16 |
function getSelection (el) {
|
17 |
+
let start, end
|
|
|
18 |
if (el.selectionStart !== undefined) {
|
19 |
start = el.selectionStart
|
20 |
end = el.selectionEnd
|
21 |
}
|
22 |
else {
|
23 |
try {
|
24 |
el.focus()
|
25 |
+
let rangeEl = el.createTextRange()
|
26 |
+
let clone = rangeEl.duplicate()
|
27 |
|
28 |
rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
|
29 |
clone.setEndPoint('EndToStart', rangeEl)
|
38 |
}
|
39 |
|
40 |
function setSelection(el, selection) {
|
|
|
|
|
41 |
try {
|
42 |
if (el.selectionStart !== undefined) {
|
43 |
el.focus()
|
44 |
el.setSelectionRange(selection.start, selection.end)
|
45 |
}
|
46 |
else {
|
47 |
el.focus()
|
48 |
+
let rangeEl = el.createTextRange()
|
49 |
rangeEl.collapse(true)
|
50 |
rangeEl.moveStart('character', selection.start)
|
51 |
rangeEl.moveEnd('character', selection.end - selection.start)
|
56 |
}
|
57 |
|
58 |
class MaskedInput extends React.Component {
|
59 |
+
static propTypes = {
|
60 |
+
mask: PropTypes.string.isRequired,
|
61 |
+
|
62 |
+
formatCharacters: PropTypes.object,
|
63 |
+
placeholderChar: PropTypes.string
|
64 |
+
}
|
65 |
|
66 |
+
static defaultProps = {
|
67 |
+
value: ''
|
|
|
|
|
68 |
}
|
69 |
|
70 |
componentWillMount() {
|
71 |
+
let options = {
|
72 |
pattern: this.props.mask,
|
73 |
value: this.props.value,
|
74 |
formatCharacters: this.props.formatCharacters
|
127 |
setSelection(this.input, this.mask.selection)
|
128 |
}
|
129 |
|
130 |
+
_onChange = (e) => {
|
131 |
// console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)
|
132 |
|
133 |
+
let maskValue = this.mask.getValue()
|
134 |
+
let incomingValue = e.target.value
|
135 |
+
if (incomingValue !== maskValue) { // only modify mask if form contents actually changed
|
136 |
+
this._updateMaskSelection()
|
137 |
+
this.mask.setValue(incomingValue) // write the whole updated value into the mask
|
138 |
+
e.target.value = this._getDisplayValue() // update the form with pattern applied to the value
|
139 |
+
this._updateInputSelection()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
}
|
141 |
+
|
142 |
if (this.props.onChange) {
|
143 |
this.props.onChange(e)
|
144 |
}
|
145 |
}
|
146 |
|
147 |
+
_onKeyDown = (e) => {
|
148 |
// console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
|
149 |
|
150 |
if (isUndo(e)) {
|
174 |
e.preventDefault()
|
175 |
this._updateMaskSelection()
|
176 |
if (this.mask.backspace()) {
|
177 |
+
let value = this._getDisplayValue()
|
178 |
e.target.value = value
|
179 |
if (value) {
|
180 |
this._updateInputSelection()
|
186 |
}
|
187 |
}
|
188 |
|
189 |
+
_onKeyPress = (e) => {
|
190 |
// console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
|
191 |
|
192 |
// Ignore modified key presses
|
204 |
}
|
205 |
}
|
206 |
|
207 |
+
_onPaste = (e) => {
|
208 |
// console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)
|
209 |
|
210 |
e.preventDefault()
|
213 |
if (this.mask.paste(e.clipboardData.getData('Text'))) {
|
214 |
e.target.value = this.mask.getValue()
|
215 |
// Timeout needed for IE
|
216 |
+
setTimeout(() => this._updateInputSelection(), 0)
|
217 |
if (this.props.onChange) {
|
218 |
this.props.onChange(e)
|
219 |
}
|
220 |
}
|
221 |
}
|
222 |
|
223 |
_getDisplayValue() {
|
224 |
+
let value = this.mask.getValue()
|
225 |
return value === this.mask.emptyValue ? '' : value
|
226 |
}
|
227 |
|
252 |
}
|
253 |
|
254 |
render() {
|
255 |
+
let ref = r => { this.input = r }
|
256 |
+
let maxLength = this.mask.pattern.length
|
257 |
+
let value = this._getDisplayValue()
|
258 |
+
let eventHandlers = this._getEventHandlers()
|
259 |
+
let { size = maxLength, placeholder = this.mask.emptyValue } = this.props
|
260 |
+
|
261 |
+
let { placeholderChar, formatCharacters, ...cleanedProps } = this.props // eslint-disable-line no-unused-vars
|
262 |
+
let inputProps = { ...cleanedProps, ...eventHandlers, ref, maxLength, value, size, placeholder }
|
263 |
return <input {...inputProps} />
|
264 |
}
|
265 |
}
|
266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
267 |
export default MaskedInput
|
MaskedInput
A React component for <input>
masking, built on top of inputmask-core.
npm install react-maskedinput --save
The browser bundle exposes a global MaskedInput
variable and expects to find a global React
variable to work with.
Give MaskedInput
a mask
and an onChange
callback:
import React from 'react'
import MaskedInput from 'react-maskedinput'
class CreditCardDetails extends React.Component {
state = {
card: '',
expiry: '',
ccv: ''
}
_onChange = (e) => {
this.setState({[e.target.name]: e.target.value})
}
render() {
return <div className="CreditCardDetails">
<label>
Card Number:{' '}
<MaskedInput mask="1111 1111 1111 1111" name="card" size="20" onChange={this._onChange}/>
</label>
<label>
Expiry Date:{' '}
<MaskedInput mask="11/1111" name="expiry" placeholder="mm/yyyy" onChange={this._onChange}/>
</label>
<label>
CCV:{' '}
<MaskedInput mask="111" name="ccv" onChange={this._onChange}/>
</label>
</div>
}
}
Create some wrapper components if you have a masking configuration which will be reused:
function CustomInput(props) {
return <MaskedInput
mask="1111-WW-11"
placeholder="1234-WW-12"
size="11"
{...props}
formatCharacters={{
'W': {
validate(char) { return /\w/.test(char ) },
transform(char) { return char.toUpperCase() }
}
}}
/>
}
mask
: string
The masking pattern to be applied to the <input>
.
See the inputmask-core docs for supported formatting characters.
onChange
: (event: SyntheticEvent) => any
A callback which will be called any time the mask's value changes.
This will be passed a SyntheticEvent
with the input accessible via event.target
as usual.
Note: this component currently calls onChange
directly, it does not generate an onChange
event which will bubble up like a regular <input>
component, so you must pass an onChange
if you want to get a value back out.
formatCharacters
: Object
Customised format character definitions for use in the pattern.
See the inputmask-core docs for details of the structure of this object.
placeholderChar
: string
Customised placeholder character used to fill in editable parts of the pattern.
See the inputmask-core docs for details.
value
: string
A default value for the mask.
placeholder
: string
A default placeholder
will be generated from the mask's pattern, but you can pass a placeholder
prop to provide your own.
size
: number | string
By default, the rendered <input>
's size
will be the length of the pattern, but you can pass a size
prop to override this.
Any other props passed in will be passed as props to the rendered <input>
, except for the following, which are managed by the component:
maxLength
- will always be equal to the pattern's .length
onKeyDown
, onKeyPress
& onPaste
- will each trigger a call to onChange
when necessary