operation.tsx 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useState } from 'react'
  4. import type { Placement } from '@floating-ui/react'
  5. import {
  6. RiArrowDownSLine,
  7. } from '@remixicon/react'
  8. import { useTranslation } from 'react-i18next'
  9. import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
  10. import cn from '@/utils/classnames'
  11. type Props = {
  12. title: string
  13. isPinned: boolean
  14. isShowRenameConversation?: boolean
  15. onRenameConversation?: () => void
  16. isShowDelete: boolean
  17. togglePin: () => void
  18. onDelete: () => void
  19. placement?: Placement
  20. }
  21. const Operation: FC<Props> = ({
  22. title,
  23. isPinned,
  24. togglePin,
  25. isShowRenameConversation,
  26. onRenameConversation,
  27. isShowDelete,
  28. onDelete,
  29. placement = 'bottom-start',
  30. }) => {
  31. const { t } = useTranslation()
  32. const [open, setOpen] = useState(false)
  33. return (
  34. <PortalToFollowElem
  35. open={open}
  36. onOpenChange={setOpen}
  37. placement={placement}
  38. offset={4}
  39. >
  40. <PortalToFollowElemTrigger
  41. onClick={() => setOpen(v => !v)}
  42. >
  43. <div className={cn('flex items-center p-1.5 pl-2 rounded-lg text-text-secondary cursor-pointer hover:bg-state-base-hover', open && 'bg-state-base-hover')}>
  44. <div className='system-md-semibold'>{title}</div>
  45. <RiArrowDownSLine className='w-4 h-4 ' />
  46. </div>
  47. </PortalToFollowElemTrigger>
  48. <PortalToFollowElemContent className="z-50">
  49. <div
  50. className={'min-w-[120px] p-1 bg-components-panel-bg-blur backdrop-blur-sm rounded-xl border-[0.5px] border-components-panel-border shadow-lg'}
  51. >
  52. <div className={cn('flex items-center space-x-1 px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover')} onClick={togglePin}>
  53. <span className='grow'>{isPinned ? t('explore.sidebar.action.unpin') : t('explore.sidebar.action.pin')}</span>
  54. </div>
  55. {isShowRenameConversation && (
  56. <div className={cn('flex items-center space-x-1 px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover')} onClick={onRenameConversation}>
  57. <span className='grow'>{t('explore.sidebar.action.rename')}</span>
  58. </div>
  59. )}
  60. {isShowDelete && (
  61. <div className={cn('group flex items-center space-x-1 px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-destructive-hover hover:text-text-destructive')} onClick={onDelete} >
  62. <span className='grow'>{t('explore.sidebar.action.delete')}</span>
  63. </div>
  64. )}
  65. </div>
  66. </PortalToFollowElemContent>
  67. </PortalToFollowElem>
  68. )
  69. }
  70. export default React.memo(Operation)