| @@ -12,6 +12,7 @@ import { SimpleSelect } from '@/app/components/base/select' | |||
| import TagInput from '@/app/components/base/tag-input' | |||
| export type ParameterValue = number | string | string[] | boolean | undefined | |||
| type ParameterItemProps = { | |||
| parameterRule: ModelParameterRule | |||
| value?: ParameterValue | |||
| @@ -28,48 +29,29 @@ const ParameterItem: FC<ParameterItemProps> = ({ | |||
| }) => { | |||
| const language = useLanguage() | |||
| const [localValue, setLocalValue] = useState(value) | |||
| const mergedValue = isNullOrUndefined(value) ? localValue : value | |||
| const getDefaultValue = () => { | |||
| let defaultValue: ParameterValue | |||
| if (parameterRule.type === 'int' || parameterRule.type === 'float') { | |||
| if (isNullOrUndefined(parameterRule.default)) { | |||
| if (parameterRule.min) | |||
| defaultValue = parameterRule.min | |||
| else | |||
| defaultValue = 0 | |||
| } | |||
| else { | |||
| defaultValue = parameterRule.default | |||
| } | |||
| } | |||
| if (parameterRule.type === 'string' && !parameterRule.options?.length) | |||
| defaultValue = parameterRule.default || '' | |||
| if (parameterRule.type === 'string' && parameterRule.options?.length) | |||
| defaultValue = parameterRule.default || '' | |||
| if (parameterRule.type === 'boolean') | |||
| if (parameterRule.type === 'int' || parameterRule.type === 'float') | |||
| defaultValue = isNullOrUndefined(parameterRule.default) ? (parameterRule.min || 0) : parameterRule.default | |||
| else if (parameterRule.type === 'string') | |||
| defaultValue = parameterRule.options?.length ? (parameterRule.default || '') : (parameterRule.default || '') | |||
| else if (parameterRule.type === 'boolean') | |||
| defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : false | |||
| if (parameterRule.type === 'tag') | |||
| else if (parameterRule.type === 'tag') | |||
| defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : [] | |||
| return defaultValue | |||
| } | |||
| const renderValue = isNullOrUndefined(mergedValue) ? getDefaultValue() : mergedValue | |||
| const handleChange = (v: ParameterValue) => { | |||
| setLocalValue(v) | |||
| const renderValue = value ?? localValue ?? getDefaultValue() | |||
| if (onChange) { | |||
| if (parameterRule.name === 'stop') | |||
| onChange(v) | |||
| else if (!isNullOrUndefined(value)) | |||
| onChange(v) | |||
| } | |||
| const handleInputChange = (newValue: ParameterValue) => { | |||
| setLocalValue(newValue) | |||
| if (onChange && (parameterRule.name === 'stop' || !isNullOrUndefined(value))) | |||
| onChange(newValue) | |||
| } | |||
| const handleNumberInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||
| @@ -81,45 +63,127 @@ const ParameterItem: FC<ParameterItemProps> = ({ | |||
| if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!) | |||
| num = parameterRule.min as number | |||
| handleChange(num) | |||
| handleInputChange(num) | |||
| } | |||
| const handleSlideChange = (num: number) => { | |||
| handleChange(num) | |||
| handleInputChange(num) | |||
| } | |||
| const handleRadioChange = (v: number) => { | |||
| handleChange(v === 1) | |||
| handleInputChange(v === 1) | |||
| } | |||
| const handleStringInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||
| handleChange(e.target.value) | |||
| handleInputChange(e.target.value) | |||
| } | |||
| const handleSelect = (option: { value: string | number; name: string }) => { | |||
| handleChange(option.value) | |||
| handleInputChange(option.value) | |||
| } | |||
| const handleTagChange = (newSequences: string[]) => { | |||
| handleChange(newSequences) | |||
| handleInputChange(newSequences) | |||
| } | |||
| const handleSwitch = (checked: boolean) => { | |||
| if (onSwitch) { | |||
| let assignValue: ParameterValue = localValue | |||
| if (isNullOrUndefined(localValue)) | |||
| assignValue = getDefaultValue() | |||
| const assignValue: ParameterValue = localValue || getDefaultValue() | |||
| onSwitch(checked, assignValue) | |||
| } | |||
| } | |||
| const numberInputWithSlide = (parameterRule.type === 'int' || parameterRule.type === 'float') | |||
| const renderInput = () => { | |||
| const numberInputWithSlide = (parameterRule.type === 'int' || parameterRule.type === 'float') | |||
| && !isNullOrUndefined(parameterRule.min) | |||
| && !isNullOrUndefined(parameterRule.max) | |||
| const numberInput = (parameterRule.type === 'int' || parameterRule.type === 'float') | |||
| && (isNullOrUndefined(parameterRule.min) || isNullOrUndefined(parameterRule.max)) | |||
| if (parameterRule.type === 'int' || parameterRule.type === 'float') { | |||
| let step = 100 | |||
| if (parameterRule.max) { | |||
| if (parameterRule.max < 10) | |||
| step = 0.1 | |||
| else if (parameterRule.max < 100) | |||
| step = 1 | |||
| else if (parameterRule.max < 1000) | |||
| step = 10 | |||
| else if (parameterRule.max < 10000) | |||
| step = 100 | |||
| } | |||
| return ( | |||
| <> | |||
| {numberInputWithSlide && <Slider | |||
| className='w-[120px]' | |||
| value={renderValue as number} | |||
| min={parameterRule.min} | |||
| max={parameterRule.max} | |||
| step={step} | |||
| onChange={handleSlideChange} | |||
| />} | |||
| <input | |||
| className='shrink-0 block ml-4 pl-3 w-16 h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' | |||
| type='number' | |||
| max={parameterRule.max} | |||
| min={parameterRule.min} | |||
| step={numberInputWithSlide ? step : +`0.${parameterRule.precision || 0}`} | |||
| value={renderValue as string} | |||
| onChange={handleNumberInputChange} | |||
| /> | |||
| </> | |||
| ) | |||
| } | |||
| if (parameterRule.type === 'boolean') { | |||
| return ( | |||
| <Radio.Group | |||
| className='w-[200px] flex items-center' | |||
| value={renderValue ? 1 : 0} | |||
| onChange={handleRadioChange} | |||
| > | |||
| <Radio value={1} className='!mr-1 w-[94px]'>True</Radio> | |||
| <Radio value={0} className='w-[94px]'>False</Radio> | |||
| </Radio.Group> | |||
| ) | |||
| } | |||
| if (parameterRule.type === 'string' && !parameterRule.options?.length) { | |||
| return ( | |||
| <input | |||
| className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' | |||
| value={renderValue as string} | |||
| onChange={handleStringInputChange} | |||
| /> | |||
| ) | |||
| } | |||
| if (parameterRule.type === 'string' && !!parameterRule?.options?.length) { | |||
| return ( | |||
| <SimpleSelect | |||
| className='!py-0' | |||
| wrapperClassName='!w-[200px] !h-8' | |||
| defaultValue={renderValue as string} | |||
| onSelect={handleSelect} | |||
| items={parameterRule.options.map(option => ({ value: option, name: option }))} | |||
| /> | |||
| ) | |||
| } | |||
| if (parameterRule.type === 'tag') { | |||
| return ( | |||
| <div className='w-[200px]'> | |||
| <TagInput | |||
| items={renderValue as string[]} | |||
| onChange={handleTagChange} | |||
| customizedConfirmKey='Tab' | |||
| /> | |||
| </div> | |||
| ) | |||
| } | |||
| return null | |||
| } | |||
| return ( | |||
| <div className={`flex items-center justify-between ${className}`}> | |||
| @@ -161,82 +225,7 @@ const ParameterItem: FC<ParameterItemProps> = ({ | |||
| ) | |||
| } | |||
| </div> | |||
| { | |||
| numberInputWithSlide && ( | |||
| <div className='flex items-center'> | |||
| <Slider | |||
| className='w-[120px]' | |||
| value={renderValue as number} | |||
| min={parameterRule.min} | |||
| max={parameterRule.max} | |||
| step={+`0.${parameterRule.precision || 0}`} | |||
| onChange={handleSlideChange} | |||
| /> | |||
| <input | |||
| className='shrink-0 block ml-4 pl-3 w-16 h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' | |||
| type='number' | |||
| max={parameterRule.max} | |||
| min={parameterRule.min} | |||
| step={+`0.${parameterRule.precision || 0}`} | |||
| value={renderValue as string} | |||
| onChange={handleNumberInputChange} | |||
| /> | |||
| </div> | |||
| ) | |||
| } | |||
| { | |||
| parameterRule.type === 'boolean' && ( | |||
| <Radio.Group | |||
| className='w-[200px] flex items-center' | |||
| value={renderValue ? 1 : 0} | |||
| onChange={handleRadioChange} | |||
| > | |||
| <Radio value={1} className='!mr-1 w-[94px]'>True</Radio> | |||
| <Radio value={0} className='w-[94px]'>False</Radio> | |||
| </Radio.Group> | |||
| ) | |||
| } | |||
| { | |||
| numberInput && ( | |||
| <input | |||
| type='number' | |||
| className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' | |||
| value={renderValue as string} | |||
| onChange={handleNumberInputChange} | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| parameterRule.type === 'string' && !parameterRule.options?.length && ( | |||
| <input | |||
| className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900' | |||
| value={renderValue as string} | |||
| onChange={handleStringInputChange} | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| parameterRule.type === 'string' && !!parameterRule?.options?.length && ( | |||
| <SimpleSelect | |||
| className='!py-0' | |||
| wrapperClassName='!w-[200px] !h-8' | |||
| defaultValue={renderValue as string} | |||
| onSelect={handleSelect} | |||
| items={parameterRule.options.map(option => ({ value: option, name: option }))} | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| parameterRule.type === 'tag' && ( | |||
| <div className='w-[200px]'> | |||
| <TagInput | |||
| items={renderValue as string[]} | |||
| onChange={handleTagChange} | |||
| customizedConfirmKey='Tab' | |||
| /> | |||
| </div> | |||
| ) | |||
| } | |||
| {renderInput()} | |||
| </div> | |||
| ) | |||
| } | |||