object-value-item.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import produce from 'immer'
  6. import { useContext } from 'use-context-selector'
  7. import { ToastContext } from '@/app/components/base/toast'
  8. import VariableTypeSelector from '@/app/components/workflow/panel/chat-variable-panel/components/variable-type-select'
  9. import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button'
  10. import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
  11. type Props = {
  12. index: number
  13. list: any[]
  14. onChange: (list: any[]) => void
  15. }
  16. const typeList = [
  17. ChatVarType.String,
  18. ChatVarType.Number,
  19. ]
  20. export const DEFAULT_OBJECT_VALUE = {
  21. key: '',
  22. type: ChatVarType.String,
  23. value: undefined,
  24. }
  25. const ObjectValueItem: FC<Props> = ({
  26. index,
  27. list,
  28. onChange,
  29. }) => {
  30. const { t } = useTranslation()
  31. const { notify } = useContext(ToastContext)
  32. const [isFocus, setIsFocus] = useState(false)
  33. const handleKeyChange = useCallback((index: number) => {
  34. return (e: React.ChangeEvent<HTMLInputElement>) => {
  35. const newList = produce(list, (draft: any[]) => {
  36. if (!/^[a-zA-Z0-9_]+$/.test(e.target.value))
  37. return notify({ type: 'error', message: 'key is can only contain letters, numbers and underscores' })
  38. draft[index].key = e.target.value
  39. })
  40. onChange(newList)
  41. }
  42. }, [list, notify, onChange])
  43. const handleTypeChange = useCallback((index: number) => {
  44. return (type: ChatVarType) => {
  45. const newList = produce(list, (draft) => {
  46. draft[index].type = type
  47. if (type === ChatVarType.Number)
  48. draft[index].value = isNaN(Number(draft[index].value)) ? undefined : Number(draft[index].value)
  49. else
  50. draft[index].value = draft[index].value ? String(draft[index].value) : undefined
  51. })
  52. onChange(newList)
  53. }
  54. }, [list, onChange])
  55. const handleValueChange = useCallback((index: number) => {
  56. return (e: React.ChangeEvent<HTMLInputElement>) => {
  57. const newList = produce(list, (draft: any[]) => {
  58. draft[index].value = draft[index].type === ChatVarType.String ? e.target.value : isNaN(Number(e.target.value)) ? undefined : Number(e.target.value)
  59. })
  60. onChange(newList)
  61. }
  62. }, [list, onChange])
  63. const handleItemRemove = useCallback((index: number) => {
  64. return () => {
  65. const newList = produce(list, (draft) => {
  66. draft.splice(index, 1)
  67. })
  68. onChange(newList)
  69. }
  70. }, [list, onChange])
  71. const handleItemAdd = useCallback(() => {
  72. const newList = produce(list, (draft: any[]) => {
  73. draft.push(DEFAULT_OBJECT_VALUE)
  74. })
  75. onChange(newList)
  76. }, [list, onChange])
  77. const handleFocusChange = useCallback(() => {
  78. setIsFocus(true)
  79. if (index === list.length - 1)
  80. handleItemAdd()
  81. }, [handleItemAdd, index, list.length])
  82. return (
  83. <div className='group flex border-t border-gray-200'>
  84. {/* Key */}
  85. <div className='w-[120px] border-r border-gray-200'>
  86. <input
  87. className='block px-2 w-full h-7 text-text-secondary system-xs-regular appearance-none outline-none caret-primary-600 hover:bg-state-base-hover focus:bg-components-input-bg-active placeholder:system-xs-regular placeholder:text-components-input-text-placeholder'
  88. placeholder={t('workflow.chatVariable.modal.objectKey') || ''}
  89. value={list[index].key}
  90. onChange={handleKeyChange(index)}
  91. />
  92. </div>
  93. {/* Type */}
  94. <div className='w-[96px] border-r border-gray-200'>
  95. <VariableTypeSelector
  96. inCell
  97. value={list[index].type}
  98. list={typeList}
  99. onSelect={handleTypeChange(index)}
  100. popupClassName='w-[120px]'
  101. />
  102. </div>
  103. {/* Value */}
  104. <div className='relative w-[230px]'>
  105. <input
  106. className='block px-2 w-full h-7 text-text-secondary system-xs-regular appearance-none outline-none caret-primary-600 hover:bg-state-base-hover focus:bg-components-input-bg-active placeholder:system-xs-regular placeholder:text-components-input-text-placeholder'
  107. placeholder={t('workflow.chatVariable.modal.objectValue') || ''}
  108. value={list[index].value}
  109. onChange={handleValueChange(index)}
  110. onFocus={() => handleFocusChange()}
  111. onBlur={() => setIsFocus(false)}
  112. type={list[index].type === ChatVarType.Number ? 'number' : 'text'}
  113. />
  114. {list.length > 1 && !isFocus && (
  115. <RemoveButton
  116. className='z-10 group-hover:block hidden absolute right-1 top-0.5'
  117. onClick={handleItemRemove(index)}
  118. />
  119. )}
  120. </div>
  121. </div>
  122. )
  123. }
  124. export default React.memo(ObjectValueItem)