index.tsx 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import type { FC } from 'react'
  2. import { useRef, useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import { RiCloseCircleFill, RiSearchLine } from '@remixicon/react'
  5. import cn from '@/utils/classnames'
  6. type SearchInputProps = {
  7. placeholder?: string
  8. className?: string
  9. value: string
  10. onChange: (v: string) => void
  11. white?: boolean
  12. }
  13. const SearchInput: FC<SearchInputProps> = ({
  14. placeholder,
  15. className,
  16. value,
  17. onChange,
  18. white,
  19. }) => {
  20. const { t } = useTranslation()
  21. const [focus, setFocus] = useState<boolean>(false)
  22. const isComposing = useRef<boolean>(false)
  23. const [internalValue, setInternalValue] = useState<string>(value)
  24. return (
  25. <div className={cn(
  26. 'group flex h-8 items-center overflow-hidden rounded-lg border-none bg-components-input-bg-normal px-2 hover:bg-components-input-bg-hover',
  27. focus && '!bg-components-input-bg-active',
  28. white && '!border-gray-300 !bg-white shadow-xs hover:!border-gray-300 hover:!bg-white',
  29. className,
  30. )}>
  31. <div className="pointer-events-none mr-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
  32. <RiSearchLine className="h-4 w-4 text-components-input-text-placeholder" aria-hidden="true" />
  33. </div>
  34. <input
  35. type="text"
  36. name="query"
  37. className={cn(
  38. 'system-sm-regular caret-#295EFF block h-[18px] grow appearance-none border-0 bg-transparent text-components-input-text-filled outline-none placeholder:text-components-input-text-placeholder',
  39. white && '!bg-white placeholder:!text-gray-400 hover:!bg-white group-hover:!bg-white',
  40. )}
  41. placeholder={placeholder || t('common.operation.search')!}
  42. value={internalValue}
  43. onChange={(e) => {
  44. setInternalValue(e.target.value)
  45. if (!isComposing.current)
  46. onChange(e.target.value)
  47. }}
  48. onCompositionStart={() => {
  49. isComposing.current = true
  50. }}
  51. onCompositionEnd={(e) => {
  52. isComposing.current = false
  53. onChange(e.data)
  54. }}
  55. onFocus={() => setFocus(true)}
  56. onBlur={() => setFocus(false)}
  57. autoComplete="off"
  58. />
  59. {value && (
  60. <div
  61. className='group/clear flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center'
  62. onClick={() => {
  63. onChange('')
  64. setInternalValue('')
  65. }}
  66. >
  67. <RiCloseCircleFill className='h-4 w-4 text-text-quaternary group-hover/clear:text-text-tertiary' />
  68. </div>
  69. )}
  70. </div>
  71. )
  72. }
  73. export default SearchInput