Skip to content

Instantly share code, notes, and snippets.

@SergProduction
Created October 20, 2020 18:01
Show Gist options
  • Save SergProduction/1840a0cec2cb73d7f8d4157a20c5404c to your computer and use it in GitHub Desktop.
Save SergProduction/1840a0cec2cb73d7f8d4157a20c5404c to your computer and use it in GitHub Desktop.
react input mask
export class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
mask: props.mask && props.maskValidate ? new Mask() : null
}
}
onChangeMask = (event) => {
if (this.state.mask === null) {
this.props.onChange(event)
return
}
const [isValid, maskedValue] = this.state.mask.parse(this.props.mask, this.props.maskValidate, event.currentTarget.value)
if (isValid) {
event.currentTarget.value = maskedValue
this.props.onChange(event)
}
}
render() {
const { mask, maskValidate, ...inputProps } = this.props
return (
<input
{...inputProps}
onChange={this.onChangeMask}
/>
)
}
}
class App extends React.Component {
render() {
return (
<Input
mask="____.__.__."
maskValidate={[/^\d{0,4}$/, '.', /^\d{0,2}$/, '.', /^\d{0,2}$/]}
onChange={e => this.setState({value: e.currentTarget.value})}
value={this.state.value}
/>
)
}
}
const multiSplit = (str, delimeters) => {
let right = str
const result = []
for (let i = 0; i < delimeters.length; i++) {
const indexDel = right.indexOf(delimeters[i])
if (indexDel === -1) continue
result.push(right.slice(0, indexDel))
right = right.slice(indexDel + 1)
}
result.push(right)
return result
}
export class Mask {
constructor() {
this.isDel = false
this.prevValue = ""
}
join(targets, masks, delimeters) {
return targets.reduce((acc, part, i) => {
if (
part.length === masks[i].length &&
this.isDel === true &&
i === targets.length - 1
) {
return acc + part.slice(0, -1)
} else if (part.length === masks[i].length) {
return delimeters[i] !== undefined
? acc + part + delimeters[i]
: acc + part
} else {
return acc + part
}
}, "")
}
/**
* @params {string} mask
* @params {regexps} validate
* @params {string} value
* @example
masks = '__.__.____',
validate = [/^\d{0,2}$/, ".", /^\d{0,2}$/, ".", /^\d{0,4}$/],
value = '13.09.2019'
*/
parse(mask, validate, value) {
this.isDel = this.prevValue.length > value.length
const delimeters = validate.filter(m => !(m instanceof RegExp))
const regexps = validate.filter(m => m instanceof RegExp)
const masks = multiSplit(mask, delimeters)
let targets = multiSplit(value, delimeters)
if (this.isDel) {
let right = targets.join("")
targets = targets.map((_, i) => {
const res = right.slice(0, masks[i].length)
right = right.slice(masks[i].length)
return res
})
}
const isValid = targets.every((partText, i) => regexps[i].test(partText))
if (isValid) {
const valueWithMask = this.join(targets, masks, delimeters)
this.prevValue = valueWithMask
return [true, valueWithMask]
}
return [false, value]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment