index.tsx 3.2 KB

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