parameter-item.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import type { FC } from 'react'
  2. import { useState } from 'react'
  3. import type { ModelParameterRule } from '../declarations'
  4. import { useLanguage } from '../hooks'
  5. import { isNullOrUndefined } from '../utils'
  6. import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
  7. import Switch from '@/app/components/base/switch'
  8. import Tooltip from '@/app/components/base/tooltip'
  9. import Slider from '@/app/components/base/slider'
  10. import Radio from '@/app/components/base/radio'
  11. import { SimpleSelect } from '@/app/components/base/select'
  12. import TagInput from '@/app/components/base/tag-input'
  13. export type ParameterValue = number | string | string[] | boolean | undefined
  14. type ParameterItemProps = {
  15. parameterRule: ModelParameterRule
  16. value?: ParameterValue
  17. onChange?: (value: ParameterValue) => void
  18. className?: string
  19. onSwitch?: (checked: boolean, assignValue: ParameterValue) => void
  20. }
  21. const ParameterItem: FC<ParameterItemProps> = ({
  22. parameterRule,
  23. value,
  24. onChange,
  25. className,
  26. onSwitch,
  27. }) => {
  28. const language = useLanguage()
  29. const [localValue, setLocalValue] = useState(value)
  30. const getDefaultValue = () => {
  31. let defaultValue: ParameterValue
  32. if (parameterRule.type === 'int' || parameterRule.type === 'float')
  33. defaultValue = isNullOrUndefined(parameterRule.default) ? (parameterRule.min || 0) : parameterRule.default
  34. else if (parameterRule.type === 'string')
  35. defaultValue = parameterRule.options?.length ? (parameterRule.default || '') : (parameterRule.default || '')
  36. else if (parameterRule.type === 'boolean')
  37. defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : false
  38. else if (parameterRule.type === 'tag')
  39. defaultValue = !isNullOrUndefined(parameterRule.default) ? parameterRule.default : []
  40. return defaultValue
  41. }
  42. const renderValue = value ?? localValue ?? getDefaultValue()
  43. const handleInputChange = (newValue: ParameterValue) => {
  44. setLocalValue(newValue)
  45. if (onChange && (parameterRule.name === 'stop' || !isNullOrUndefined(value)))
  46. onChange(newValue)
  47. }
  48. const handleNumberInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  49. let num = +e.target.value
  50. if (!isNullOrUndefined(parameterRule.max) && num > parameterRule.max!)
  51. num = parameterRule.max as number
  52. if (!isNullOrUndefined(parameterRule.min) && num < parameterRule.min!)
  53. num = parameterRule.min as number
  54. handleInputChange(num)
  55. }
  56. const handleSlideChange = (num: number) => {
  57. handleInputChange(num)
  58. }
  59. const handleRadioChange = (v: number) => {
  60. handleInputChange(v === 1)
  61. }
  62. const handleStringInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  63. handleInputChange(e.target.value)
  64. }
  65. const handleSelect = (option: { value: string | number; name: string }) => {
  66. handleInputChange(option.value)
  67. }
  68. const handleTagChange = (newSequences: string[]) => {
  69. handleInputChange(newSequences)
  70. }
  71. const handleSwitch = (checked: boolean) => {
  72. if (onSwitch) {
  73. const assignValue: ParameterValue = localValue || getDefaultValue()
  74. onSwitch(checked, assignValue)
  75. }
  76. }
  77. const renderInput = () => {
  78. const numberInputWithSlide = (parameterRule.type === 'int' || parameterRule.type === 'float')
  79. && !isNullOrUndefined(parameterRule.min)
  80. && !isNullOrUndefined(parameterRule.max)
  81. if (parameterRule.type === 'int' || parameterRule.type === 'float') {
  82. let step = 100
  83. if (parameterRule.max) {
  84. if (parameterRule.max < 10)
  85. step = 0.1
  86. else if (parameterRule.max < 100)
  87. step = 1
  88. else if (parameterRule.max < 1000)
  89. step = 10
  90. else if (parameterRule.max < 10000)
  91. step = 100
  92. }
  93. return (
  94. <>
  95. {numberInputWithSlide && <Slider
  96. className='w-[120px]'
  97. value={renderValue as number}
  98. min={parameterRule.min}
  99. max={parameterRule.max}
  100. step={step}
  101. onChange={handleSlideChange}
  102. />}
  103. <input
  104. 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'
  105. type='number'
  106. max={parameterRule.max}
  107. min={parameterRule.min}
  108. step={numberInputWithSlide ? step : +`0.${parameterRule.precision || 0}`}
  109. value={renderValue as string}
  110. onChange={handleNumberInputChange}
  111. />
  112. </>
  113. )
  114. }
  115. if (parameterRule.type === 'boolean') {
  116. return (
  117. <Radio.Group
  118. className='w-[200px] flex items-center'
  119. value={renderValue ? 1 : 0}
  120. onChange={handleRadioChange}
  121. >
  122. <Radio value={1} className='!mr-1 w-[94px]'>True</Radio>
  123. <Radio value={0} className='w-[94px]'>False</Radio>
  124. </Radio.Group>
  125. )
  126. }
  127. if (parameterRule.type === 'string' && !parameterRule.options?.length) {
  128. return (
  129. <input
  130. className='flex items-center px-3 w-[200px] h-8 appearance-none outline-none rounded-lg bg-gray-100 text-[13px] text-gra-900'
  131. value={renderValue as string}
  132. onChange={handleStringInputChange}
  133. />
  134. )
  135. }
  136. if (parameterRule.type === 'string' && !!parameterRule?.options?.length) {
  137. return (
  138. <SimpleSelect
  139. className='!py-0'
  140. wrapperClassName='!w-[200px] !h-8'
  141. defaultValue={renderValue as string}
  142. onSelect={handleSelect}
  143. items={parameterRule.options.map(option => ({ value: option, name: option }))}
  144. />
  145. )
  146. }
  147. if (parameterRule.type === 'tag') {
  148. return (
  149. <div className='w-[200px]'>
  150. <TagInput
  151. items={renderValue as string[]}
  152. onChange={handleTagChange}
  153. customizedConfirmKey='Tab'
  154. />
  155. </div>
  156. )
  157. }
  158. return null
  159. }
  160. return (
  161. <div className={`flex items-center justify-between ${className}`}>
  162. <div>
  163. <div className='shrink-0 flex items-center w-[200px]'>
  164. <div
  165. className='mr-0.5 text-[13px] font-medium text-gray-700 truncate'
  166. title={parameterRule.label[language]}
  167. >
  168. {parameterRule.label[language]}
  169. </div>
  170. {
  171. parameterRule.help && (
  172. <Tooltip
  173. selector={`model-parameter-rule-${parameterRule.name}`}
  174. htmlContent={(
  175. <div className='w-[200px] whitespace-pre-wrap'>{parameterRule.help[language]}</div>
  176. )}
  177. >
  178. <HelpCircle className='mr-1.5 w-3.5 h-3.5 text-gray-400' />
  179. </Tooltip>
  180. )
  181. }
  182. {
  183. !parameterRule.required && parameterRule.name !== 'stop' && (
  184. <Switch
  185. defaultValue={!isNullOrUndefined(value)}
  186. onChange={handleSwitch}
  187. size='md'
  188. />
  189. )
  190. }
  191. </div>
  192. {
  193. parameterRule.type === 'tag' && (
  194. <div className='w-[200px] text-gray-400 text-xs font-normal'>
  195. {parameterRule?.tagPlaceholder?.[language]}
  196. </div>
  197. )
  198. }
  199. </div>
  200. {renderInput()}
  201. </div>
  202. )
  203. }
  204. export default ParameterItem