skorphil / nextjs-form

Educational NextJs project to build a complex mobile-first form
https://www.chromatic.com/library?appId=65b2882ac1586a6b21cba0dd
MIT License
3 stars 0 forks source link

format registered number input AssetContainer #38

Closed skorphil closed 6 months ago

skorphil commented 7 months ago

Similar to:

Need to transform numbers inside inputs. 10000 -> 10,000 or 10 000 Need support for floating values (i.e. for crypto) 0.00065934

skorphil commented 7 months ago

Some native features of use-hook-form: https://react-hook-form.com/advanced-usage#TransformandParse

They utilize controller, but i use register as recommended in chackra UI docs https://chakra-ui.com/getting-started/with-hook-form

However controller and register have different methods https://github.com/orgs/react-hook-form/discussions/11538 and to update the value of registered component, the setValue() method should be used. Async nature of setValue() causing unwanted carret jump: https://github.com/skorphil/nextjs-form/issues/38#issuecomment-1973532662

skorphil commented 6 months ago

I need to caver range of scenarios. 1,000.00 for fiat 1,000.xxxxxxx... for crypto

Additional scenario problem: 1,000. being converted to 1,000 so it is impossible to add fractoring digits if it wasn't formatted like so Also the problem appears with jumping cursor during input Scenario of removing last remaining number

ChackraUI NumberInput not working with locale strings. Need to use Input

skorphil commented 6 months ago

Additional scenario problem: 1,000. being converted to 1,000 so it is impossible to add fractoring digits if it wasn't formatted like so

Made regex to capture zeros (?<!\..*)\.0*(?![0-9]) regexr.com/7sqjq

skorphil commented 6 months ago

Another problem is JS float precision.

  1. parseFloat() is quite enough for numbers like 0.00065934
  2. NumberFormat() and toLocaleString() both failing
const number = 0.00065934

const numForm = Intl.NumberFormat().format(number) // 0.001
const toLoc = number.toLocaleString() // 0.001

numeral.js increases formatting accuracy to parseFloat()

const numString = "0.123456789123456789"

const parseFl = parseFloat(numString) // '0.12345678912345678'

const number = 0.123456789123456789

const numForm = Intl.NumberFormat().format(number) // 0.123
const toLoc = number.toLocaleString() // 0.123
const numeralVar = numeral(number).format('0.[00000000000000000]') // '0.12345678912345678'

for more accuracy https://www.npmjs.com/package/decimal.js/v/3.0.0

skorphil commented 6 months ago

The more complex problem to deal with is carret jumping after updating value with:

<Input
          {...register(`${assetName}.amount`, {
            onChange: (e) => {
              const numberFormatted = stringToNumberFormatLocale(
                e.target.value
              );
              setValue(`${assetName}.amount`, numberFormatted); // async function, so carret position resets
            },
          })}
        />

https://github.com/skorphil/nextjs-form/assets/6762009/e68ac433-df43-48ad-8bc4-45ff0ecef928

Might be better to use external libraries for some sort of masks

skorphil commented 6 months ago

BTW Mantine UI inputs supports this natively

Under the hood it uses react-number-format https://github.com/mantinedev/mantine/blob/master/packages/%40mantine/core/src/components/NumberFormatter/NumberFormatter.tsx

skorphil commented 6 months ago

Here is an article how to combine all the libraries (chakra, react-number-format and react-hook-form) https://medium.com/@roquejr1307/chakra-form-hook-number-format-73ff9ed6f84

With a good explanation of hook-form's Controller. The author uses Controller for masked input and register for unmasked In the article legacy number-format is used https://s-yadav.github.io/react-number-format/docs/migration

skorphil commented 6 months ago

Well, solved number formatting issue with a more complex approach with react-number-format https://codesandbox.io/p/devbox/input-format-chakra-ui-form-hook-number-format-forked-wpdpjw

skorphil commented 6 months ago