index.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect, useRef, useState } from 'react'
  4. import cn from 'classnames'
  5. import { useTranslation } from 'react-i18next'
  6. import { useBoolean } from 'ahooks'
  7. import { Edit03, Pin02, Trash03 } from '../../base/icons/src/vender/line/general'
  8. import s from './style.module.css'
  9. import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
  10. export type IItemOperationProps = {
  11. className?: string
  12. isItemHovering?: boolean
  13. isPinned: boolean
  14. isShowRenameConversation?: boolean
  15. onRenameConversation?: () => void
  16. isShowDelete: boolean
  17. togglePin: () => void
  18. onDelete: () => void
  19. }
  20. const ItemOperation: FC<IItemOperationProps> = ({
  21. className,
  22. isItemHovering,
  23. isPinned,
  24. togglePin,
  25. isShowRenameConversation,
  26. onRenameConversation,
  27. isShowDelete,
  28. onDelete,
  29. }) => {
  30. const { t } = useTranslation()
  31. const [open, setOpen] = useState(false)
  32. const ref = useRef(null)
  33. const [isHovering, { setTrue: setIsHovering, setFalse: setNotHovering }] = useBoolean(false)
  34. useEffect(() => {
  35. if (!isItemHovering && !isHovering)
  36. setOpen(false)
  37. }, [isItemHovering, isHovering])
  38. return (
  39. <PortalToFollowElem
  40. open={open}
  41. onOpenChange={setOpen}
  42. placement='bottom-end'
  43. offset={4}
  44. >
  45. <PortalToFollowElemTrigger
  46. onClick={() => setOpen(v => !v)}
  47. >
  48. <div className={cn(className, s.btn, 'h-6 w-6 rounded-md border-none py-1', (isItemHovering || open) && `${s.open} !bg-gray-100 !shadow-none`)}></div>
  49. </PortalToFollowElemTrigger>
  50. <PortalToFollowElemContent
  51. className="z-50"
  52. >
  53. <div
  54. ref={ref}
  55. className={'min-w-[120px] p-1 bg-white rounded-lg border border--gray-200 shadow-lg'}
  56. onMouseEnter={setIsHovering}
  57. onMouseLeave={setNotHovering}
  58. onClick={(e) => {
  59. e.stopPropagation()
  60. }}
  61. >
  62. <div className={cn(s.actionItem, 'hover:bg-gray-50 group')} onClick={togglePin}>
  63. <Pin02 className='shrink-0 w-4 h-4 text-gray-500'/>
  64. <span className={s.actionName}>{isPinned ? t('explore.sidebar.action.unpin') : t('explore.sidebar.action.pin')}</span>
  65. </div>
  66. {isShowRenameConversation && (
  67. <div className={cn(s.actionItem, 'hover:bg-gray-50 group')} onClick={onRenameConversation}>
  68. <Edit03 className='shrink-0 w-4 h-4 text-gray-500'/>
  69. <span className={s.actionName}>{t('explore.sidebar.action.rename')}</span>
  70. </div>
  71. )}
  72. {isShowDelete && (
  73. <div className={cn(s.actionItem, s.deleteActionItem, 'hover:bg-gray-50 group')} onClick={onDelete} >
  74. <Trash03 className={cn(s.deleteActionItemChild, 'shrink-0 w-4 h-4 stroke-current text-gray-500 stroke-2')} />
  75. <span className={cn(s.actionName, s.deleteActionItemChild)}>{t('explore.sidebar.action.delete')}</span>
  76. </div>
  77. )}
  78. </div>
  79. </PortalToFollowElemContent>
  80. </PortalToFollowElem>
  81. )
  82. }
  83. export default React.memo(ItemOperation)