Skip to content

Instantly share code, notes, and snippets.

@romgrk
Last active April 21, 2017 03:42
Show Gist options
  • Select an option

  • Save romgrk/9513d238e00bb5df60af7b2f661bbe17 to your computer and use it in GitHub Desktop.

Select an option

Save romgrk/9513d238e00bb5df60af7b2f661bbe17 to your computer and use it in GitHub Desktop.
/*
* MaskedInput.js
* Shamelessly copied from: https://github.com/insin/react-maskedinput (MIT License)
*
* Usage:
* <MaskedInput mask='11/11/1111' onChange={value => console.log(value)} value={myValue} />
*/
import React from 'react';
import { connect } from 'react-redux';
import Input from 'react-toolbox/lib/input';
import InputMask from 'inputmask-core';
var KEYCODE_Z = 90
var KEYCODE_Y = 89
var MaskedInput = React.createClass({
propTypes: {
mask: React.PropTypes.string.isRequired,
formatCharacters: React.PropTypes.object,
placeholderChar: React.PropTypes.string
},
getDefaultProps() {
return {
value: ''
}
},
getInitialState() {
return {
value: ''
}
},
componentWillMount() {
var options = {
pattern: this.props.mask,
value: this.props.value,
formatCharacters: this.props.formatCharacters
}
if (this.props.placeholderChar) {
options.placeholderChar = this.props.placeholderChar
}
this.mask = new InputMask(options)
},
componentWillReceiveProps(nextProps) {
if (this.props.mask !== nextProps.mask && this.props.value !== nextProps.mask) {
// if we get a new value and a new mask at the same time
// check if the mask.value is still the initial value
// - if so use the nextProps value
// - otherwise the `this.mask` has a value for us (most likely from paste action)
if (this.mask.getValue() === this.mask.emptyValue) {
this.mask.setPattern(nextProps.mask, {value: nextProps.value})
}
else {
this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
}
}
else if (this.props.mask !== nextProps.mask) {
this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
}
else if (this.props.value !== nextProps.value) {
this.mask.setValue(nextProps.value)
}
},
componentWillUpdate(nextProps, nextState) {
if (nextProps.mask !== this.props.mask) {
this._updatePattern(nextProps)
}
},
componentDidUpdate(prevProps) {
if (prevProps.mask !== this.props.mask && this.mask.selection.start) {
this._updateInputSelection()
}
},
_updatePattern: function(props) {
this.mask.setPattern(props.mask, {
value: this.mask.getRawValue(),
selection: getSelection(this.input)
})
},
_updateMaskSelection() {
this.mask.selection = getSelection(this.input)
},
_updateInputSelection() {
setSelection(this.input, this.mask.selection)
},
_onChange(newValue) {
// console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)
var value
var maskValue = this.mask.getValue()
if (newValue !== maskValue) {
// Cut or delete operations will have shortened the value
if (newValue.length < maskValue.length) {
var sizeDiff = maskValue.length - newValue.length
this._updateMaskSelection()
this.mask.selection.end = this.mask.selection.start + sizeDiff
this.mask.backspace()
}
value = this._getDisplayValue()
this.setState({ value: value })
if (value) {
this._updateInputSelection()
}
}
if (this.props.onChange) {
this.props.onChange(value)
}
},
_onKeyDown(e) {
// console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
if (isUndo(e)) {
e.preventDefault()
if (this.mask.undo()) {
e.target.value = this._getDisplayValue()
this._updateInputSelection()
if (this.props.onChange) {
this.props.onChange(this._getDisplayValue())
}
}
return
}
else if (isRedo(e)) {
e.preventDefault()
if (this.mask.redo()) {
e.target.value = this._getDisplayValue()
this._updateInputSelection()
if (this.props.onChange) {
this.props.onChange(this._getDisplayValue())
}
}
return
}
if (e.key === 'Backspace') {
e.preventDefault()
this._updateMaskSelection()
if (this.mask.backspace()) {
var value = this._getDisplayValue()
e.target.value = value
if (value) {
this._updateInputSelection()
}
if (this.props.onChange) {
this.props.onChange(this._getDisplayValue())
}
}
}
},
_onKeyPress(e) {
// console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)
// Ignore modified key presses
// Ignore enter key to allow form submission
if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return }
e.preventDefault()
this._updateMaskSelection()
if (this.mask.input((e.key || e.data))) {
e.target.value = this.mask.getValue()
this._updateInputSelection()
if (this.props.onChange) {
this.props.onChange(this.mask.getValue())
}
}
},
_onPaste(e) {
// console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)
e.preventDefault()
this._updateMaskSelection()
// getData value needed for IE also works in FF & Chrome
if (this.mask.paste(e.clipboardData.getData('Text'))) {
e.target.value = this.mask.getValue()
// Timeout needed for IE
setTimeout(this._updateInputSelection, 0)
if (this.props.onChange) {
this.props.onChange(this.mask.getValue())
}
}
},
_getDisplayValue() {
//var value = this.mask.getValue()
//return value === this.mask.emptyValue ? '' : value
return this.mask.getValue()
},
_keyPressPropName() {
if (typeof navigator !== 'undefined') {
return navigator.userAgent.match(/Android/i)
? 'onBeforeInput'
: 'onKeyPress'
}
return 'onKeyPress'
},
_getEventHandlers() {
return {
onChange: this._onChange,
onKeyDown: this._onKeyDown,
onPaste: this._onPaste,
[this._keyPressPropName()]: this._onKeyPress
}
},
focus() {
this.input.focus()
},
blur() {
this.input.blur()
},
render() {
var ref = r => { if (r) this.input = r.refs.wrappedInstance.inputNode }
var maxLength = this.mask.pattern.length
var value = this._getDisplayValue()
var eventHandlers = this._getEventHandlers()
var { size = maxLength, placeholder = this.mask.emptyValue } = this.props
var {placeholderChar, formatCharacters, ...cleanedProps} = this.props
var inputProps = { ...cleanedProps, ...eventHandlers, ref, maxLength, value, size, placeholder }
return <Input {...inputProps} />
}
})
function isUndo(e) {
return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}
function isRedo(e) {
return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}
function getSelection (el) {
var start, end, rangeEl, clone
if (el.selectionStart !== undefined) {
start = el.selectionStart
end = el.selectionEnd
}
else {
try {
el.focus()
rangeEl = el.createTextRange()
clone = rangeEl.duplicate()
rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
clone.setEndPoint('EndToStart', rangeEl)
start = clone.text.length
end = start + rangeEl.text.length
}
catch (e) { /* not focused or not visible */ }
}
return { start, end }
}
function setSelection(el, selection) {
var rangeEl
try {
if (el.selectionStart !== undefined) {
el.focus()
el.setSelectionRange(selection.start, selection.end)
}
else {
el.focus()
rangeEl = el.createTextRange()
rangeEl.collapse(true)
rangeEl.moveStart('character', selection.start)
rangeEl.moveEnd('character', selection.end - selection.start)
rangeEl.select()
}
}
catch (e) { /* not focused or not visible */ }
}
export default connect(
)(MaskedInput);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment