renato-bohler / redux-form-input-masks

Input masking with redux-form made easy
https://bohler.dev/redux-form-input-masks/
MIT License
138 stars 10 forks source link

Shows decimals should be optional #132

Open karina2004liz opened 2 years ago

karina2004liz commented 2 years ago

Hi Renato!

What is the current behavior?

Currently we can add decimals in the "decimalPlaces" property and these are shown by default, if we put {decimalPlaces: 2} the value that is shown will be "0.00". It would be great if they could add an additional property like "fixedDecimalScale" which is a boolean that allows to show or not show decimals, even if they are allowed. That way our displayed value could stay at "0", but we could add decimals if needed

What is the expected behavior?

Add the "fixedDecimalScale" property that allows us to make it optional to show the decimals

import { createNumberMask } from 'redux-form-input-masks';

const myCustomNumberMask = createNumberMask({ decimalPlaces: 2, fixedDecimalScale: false });

<Field name="my-numeric-field" component={TextField} type="tel" validate={validation} {...myCustomNumberMask} />

Final result would be in the input value as 0 but if the user add decimals just can add two digits, initial value = 0 user add value = 10 Final result = 10 user wants add decimals = 10.5 Final result = 10.50

In this way, with prop "fixedDecimalScale" we can choose if we want to show decimals values when the input is rendered, if this prop is false the initial value is 0, if not the initial value would be 0.00

Is it this possible?

renato-bohler commented 2 years ago

Hey!

Hmm, it's been a long time, but I think that would be possible to implement. I'm not adding new features to this repository, though 😅

I've tried implementing something like this manually, like so:

// This doesn't work very well because of poor caret manipulation
const Component = () => {
  const [isDecimalPlacesEnabled, setDecimalPlacesEnabled] = useState(false);

  const handleKeyDown = (event) => {
    if (event.key !== '.' || isDecimalPlacesEnabled) return;

    setDecimalPlacesEnabled(true);

    // TODO: uncomment to try and fix the caret positioning after enabling decimal places
    // event.persist();
    // setTimeout(() => {
    //   event.target.selectionStart -= 2;
    //   event.target.selectionEnd = event.target.selectionStart;
    // });
  }

  const currencyMask = createNumberMask({
    decimalPlaces: isDecimalPlacesEnabled ? 2 : 0,
  });

  return (
    <Field
      name="amount"
      component="input"
      type="tel"
      onKeyUpCapture={handleKeyDown}
      {...currencyMask}
    />
  );
}

This enables decimalPlaces whenever the user presses the period (.) key, but the caret position jumps to the end after that, which ends up being a bad user experience. If the user presses 123.45 the field would end up with the value 12300.45 (completely unintuitive).

I've tried to manually update the caret position after enabling decimal places, but that doesn't solve the problem completely either.

If you want to have a different input (like a checkbox, for instance) controlling wether you want to allow decimal places or not, this might work:

rfim2

// This could work
const Component = () => {
  const [isDecimalPlacesEnabled, setDecimalPlacesEnabled] = useState(false);

  const handleCheckboxChange = (event) => {
    setDecimalPlacesEnabled(event.currentTarget.checked);
  }

  const currencyMask = createNumberMask({
    decimalPlaces: isDecimalPlacesEnabled ? 2 : 0,
  });

  return (
    <>
      <Field
        name="amount"
        component="input"
        type="tel"
        {...currencyMask}
      />
      <input type="checkbox" name="allowDecimal" id="allowDecimal" onChange={handleCheckboxChange}/>
      <label for="allowDecimal">Allow decimals</label>
    </>
  );
}
karina2004liz commented 2 years ago

Thanks a lot for your answer Renato! I will try to implement the first one! Wish me luck!