From 94822d902f22598b2ffe57795eb4e9633656c44a Mon Sep 17 00:00:00 2001 From: "web@ppanel" Date: Thu, 13 Mar 2025 17:32:36 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(input):=20Add=20minimum=20valu?= =?UTF-8?q?e=20constraint=20and=20enhance=20number=20handling=20in=20Enhan?= =?UTF-8?q?cedInput?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/user-profile/basic-info-form.tsx | 3 + .../src/custom-components/enhanced-input.tsx | 74 +++++++++++++++---- packages/ui/src/utils/unit-conversions.ts | 49 ++++++------ 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/apps/admin/app/dashboard/user/[id]/user-profile/basic-info-form.tsx b/apps/admin/app/dashboard/user/[id]/user-profile/basic-info-form.tsx index 752446f..76e4048 100644 --- a/apps/admin/app/dashboard/user/[id]/user-profile/basic-info-form.tsx +++ b/apps/admin/app/dashboard/user/[id]/user-profile/basic-info-form.tsx @@ -113,6 +113,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () = unitConversion('centsToDollars', value)} @@ -136,6 +137,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () = unitConversion('centsToDollars', value)} @@ -159,6 +161,7 @@ export function BasicInfoForm({ user, refetch }: { user: API.User; refetch: () = unitConversion('centsToDollars', value)} diff --git a/packages/ui/src/custom-components/enhanced-input.tsx b/packages/ui/src/custom-components/enhanced-input.tsx index cd44de2..8b8f13a 100644 --- a/packages/ui/src/custom-components/enhanced-input.tsx +++ b/packages/ui/src/custom-components/enhanced-input.tsx @@ -6,7 +6,7 @@ export interface EnhancedInputProps extends Omit, 'prefix'> { prefix?: string | ReactNode; suffix?: string | ReactNode; - formatInput?: (value: string | number) => string; + formatInput?: (value: string | number) => string | number; formatOutput?: (value: string | number) => string | number; onValueChange?: (value: string | number) => void; onValueBlur?: (value: string | number) => void; @@ -26,50 +26,94 @@ export function EnhancedInput({ ...props }: EnhancedInputProps) { const getProcessedValue = (inputValue: unknown) => { - const newValue = inputValue === '' || inputValue === 0 ? '' : String(inputValue ?? ''); + if (inputValue === '' || inputValue === 0 || inputValue === '0') return ''; + const newValue = String(inputValue ?? ''); return formatInput ? formatInput(newValue) : newValue; }; - const [value, setValue] = useState(() => getProcessedValue(initialValue)); + const [value, setValue] = useState(() => getProcessedValue(initialValue)); + // @ts-expect-error - This is a controlled component + const [internalValue, setInternalValue] = useState(initialValue ?? ''); useEffect(() => { - if (initialValue !== value) { + if (initialValue !== internalValue) { const newValue = getProcessedValue(initialValue); - if (value !== newValue) setValue(newValue); + if (value !== newValue) { + setValue(newValue); + // @ts-expect-error - This is a controlled component + setInternalValue(initialValue ?? ''); + } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialValue, formatInput]); - const processValue = (inputValue: string) => { + const processValue = (inputValue: string | number) => { let processedValue: number | string = inputValue?.toString().trim(); + + if (processedValue === '0' && props.type === 'number') { + return 0; + } + if (processedValue && props.type === 'number') processedValue = Number(processedValue); return formatOutput ? formatOutput(processedValue) : processedValue; }; const handleChange = (e: ChangeEvent) => { let inputValue = e.target.value; - if (props.type === 'number' && inputValue) { - const numericValue = Number(inputValue); - if (!isNaN(numericValue)) { - const min = Number.isFinite(props.min) ? props.min : -Infinity; - const max = Number.isFinite(props.max) ? props.max : Infinity; - inputValue = String(Math.max(min!, Math.min(max!, numericValue))); + + if (props.type === 'number') { + if (inputValue === '0') { + setValue(''); + setInternalValue(0); + onValueChange?.(0); + return; + } + + if (/^-?\d*\.?\d*$/.test(inputValue) || inputValue === '-' || inputValue === '.') { + const numericValue = Number(inputValue); + if (!isNaN(numericValue) && inputValue !== '-' && inputValue !== '.') { + const min = Number.isFinite(props.min) ? props.min : -Infinity; + const max = Number.isFinite(props.max) ? props.max : Infinity; + const constrainedValue = Math.max(min!, Math.min(max!, numericValue)); + inputValue = String(constrainedValue); + setInternalValue(constrainedValue); + } else { + setInternalValue(inputValue); + } + setValue(inputValue === '0' ? '' : inputValue); } - setValue(inputValue === '0' ? '' : inputValue); } else { setValue(inputValue); + setInternalValue(inputValue); } + const outputValue = processValue(inputValue); - console.log(); onValueChange?.(outputValue); }; const handleBlur = () => { + if (props.type === 'number' && value) { + if (value === '-' || value === '.') { + setValue(''); + setInternalValue(''); + onValueBlur?.(''); + return; + } + + // 确保0值显示为空 + if (value === '0') { + setValue(''); + onValueBlur?.(0); + return; + } + } + const outputValue = processValue(value); if ((initialValue || '') !== outputValue) { onValueBlur?.(outputValue); } }; + const renderPrefix = () => { return typeof prefix === 'string' ? (
{prefix}
@@ -77,6 +121,7 @@ export function EnhancedInput({ prefix ); }; + const renderSuffix = () => { return typeof suffix === 'string' ? (
{suffix}
@@ -95,6 +140,7 @@ export function EnhancedInput({ > {renderPrefix()} = { + centsToDollars: { formula: 'value / 100', precision: 2 }, + dollarsToCents: { formula: 'value * 100', precision: 0 }, + bitsToMb: { formula: 'value / 1024 / 1024', precision: 2 }, + mbToBits: { formula: 'value * 1024 * 1024', precision: 0 }, + bytesToGb: { formula: 'value / 1024 / 1024 / 1024', precision: 2 }, + gbToBytes: { formula: 'value * 1024 * 1024 * 1024', precision: 0 }, +}; + +export function unitConversion(type: ConversionType, value?: number | string) { + if (!value) return 0; + + const config = conversionConfig[type]; + if (!config) throw new Error('Invalid conversion type'); + + const formula = config.formula.replace('value', `${value}`); + const result = evaluate(formula); + return Number(format(result, { notation: 'fixed', precision: config.precision })); } export function evaluateWithPrecision(expression: string) { const result = evaluate(expression); - const formatted = format(result, { notation: 'fixed', precision: 2 }); - return Number(formatted); }