diff --git a/frontend/src/components/NumberInput/index.tsx b/frontend/src/components/NumberInput/index.tsx new file mode 100644 index 00000000..9093d620 --- /dev/null +++ b/frontend/src/components/NumberInput/index.tsx @@ -0,0 +1,60 @@ +import clsx from 'clsx' +import React, { forwardRef, useEffect, useState } from 'react' +import Box, { BoxProps } from 'src/theme/components/Box' + +import * as styles from './style.css' + +const formatNumber = (value: string) => { + const numericValue = parseFloat(value.replace(/[^0-9.]/g, '')) + if (isNaN(numericValue)) return '' + + return new Intl.NumberFormat('en-US', { + style: 'decimal', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(numericValue) +} + +type NumberInputProps = { + addon?: React.ReactNode +} & BoxProps + +const NumberInput = forwardRef( + ({ addon, className, value, onChange, onBlur, ...props }, ref) => { + const [inputValue, setInputValue] = useState('') + useEffect(() => { + if (value !== undefined && value !== null) { + setInputValue(formatNumber(value.toString())) + } + }, [value]) + + const handleInputEvent = (event: React.ChangeEvent) => { + setInputValue(event.target.value.replace(/[^0-9.]/g, '')) + onChange && onChange(event) + } + const handleBlurEvent = (event: React.FocusEvent) => { + setInputValue(formatNumber(event.target.value)) + onBlur && onBlur(event) + } + + return ( + + {addon} + + + ) + } +) + +NumberInput.displayName = 'NumberInput' + +export default NumberInput diff --git a/frontend/src/components/NumberInput/style.css.ts b/frontend/src/components/NumberInput/style.css.ts new file mode 100644 index 00000000..116153d2 --- /dev/null +++ b/frontend/src/components/NumberInput/style.css.ts @@ -0,0 +1,33 @@ +import { sprinkles } from 'src/theme/css/sprinkles.css' + +export const inputContainer = sprinkles({ + display: 'flex', + alignItems: 'center', + borderRadius: '10', + borderWidth: '1px', + borderStyle: 'solid', + overflow: 'hidden', + padding: '12', + fontSize: '16', + borderColor: { + default: 'border1', + hover: 'accent', + }, + gap: '8', + transitionDuration: '125', + backgroundColor: 'bg1', +}) + +export const input = sprinkles({ + fontSize: '16', + position: 'relative', + whiteSpace: 'nowrap', + outline: 'none', + color: { + default: 'text1', + placeholder: 'text2', + }, + background: 'none', + border: 'none', + width: 'full', +}) diff --git a/frontend/src/pages/Launch/index.tsx b/frontend/src/pages/Launch/index.tsx index 7e6186bb..96823362 100644 --- a/frontend/src/pages/Launch/index.tsx +++ b/frontend/src/pages/Launch/index.tsx @@ -5,6 +5,7 @@ import { useCallback, useState } from 'react' import { useFieldArray, useForm } from 'react-hook-form' import { IconButton, PrimaryButton, SecondaryButton } from 'src/components/Button' import Input from 'src/components/Input' +import NumberInput from 'src/components/NumberInput' import { TOKEN_CLASS_HASH, UDC } from 'src/constants/contracts' import Box from 'src/theme/components/Box' import { Column, Row } from 'src/theme/components/Flex' @@ -35,7 +36,7 @@ const schema = z.object({ symbol: z.string().min(1), initialRecipientAddress: address, ownerAddress: address, - initialSupply: z.number().min(0), + initialSupply: z.number().min(1), holders: z.array(holder), }) @@ -60,7 +61,6 @@ export default function LaunchPage() { reset: resetForm, } = useForm>({ resolver: zodResolver(schema), - defaultValues: { initialSupply: 10_000_000_000 }, }) const { fields, append, remove } = useFieldArray({ @@ -181,7 +181,7 @@ export default function LaunchPage() { Initial Supply - + {errors.initialSupply?.message ? {errors.initialSupply.message} : null}