|
@@ -3,11 +3,14 @@ import { Input } from '@/vdb/components/ui/input.js';
|
|
|
|
|
|
|
|
import { DashboardFormComponentProps } from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
import { DashboardFormComponentProps } from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
|
import { isReadonlyField } from '@/vdb/framework/form-engine/utils.js';
|
|
import { isReadonlyField } from '@/vdb/framework/form-engine/utils.js';
|
|
|
|
|
+import { ReactNode } from 'react';
|
|
|
|
|
|
|
|
export type NumberInputProps = DashboardFormComponentProps & {
|
|
export type NumberInputProps = DashboardFormComponentProps & {
|
|
|
min?: number;
|
|
min?: number;
|
|
|
max?: number;
|
|
max?: number;
|
|
|
step?: number;
|
|
step?: number;
|
|
|
|
|
+ prefix?: ReactNode;
|
|
|
|
|
+ suffix?: ReactNode;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -17,28 +20,43 @@ export type NumberInputProps = DashboardFormComponentProps & {
|
|
|
* @docsCategory form-components
|
|
* @docsCategory form-components
|
|
|
* @docsPage NumberInput
|
|
* @docsPage NumberInput
|
|
|
*/
|
|
*/
|
|
|
-export function NumberInput({ fieldDef, onChange, ...fieldProps }: Readonly<NumberInputProps>) {
|
|
|
|
|
|
|
+export function NumberInput({
|
|
|
|
|
+ fieldDef,
|
|
|
|
|
+ onChange,
|
|
|
|
|
+ prefix: overridePrefix,
|
|
|
|
|
+ suffix: overrideSuffix,
|
|
|
|
|
+ ...fieldProps
|
|
|
|
|
+}: Readonly<NumberInputProps>) {
|
|
|
const readOnly = fieldProps.disabled || isReadonlyField(fieldDef);
|
|
const readOnly = fieldProps.disabled || isReadonlyField(fieldDef);
|
|
|
const isFloat = fieldDef ? fieldDef.type === 'float' : false;
|
|
const isFloat = fieldDef ? fieldDef.type === 'float' : false;
|
|
|
const min = fieldProps.min ?? fieldDef?.ui?.min;
|
|
const min = fieldProps.min ?? fieldDef?.ui?.min;
|
|
|
const max = fieldProps.max ?? fieldDef?.ui?.max;
|
|
const max = fieldProps.max ?? fieldDef?.ui?.max;
|
|
|
const step = fieldProps.step ?? (fieldDef?.ui?.step || (isFloat ? 0.01 : 1));
|
|
const step = fieldProps.step ?? (fieldDef?.ui?.step || (isFloat ? 0.01 : 1));
|
|
|
- const prefix = fieldDef?.ui?.prefix;
|
|
|
|
|
- const suffix = fieldDef?.ui?.suffix;
|
|
|
|
|
|
|
+ const prefix = overridePrefix ?? fieldDef?.ui?.prefix;
|
|
|
|
|
+ const suffix = overrideSuffix ?? fieldDef?.ui?.suffix;
|
|
|
const shouldUseAffixedInput = prefix || suffix;
|
|
const shouldUseAffixedInput = prefix || suffix;
|
|
|
|
|
+ const value = fieldProps.value ?? '';
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
if (readOnly) return;
|
|
if (readOnly) return;
|
|
|
- const numValue = e.target.valueAsNumber;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let numValue = e.target.valueAsNumber;
|
|
|
|
|
+
|
|
|
|
|
+ if (Number.isNaN(numValue) && e.target.value) {
|
|
|
|
|
+ const normalized = e.target.value.replace(',', '.');
|
|
|
|
|
+ numValue = Number(normalized);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (Number.isNaN(numValue)) {
|
|
if (Number.isNaN(numValue)) {
|
|
|
onChange(null);
|
|
onChange(null);
|
|
|
} else {
|
|
} else {
|
|
|
- onChange(e.target.valueAsNumber);
|
|
|
|
|
|
|
+ onChange(numValue);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
if (shouldUseAffixedInput) {
|
|
if (shouldUseAffixedInput) {
|
|
|
return (
|
|
return (
|
|
|
<AffixedInput
|
|
<AffixedInput
|
|
|
{...fieldProps}
|
|
{...fieldProps}
|
|
|
|
|
+ value={value}
|
|
|
type="number"
|
|
type="number"
|
|
|
onChange={handleChange}
|
|
onChange={handleChange}
|
|
|
min={min}
|
|
min={min}
|
|
@@ -57,6 +75,7 @@ export function NumberInput({ fieldDef, onChange, ...fieldProps }: Readonly<Numb
|
|
|
type="number"
|
|
type="number"
|
|
|
onChange={handleChange}
|
|
onChange={handleChange}
|
|
|
{...fieldProps}
|
|
{...fieldProps}
|
|
|
|
|
+ value={value}
|
|
|
min={min}
|
|
min={min}
|
|
|
max={max}
|
|
max={max}
|
|
|
step={step}
|
|
step={step}
|