diff --git a/packages/preact/src/utils/getElementInput.ts b/packages/preact/src/utils/getElementInput.ts index 3d0c8de8..6dd6e278 100644 --- a/packages/preact/src/utils/getElementInput.ts +++ b/packages/preact/src/utils/getElementInput.ts @@ -39,7 +39,9 @@ export function getElementInput< ? [...((field.value.peek() || []) as string[]), value] : ((field.value.peek() || []) as string[]).filter((v) => v !== value) : type === 'number' - ? valueAsNumber + ? element instanceof HTMLSelectElement? + value ? parseFloat(value) : undefined + : valueAsNumber : type === 'boolean' ? checked : type === 'File' && files diff --git a/packages/qwik/src/utils/getElementInput.ts b/packages/qwik/src/utils/getElementInput.ts index d8138423..48ea2ddd 100644 --- a/packages/qwik/src/utils/getElementInput.ts +++ b/packages/qwik/src/utils/getElementInput.ts @@ -40,7 +40,9 @@ export function getElementInput< ? [...((field.value || []) as string[]), value] : ((field.value || []) as string[]).filter((v) => v !== value) : type === 'number' - ? valueAsNumber + ? element instanceof HTMLSelectElement? + value ? parseFloat(value) : undefined + : valueAsNumber : type === 'boolean' ? checked : type === 'File' && files diff --git a/packages/react/src/utils/getElementInput.ts b/packages/react/src/utils/getElementInput.ts index 3d0c8de8..6dd6e278 100644 --- a/packages/react/src/utils/getElementInput.ts +++ b/packages/react/src/utils/getElementInput.ts @@ -39,7 +39,9 @@ export function getElementInput< ? [...((field.value.peek() || []) as string[]), value] : ((field.value.peek() || []) as string[]).filter((v) => v !== value) : type === 'number' - ? valueAsNumber + ? element instanceof HTMLSelectElement? + value ? parseFloat(value) : undefined + : valueAsNumber : type === 'boolean' ? checked : type === 'File' && files diff --git a/packages/solid/src/utils/getElementInput.ts b/packages/solid/src/utils/getElementInput.ts index 816f82f9..775d8934 100644 --- a/packages/solid/src/utils/getElementInput.ts +++ b/packages/solid/src/utils/getElementInput.ts @@ -20,7 +20,7 @@ import type { */ export function getElementInput< TFieldValues extends FieldValues, - TFieldName extends FieldPath + TFieldName extends FieldPath, >( element: FieldElement, field: InternalFieldStore, @@ -28,27 +28,42 @@ export function getElementInput< ): FieldPathValue { const { checked, files, options, value, valueAsDate, valueAsNumber } = element as HTMLInputElement & HTMLSelectElement & HTMLTextAreaElement; - return untrack(() => - !type || type === 'string' - ? value - : type === 'string[]' - ? options + return untrack(() => { + if (!type || type === 'string') { + return value; + } + if (type === 'string[]') { + return options ? [...options] .filter((e) => e.selected && !e.disabled) .map((e) => e.value) : checked - ? [...((field.value.get() || []) as string[]), value] - : ((field.value.get() || []) as string[]).filter((v) => v !== value) - : type === 'number' - ? valueAsNumber - : type === 'boolean' - ? checked - : type === 'File' && files - ? files[0] - : type === 'File[]' && files - ? [...files] - : type === 'Date' && valueAsDate - ? valueAsDate - : field.value.get() - ) as FieldPathValue; + ? [...((field.value.get() || []) as string[]), value] + : ((field.value.get() || []) as string[]).filter((v) => v !== value); + } + + if (type === 'number') { + if (element instanceof HTMLSelectElement) { + return value ? parseFloat(value) : undefined; + } + + return valueAsNumber; + } + + if (type === 'boolean') { + return checked; + } + + if (type === 'File') { + return files ? files[0] : undefined; + } + + if (type === 'File[]') { + return files ? [...files] : undefined; + } + + if (type === 'Date') { + return valueAsDate; + } + }) as FieldPathValue; } diff --git a/playgrounds/preact/src/components/Select.tsx b/playgrounds/preact/src/components/Select.tsx index 0a38706c..787c07e5 100644 --- a/playgrounds/preact/src/components/Select.tsx +++ b/playgrounds/preact/src/components/Select.tsx @@ -7,14 +7,14 @@ import { AngleDownIcon } from '../icons'; import { InputError } from './InputError'; import { InputLabel } from './InputLabel'; -type SelectProps = { +type SelectProps = { name: string; - value: ReadonlySignal; + value: ReadonlySignal; ref: Ref; onInput: JSX.GenericEventHandler; onChange: JSX.GenericEventHandler; onBlur: JSX.FocusEventHandler; - options: { label: string; value: string }[]; + options: { label: string; value: T }[]; multiple?: boolean; size?: number; placeholder?: string; @@ -29,7 +29,7 @@ type SelectProps = { * decorations can be displayed in or around the field to communicate the * entry requirements. */ -export const Select = forwardRef( +export const Select = forwardRef>( ({ value, options, label, error, ...props }, ref) => { const { name, required, multiple, placeholder } = props; @@ -37,7 +37,7 @@ export const Select = forwardRef( const values = useComputed(() => Array.isArray(value.value) ? value.value - : value.value && typeof value.value === 'string' + : (typeof props.value === 'string' || typeof props.value === 'number') ? [value.value] : [] ); diff --git a/playgrounds/preact/src/routes/special.tsx b/playgrounds/preact/src/routes/special.tsx index f7a8a7be..19729c11 100644 --- a/playgrounds/preact/src/routes/special.tsx +++ b/playgrounds/preact/src/routes/special.tsx @@ -20,6 +20,7 @@ type SpecialForm = { select: { array: string[]; string: string; + number: number; }; file: { list: File[]; @@ -130,6 +131,23 @@ export default function SpecialPage() { )} + + {(field, props) => ( + + )} + + {(field, props) => ( >(isBlob)), @@ -53,6 +54,7 @@ const getInitFormValues = (): InitialValues => ({ select: { array: [], string: undefined, + number: undefined, }, file: { list: [], @@ -206,6 +208,23 @@ export default component$(() => { )} + + {(field, props) => ( + + )} + + + {(field, props) => ( = { ref: (element: HTMLSelectElement) => void; name: string; - value: string | string[] | undefined; + value: T | T[] | undefined; onInput: JSX.EventHandler; onChange: JSX.EventHandler; onBlur: JSX.EventHandler; - options: { label: string; value: string }[]; + options: { label: string; value: T }[]; multiple?: boolean; size?: string | number; placeholder?: string; @@ -26,7 +26,7 @@ type SelectProps = { * decorations can be displayed in or around the field to communicate the * entry requirements. */ -export function Select(props: SelectProps) { +export function Select(props: SelectProps) { // Split select element props const [, selectProps] = splitProps(props, [ 'class', @@ -40,7 +40,7 @@ export function Select(props: SelectProps) { const getValues = createMemo(() => Array.isArray(props.value) ? props.value - : typeof props.value === 'string' + : (typeof props.value === 'string' || typeof props.value === 'number') ? [props.value] : [] ); @@ -61,7 +61,7 @@ export function Select(props: SelectProps) { ? 'border-red-600/50 dark:border-red-400/50' : 'border-slate-200 hover:border-slate-300 focus:border-sky-600/50 dark:border-slate-800 dark:hover:border-slate-700 dark:focus:border-sky-400/50', props.multiple ? 'py-5' : 'h-14 md:h-16 lg:h-[70px]', - props.placeholder && !props.value?.length && 'text-slate-500' + props.placeholder && !props.value && 'text-slate-500' )} id={props.name} aria-invalid={!!props.error} @@ -72,7 +72,7 @@ export function Select(props: SelectProps) { {({ label, value }) => ( - )} diff --git a/playgrounds/solid/src/routes/special.tsx b/playgrounds/solid/src/routes/special.tsx index fd157a26..e8f25909 100644 --- a/playgrounds/solid/src/routes/special.tsx +++ b/playgrounds/solid/src/routes/special.tsx @@ -21,6 +21,7 @@ type SpecialForm = { select: { array: string[]; string: string; + number: number; }; file: { list: File[]; @@ -138,6 +139,23 @@ export default function SpecialPage() { )} + + {(field, props) => ( +