From 22fc1bedc084d101a759e5c7c70b768e66255988 Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Wed, 31 Jul 2024 16:44:28 +0200 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20Formate=20la=20valeur=20affich?= =?UTF-8?q?=C3=A9e=20dans=20le=20champ=20NumberInput?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/form/question/NumberInput.tsx | 45 ++++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/components/form/question/NumberInput.tsx b/src/components/form/question/NumberInput.tsx index a30a9ed1f..0d1105bcd 100644 --- a/src/components/form/question/NumberInput.tsx +++ b/src/components/form/question/NumberInput.tsx @@ -28,11 +28,49 @@ export default function NumberInput({ const handleValueChange = (event: React.ChangeEvent) => { const inputValue = event.target.value + if (inputValue === '') { setValue(undefined) } else { - setValue(Number(inputValue)) + setValue(unformatNumber(inputValue.replace(/[^0-9.-]+/g, ''))) + } + } + + function formatNumber(number: number) { + // Créer un formateur de nombre pour la locale de l'utilisateur + const formatter = new Intl.NumberFormat(locale, { + style: 'decimal', + useGrouping: true, // Activer les séparateurs de milliers + maximumFractionDigits: 2, + }) + + // Formater la valeur numérique + return formatter.format(number) + } + + function unformatNumber(number: string) { + // Supprimer les séparateurs de milliers + return Number(number.replace(/[^0-9.-]+/g, '')) + } + + function handleInput(event: React.ChangeEvent) { + if (!event.target) return + + // Prevent the user from typing non-numeric characters + // with a regex match + const match = (event.target as HTMLInputElement).value.match(/[^0-9.-]+/g) + if (match && (event.target as HTMLInputElement).value.match(/[^0-9.-]+/g)) { + event.target.value = event.target.value.replace(match[0], '') + return } + + // Format the number as the user types + const inputValue = (event.target as HTMLInputElement).value + const formattedValue = formatNumber(Number(inputValue)) + + // Update the input value + event.target.value = formattedValue + handleValueChange(event) } return ( @@ -41,10 +79,9 @@ export default function NumberInput({ From 8f9ad0111b5a729115633c496beafd085c9c7923 Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Wed, 31 Jul 2024 16:48:10 +0200 Subject: [PATCH 2/6] Update NumberInput.tsx --- src/components/form/question/NumberInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/form/question/NumberInput.tsx b/src/components/form/question/NumberInput.tsx index 0d1105bcd..d366a5f59 100644 --- a/src/components/form/question/NumberInput.tsx +++ b/src/components/form/question/NumberInput.tsx @@ -58,14 +58,14 @@ export default function NumberInput({ // Prevent the user from typing non-numeric characters // with a regex match - const match = (event.target as HTMLInputElement).value.match(/[^0-9.-]+/g) - if (match && (event.target as HTMLInputElement).value.match(/[^0-9.-]+/g)) { + const match = event.target.value.match(/[^0-9.-]+/g) + if (match && event.target.value.match(/[^0-9.-]+/g)) { event.target.value = event.target.value.replace(match[0], '') return } // Format the number as the user types - const inputValue = (event.target as HTMLInputElement).value + const inputValue = event.target.value const formattedValue = formatNumber(Number(inputValue)) // Update the input value From fb24c8f2f77c49ef7404ed4e561ddf4963cf5146 Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Thu, 1 Aug 2024 10:56:16 +0200 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20Retire=20abb=C3=A9rations=20+=20util?= =?UTF-8?q?ise=20.toLocaleString?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/form/question/NumberInput.tsx | 31 ++++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/components/form/question/NumberInput.tsx b/src/components/form/question/NumberInput.tsx index d366a5f59..b24321613 100644 --- a/src/components/form/question/NumberInput.tsx +++ b/src/components/form/question/NumberInput.tsx @@ -37,15 +37,9 @@ export default function NumberInput({ } function formatNumber(number: number) { - // Créer un formateur de nombre pour la locale de l'utilisateur - const formatter = new Intl.NumberFormat(locale, { - style: 'decimal', - useGrouping: true, // Activer les séparateurs de milliers - maximumFractionDigits: 2, + return number.toLocaleString(locale, { + maximumFractionDigits: 1, }) - - // Formater la valeur numérique - return formatter.format(number) } function unformatNumber(number: string) { @@ -56,11 +50,14 @@ export default function NumberInput({ function handleInput(event: React.ChangeEvent) { if (!event.target) return + const { value } = event.target + // Prevent the user from typing non-numeric characters // with a regex match - const match = event.target.value.match(/[^0-9.-]+/g) - if (match && event.target.value.match(/[^0-9.-]+/g)) { - event.target.value = event.target.value.replace(match[0], '') + const match = value.match(/[^0-9.-]+/g) + + if (match) { + event.target.value = value.replace(match[0], '') return } @@ -73,6 +70,8 @@ export default function NumberInput({ handleValueChange(event) } + const formattedValue = formatNumber(Number(value)) + return (
@@ -81,14 +80,8 @@ export default function NumberInput({ className={`focus:ring-primary max-w-[8rem] rounded-xl border-2 border-primary-200 bg-white p-2 text-right transition-colors focus:border-primary-700 focus:ring-2 md:max-w-full`} inputMode="numeric" min={min} - value={isMissing ? '' : formatNumber(Number(value))} - placeholder={ - isMissing - ? value?.toLocaleString(locale, { - maximumFractionDigits: 1, - }) ?? '0' - : '0' - } + value={isMissing ? '' : formattedValue} + placeholder={isMissing ? formattedValue ?? '0' : '0'} onChange={handleValueChange} onInput={handleInput} id={id} From 94048525881ec4a8faf18a8027755395e441393b Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Mon, 19 Aug 2024 14:02:47 +0200 Subject: [PATCH 4/6] fix: Passing floats to the input being broken --- src/app/layout.tsx | 1 + src/components/form/question/NumberInput.tsx | 62 ++++++++++---------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2449499d2..5ff3aa52e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -51,6 +51,7 @@ export const marianne = localFont({ export default async function RootLayout({ children }: PropsWithChildren) { const lang = currentLocale() const region = await getGeolocation() + const migrationInstructions = await getMigrationInstructions() return ( diff --git a/src/components/form/question/NumberInput.tsx b/src/components/form/question/NumberInput.tsx index b24321613..ac7eda70f 100644 --- a/src/components/form/question/NumberInput.tsx +++ b/src/components/form/question/NumberInput.tsx @@ -1,6 +1,8 @@ +'use client' + import Trans from '@/components/translation/Trans' import { useLocale } from '@/hooks/useLocale' -import { HTMLAttributes } from 'react' +import { HTMLAttributes, useCallback, useEffect, useState } from 'react' import { DebounceInput } from 'react-debounce-input' import { twMerge } from 'tailwind-merge' @@ -24,8 +26,24 @@ export default function NumberInput({ id, ...props }: HTMLAttributes & Props) { + const [formattedValue, setFormattedValue] = useState('') + const locale = useLocale() + const formatNumberWithSpaces = useCallback( + (number: number) => { + return new Intl.NumberFormat(locale, { + minimumFractionDigits: 0, + maximumFractionDigits: 2, + }).format(number) + }, + [locale] + ) + + const unformatNumber = (formattedNumber: string) => { + return Number(formattedNumber.replace(/\s+/g, '').replace(/,/g, '.')) + } + const handleValueChange = (event: React.ChangeEvent) => { const inputValue = event.target.value @@ -36,42 +54,22 @@ export default function NumberInput({ } } - function formatNumber(number: number) { - return number.toLocaleString(locale, { - maximumFractionDigits: 1, - }) - } - - function unformatNumber(number: string) { - // Supprimer les séparateurs de milliers - return Number(number.replace(/[^0-9.-]+/g, '')) - } - - function handleInput(event: React.ChangeEvent) { - if (!event.target) return - - const { value } = event.target + useEffect(() => { + setFormattedValue( + formatNumberWithSpaces(Number(`${value}`.replace(/\s+/g, ''))) + ) + }, [value, formatNumberWithSpaces]) - // Prevent the user from typing non-numeric characters - // with a regex match - const match = value.match(/[^0-9.-]+/g) + const handleInput = (event: React.FormEvent) => { + const inputValue = (event.target as HTMLInputElement).value - if (match) { - event.target.value = value.replace(match[0], '') - return + if (inputValue === '') { + setValue(undefined) + } else { + setValue(unformatNumber(inputValue)) } - - // Format the number as the user types - const inputValue = event.target.value - const formattedValue = formatNumber(Number(inputValue)) - - // Update the input value - event.target.value = formattedValue - handleValueChange(event) } - const formattedValue = formatNumber(Number(value)) - return (
From e853c21b82a51911ed7df16ba2a6eb77276a9501 Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Thu, 29 Aug 2024 12:28:37 +0200 Subject: [PATCH 5/6] :sparkles: Use react-number-format --- package.json | 1 + src/components/form/question/NumberInput.tsx | 66 +++++++------------- yarn.lock | 5 ++ 3 files changed, 27 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index 162979100..bdaa70219 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "react-hook-form": "^7.51.2", "react-i18next": "^14.0.5", "react-modal": "^3.16.1", + "react-number-format": "^5.4.1", "react-select": "^5.8.0", "react-toastify": "^10.0.5", "react-tooltip": "^5.26.3", diff --git a/src/components/form/question/NumberInput.tsx b/src/components/form/question/NumberInput.tsx index ac7eda70f..fc1d4013d 100644 --- a/src/components/form/question/NumberInput.tsx +++ b/src/components/form/question/NumberInput.tsx @@ -1,9 +1,7 @@ -'use client' - import Trans from '@/components/translation/Trans' -import { useLocale } from '@/hooks/useLocale' -import { HTMLAttributes, useCallback, useEffect, useState } from 'react' +import { forwardRef, HTMLAttributes } from 'react' import { DebounceInput } from 'react-debounce-input' +import { NumericFormat } from 'react-number-format' import { twMerge } from 'tailwind-merge' type Props = { @@ -14,77 +12,55 @@ type Props = { min?: number id?: string className?: string + defaultValue?: string | number | null | undefined } +forwardRef(function DebouncedInputWithForwardRef( + props: React.ComponentProps, + ref: React.ForwardedRef +) { + return +}) + export default function NumberInput({ unit, value = '', isMissing, setValue, - min = 0, className, id, ...props }: HTMLAttributes & Props) { - const [formattedValue, setFormattedValue] = useState('') - - const locale = useLocale() - - const formatNumberWithSpaces = useCallback( - (number: number) => { - return new Intl.NumberFormat(locale, { - minimumFractionDigits: 0, - maximumFractionDigits: 2, - }).format(number) - }, - [locale] - ) - - const unformatNumber = (formattedNumber: string) => { - return Number(formattedNumber.replace(/\s+/g, '').replace(/,/g, '.')) - } - const handleValueChange = (event: React.ChangeEvent) => { const inputValue = event.target.value if (inputValue === '') { setValue(undefined) } else { - setValue(unformatNumber(inputValue.replace(/[^0-9.-]+/g, ''))) + setValue(unformatNumber(inputValue)) } } - useEffect(() => { - setFormattedValue( - formatNumberWithSpaces(Number(`${value}`.replace(/\s+/g, ''))) - ) - }, [value, formatNumberWithSpaces]) - - const handleInput = (event: React.FormEvent) => { - const inputValue = (event.target as HTMLInputElement).value - - if (inputValue === '') { - setValue(undefined) - } else { - setValue(unformatNumber(inputValue)) - } + function unformatNumber(number: string) { + // Supprimer les séparateurs de milliers + return Number(number.replace(/[^0-9.-]+/g, '')) } return (
- + {unit ? (   diff --git a/yarn.lock b/yarn.lock index c5a915dbf..d5454d676 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8735,6 +8735,11 @@ react-move@^2.7.0: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" +react-number-format@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.4.1.tgz#ca191af06c4618a823874efa486df50a1abbfc18" + integrity sha512-NICOjo/70dcAiwVmH6zMWoZrTQDlBrEXV/f7S0t/ewlpzp4z00pasg5G1yBX6NHLafwOF3QZ+VvK/XApwSKxdA== + react-select@^5.8.0: version "5.8.0" resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.8.0.tgz#bd5c467a4df223f079dd720be9498076a3f085b5" From 6d26f008dce118d806ac234f45035f5a783637ed Mon Sep 17 00:00:00 2001 From: Benjamin Arias Date: Mon, 2 Sep 2024 10:54:33 +0200 Subject: [PATCH 6/6] :recycle: Fixes after review --- src/app/layout.tsx | 1 - src/components/form/question/NumberInput.tsx | 17 +++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5ff3aa52e..2449499d2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -51,7 +51,6 @@ export const marianne = localFont({ export default async function RootLayout({ children }: PropsWithChildren) { const lang = currentLocale() const region = await getGeolocation() - const migrationInstructions = await getMigrationInstructions() return ( diff --git a/src/components/form/question/NumberInput.tsx b/src/components/form/question/NumberInput.tsx index fc1d4013d..897fd848c 100644 --- a/src/components/form/question/NumberInput.tsx +++ b/src/components/form/question/NumberInput.tsx @@ -1,5 +1,5 @@ import Trans from '@/components/translation/Trans' -import { forwardRef, HTMLAttributes } from 'react' +import { HTMLAttributes } from 'react' import { DebounceInput } from 'react-debounce-input' import { NumericFormat } from 'react-number-format' import { twMerge } from 'tailwind-merge' @@ -15,12 +15,10 @@ type Props = { defaultValue?: string | number | null | undefined } -forwardRef(function DebouncedInputWithForwardRef( - props: React.ComponentProps, - ref: React.ForwardedRef -) { - return -}) +function unformatNumber(number: string) { + // Supprimer les séparateurs de milliers + return Number(number.replace(/[^0-9.-]+/g, '')) +} export default function NumberInput({ unit, @@ -41,11 +39,6 @@ export default function NumberInput({ } } - function unformatNumber(number: string) { - // Supprimer les séparateurs de milliers - return Number(number.replace(/[^0-9.-]+/g, '')) - } - return (