feat: enhance form components with additional props for validation and tooltips; add OptionsField component

This commit is contained in:
twwu 2025-04-18 11:32:23 +08:00
parent 0345eb4659
commit 6eef5990c9
6 changed files with 81 additions and 11 deletions

View File

@ -2,18 +2,26 @@ import React from 'react'
import { useFieldContext } from '../..'
import Label from '../label'
import cn from '@/utils/classnames'
import type { InputNumberProps } from '../../../input-number'
import { InputNumber } from '../../../input-number'
type TextFieldProps = {
label: string
isRequired?: boolean
showOptional?: boolean
tooltip?: string
className?: string
labelClassName?: string
}
} & Omit<InputNumberProps, 'id' | 'value' | 'onChange' | 'onBlur'>
const NumberInputField = ({
label,
isRequired,
showOptional,
tooltip,
className,
labelClassName,
...inputProps
}: TextFieldProps) => {
const field = useFieldContext<number | undefined>()
@ -22,13 +30,17 @@ const NumberInputField = ({
<Label
htmlFor={field.name}
label={label}
labelClassName={labelClassName}
isRequired={isRequired}
showOptional={showOptional}
tooltip={tooltip}
className={labelClassName}
/>
<InputNumber
id={field.name}
value={field.state.value}
onChange={value => field.handleChange(value)}
onBlur={field.handleBlur}
{...inputProps}
/>
</div>
)

View File

@ -0,0 +1,34 @@
import cn from '@/utils/classnames'
import { useFieldContext } from '../..'
import Label from '../label'
import ConfigSelect from '@/app/components/app/configuration/config-var/config-select'
type OptionsFieldProps = {
label: string;
className?: string;
labelClassName?: string;
}
const OptionsField = ({
label,
className,
labelClassName,
}: OptionsFieldProps) => {
const field = useFieldContext<string[]>()
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
className={labelClassName}
/>
<ConfigSelect
options={field.state.value}
onChange={value => field.handleChange(value)}
/>
</div>
)
}
export default OptionsField

View File

@ -11,6 +11,9 @@ type SelectOption = {
type SelectFieldProps = {
label: string
options: SelectOption[]
isRequired?: boolean
showOptional?: boolean
tooltip?: string
className?: string
labelClassName?: string
}
@ -18,6 +21,9 @@ type SelectFieldProps = {
const SelectField = ({
label,
options,
isRequired,
showOptional,
tooltip,
className,
labelClassName,
}: SelectFieldProps) => {
@ -27,8 +33,11 @@ const SelectField = ({
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
className={labelClassName}
label={label}
isRequired={isRequired}
showOptional={showOptional}
tooltip={tooltip}
className={labelClassName}
/>
<PureSelect
value={field.state.value}

View File

@ -1,19 +1,26 @@
import React from 'react'
import { useFieldContext } from '../..'
import Input from '../../../input'
import Input, { type InputProps } from '../../../input'
import Label from '../label'
import cn from '@/utils/classnames'
type TextFieldProps = {
label: string
isRequired?: boolean
showOptional?: boolean
tooltip?: string
className?: string
labelClassName?: string
}
} & Omit<InputProps, 'className' | 'onChange' | 'onBlur' | 'value' | 'id'>
const TextField = ({
label,
isRequired,
showOptional,
tooltip,
className,
labelClassName,
...inputProps
}: TextFieldProps) => {
const field = useFieldContext<string>()
@ -22,13 +29,17 @@ const TextField = ({
<Label
htmlFor={field.name}
label={label}
labelClassName={labelClassName}
isRequired={isRequired}
showOptional={showOptional}
tooltip={tooltip}
className={labelClassName}
/>
<Input
id={field.name}
value={field.state.value}
onChange={e => field.handleChange(e.target.value)}
onBlur={field.handleBlur}
{...inputProps}
/>
</div>
)

View File

@ -1,14 +1,14 @@
import cn from '@/utils/classnames'
import Tooltip from '../../tooltip'
import { useTranslation } from 'react-i18next'
type LabelProps = {
export type LabelProps = {
htmlFor: string
label: string
isRequired?: boolean
showOptional?: boolean
tooltip?: string
className?: string
labelClassName?: string
}
const Label = ({
@ -17,17 +17,19 @@ const Label = ({
isRequired,
showOptional,
tooltip,
labelClassName,
className,
}: LabelProps) => {
const { t } = useTranslation()
return (
<div className='flex h-6 items-center'>
<label
htmlFor={htmlFor}
className={cn('system-sm-medium text-text-secondary', labelClassName)}
className={cn('system-sm-medium text-text-secondary', className)}
>
{label}
</label>
{showOptional && <div className='system-xs-regular ml-1 text-text-tertiary'>(Optional)</div>}
{showOptional && <div className='system-xs-regular ml-1 text-text-tertiary'>{t('common.label.optional')}</div>}
{isRequired && <div className='system-xs-regular ml-1 text-text-destructive-secondary'>*</div>}
{tooltip && (
<Tooltip

View File

@ -3,6 +3,7 @@ import TextField from './components/field/text'
import NumberInputField from './components/field/number-input'
import CheckboxField from './components/field/checkbox'
import SelectField from './components/field/select'
import OptionsField from './components/field/options'
import SubmitButton from './components/form/submit-button'
export const { fieldContext, useFieldContext, formContext, useFormContext }
@ -14,6 +15,7 @@ export const { useAppForm, withForm } = createFormHook({
NumberInputField,
CheckboxField,
SelectField,
OptionsField,
},
formComponents: {
SubmitButton,