Open skube opened 6 years ago
I started saying that I needed something similar to type times, but then I though I should give it a try. So here is something close: https://stackblitz.com/edit/react-lycodu
Essentially, specify a mask function, instead of a simple mask array. It's not exactly as you (or I) wanted, especially when only 1 digit is typed, but it's close. It looks like:
$
$1 <- this is awkward, should be $0.01, not $1
$.12
$1.23
$12.34
$123.45
The thing is I don't know yet how to fill the mask from the right. I think that might be possible with a pipe (?)
What I was trying to do for durations:
__:__:__
__:__:_1
__:__:12
__:_1:24
__:12:40
_1:24:00
Here is something that seems to work, although the code is pretty ugly... https://stackblitz.com/edit/react-wc4dj8
$__._1
$__.12
$_1.23
$12.34
$123.45
import React, { Component } from 'react';
import { render } from 'react-dom';
import MaskedInput from 'react-text-mask';
const maskFn = (input) => {
var digits = input.split("").filter((c) => c >= '0' && c <= '9');
if (digits.length < 4) {
return ['$', /\d/, /\d/, '.', /\d/, /\d/];
}
var mask = [];
for (var i = digits.length - 1; i >= 0; i--) {
mask.push(/\d/);
if (i == digits.length - 2) {
mask.push('.');
}
}
mask.push('$');
return mask.reverse()
}
const pipe = (conformedValue, config) => {
console.log(conformedValue, config);
const _ = config.placeholderChar;
var digits = config.rawValue.split("").filter((c) => c >= '0' && c <= '9');
if (digits.length == 0) {
return ``;
}
if (digits.length == 1) {
console.log(1)
return `$${_}${_}.${_}${digits[0]}`;
}
const cents = digits.slice(digits.length - 2).join('');
if (digits.length == 2) {
return `$${_}${_}.${cents}`;
}
const dollars = digits.slice(0, digits.length - 2).join('');
if (digits.length == 3) {
return `$${_}${dollars}.${cents}`
}
if (digits.length > 3) {
return `$${dollars}.${cents}`
}
return conformedValue
}
class App extends Component {
constructor() {
super();
}
render() {
return (
<div>
<MaskedInput
type="text"
mask={maskFn}
placeholder="$"
keepCharPositions={false}
pipe={pipe}
/>
</div>
);
}
}
render(<App />, document.getElementById('root'));
I see there's already an issue for the same request, from 2016: https://github.com/text-mask/text-mask/issues/263.
We could really, really use this feature :)
I used the @yohcop approach and implemented some options:
const decimalMaskOpt = ({
thousandsSeparatorSymbol = ",",
decimalSymbol = ".",
decimalPlaces = 2,
} = {}) => {
return (input) => {
var digits = (input.match(/\d/gi) || []).length;
if (digits <= decimalPlaces) return Array(decimalPlaces).fill(/\d/);
if (digits === decimalPlaces + 1) {
return [/\d/, /\d/, decimalSymbol, ...Array(decimalPlaces).fill(/\d/)];
}
var mask = [];
for (var i = digits - 1; i >= 0; i--) {
mask.push(/\d/);
if (i == digits - decimalPlaces) {
mask.push(decimalSymbol);
}
const r = digits - i;
if (r >= decimalPlaces + 2 && (r - decimalPlaces) % 3 == 0 && i > 0) {
mask.push(thousandsSeparatorSymbol);
}
}
return mask.reverse();
};
};
class App extends Component {
constructor() {
super();
}
render() {
return (
<div>
<MaskedInput
type="text"
mask={decimalMaskOpt({ decimalPlaces: 3 })}
guide={false}
/>
</div>
);
}
}
I'm wondering if it's possible to add the ability to have the decimal place auto-entered as an option?
Similar to how some ATM's work when inputing amounts, the decimal is automatically assumed and each numeric input creates the number from the right. For example, to input $20.00, one must enter 2, followed by three zeros only (no need to manually enter the decimal):