parameter-item.tsx 7.7 KB

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