Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

operation-dropdown.tsx 2.8KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useRef, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import {
  6. RiDeleteBinLine,
  7. RiEditLine,
  8. RiMoreFill,
  9. } from '@remixicon/react'
  10. import ActionButton from '@/app/components/base/action-button'
  11. import {
  12. PortalToFollowElem,
  13. PortalToFollowElemContent,
  14. PortalToFollowElemTrigger,
  15. } from '@/app/components/base/portal-to-follow-elem'
  16. import cn from '@/utils/classnames'
  17. type Props = {
  18. inCard?: boolean
  19. onOpenChange?: (open: boolean) => void
  20. onEdit: () => void
  21. onRemove: () => void
  22. }
  23. const OperationDropdown: FC<Props> = ({
  24. inCard,
  25. onOpenChange,
  26. onEdit,
  27. onRemove,
  28. }) => {
  29. const { t } = useTranslation()
  30. const [open, doSetOpen] = useState(false)
  31. const openRef = useRef(open)
  32. const setOpen = useCallback((v: boolean) => {
  33. doSetOpen(v)
  34. openRef.current = v
  35. onOpenChange?.(v)
  36. }, [doSetOpen])
  37. const handleTrigger = useCallback(() => {
  38. setOpen(!openRef.current)
  39. }, [setOpen])
  40. return (
  41. <PortalToFollowElem
  42. open={open}
  43. onOpenChange={setOpen}
  44. placement='bottom-end'
  45. offset={{
  46. mainAxis: !inCard ? -12 : 0,
  47. crossAxis: !inCard ? 36 : 0,
  48. }}
  49. >
  50. <PortalToFollowElemTrigger onClick={handleTrigger}>
  51. <div>
  52. <ActionButton size={inCard ? 'l' : 'm'} className={cn(open && 'bg-state-base-hover')}>
  53. <RiMoreFill className={cn('h-4 w-4', inCard && 'h-5 w-5')} />
  54. </ActionButton>
  55. </div>
  56. </PortalToFollowElemTrigger>
  57. <PortalToFollowElemContent className='z-50'>
  58. <div className='w-[160px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-sm'>
  59. <div
  60. className='flex cursor-pointer items-center rounded-lg px-3 py-1.5 hover:bg-state-base-hover'
  61. onClick={() => {
  62. onEdit()
  63. handleTrigger()
  64. }}
  65. >
  66. <RiEditLine className='h-4 w-4 text-text-tertiary' />
  67. <div className='system-md-regular ml-2 text-text-secondary'>{t('tools.mcp.operation.edit')}</div>
  68. </div>
  69. <div
  70. className='group flex cursor-pointer items-center rounded-lg px-3 py-1.5 hover:bg-state-destructive-hover'
  71. onClick={() => {
  72. onRemove()
  73. handleTrigger()
  74. }}
  75. >
  76. <RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive-secondary' />
  77. <div className='system-md-regular ml-2 text-text-secondary group-hover:text-text-destructive'>{t('tools.mcp.operation.remove')}</div>
  78. </div>
  79. </div>
  80. </PortalToFollowElemContent>
  81. </PortalToFollowElem>
  82. )
  83. }
  84. export default React.memo(OperationDropdown)