parameter-item.tsx 7.6 KB

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