plugin-version-picker.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import {
  6. PortalToFollowElem,
  7. PortalToFollowElemContent,
  8. PortalToFollowElemTrigger,
  9. } from '@/app/components/base/portal-to-follow-elem'
  10. import Badge from '@/app/components/base/badge'
  11. import type {
  12. OffsetOptions,
  13. Placement,
  14. } from '@floating-ui/react'
  15. import { useVersionListOfPlugin } from '@/service/use-plugins'
  16. import useTimestamp from '@/hooks/use-timestamp'
  17. import cn from '@/utils/classnames'
  18. type Props = {
  19. disabled?: boolean
  20. isShow: boolean
  21. onShowChange: (isShow: boolean) => void
  22. pluginID: string
  23. currentVersion: string
  24. trigger: React.ReactNode
  25. placement?: Placement
  26. offset?: OffsetOptions
  27. onSelect: ({
  28. version,
  29. unique_identifier,
  30. }: {
  31. version: string
  32. unique_identifier: string
  33. }) => void
  34. }
  35. const PluginVersionPicker: FC<Props> = ({
  36. disabled = false,
  37. isShow,
  38. onShowChange,
  39. pluginID,
  40. currentVersion,
  41. trigger,
  42. placement = 'bottom-start',
  43. offset = {
  44. mainAxis: 4,
  45. crossAxis: -16,
  46. },
  47. onSelect,
  48. }) => {
  49. const { t } = useTranslation()
  50. const format = t('appLog.dateTimeFormat').split(' ')[0]
  51. const { formatDate } = useTimestamp()
  52. const handleTriggerClick = () => {
  53. if (disabled) return
  54. onShowChange(true)
  55. }
  56. const { data: res } = useVersionListOfPlugin(pluginID)
  57. const handleSelect = useCallback(({ version, unique_identifier }: {
  58. version: string
  59. unique_identifier: string
  60. }) => {
  61. if (currentVersion === version)
  62. return
  63. onSelect({ version, unique_identifier })
  64. onShowChange(false)
  65. }, [currentVersion, onSelect, onShowChange])
  66. return (
  67. <PortalToFollowElem
  68. placement={placement}
  69. offset={offset}
  70. open={isShow}
  71. onOpenChange={onShowChange}
  72. >
  73. <PortalToFollowElemTrigger
  74. className={cn('inline-flex items-center cursor-pointer', disabled && 'cursor-default')}
  75. onClick={handleTriggerClick}
  76. >
  77. {trigger}
  78. </PortalToFollowElemTrigger>
  79. <PortalToFollowElemContent className='z-[1000]'>
  80. <div className="relative w-[209px] p-1 rounded-xl bg-components-panel-bg-blur border-[0.5px] border-components-panel-border shadow-lg">
  81. <div className='px-3 pt-1 pb-0.5 text-text-tertiary system-xs-medium-uppercase'>
  82. {t('plugin.detailPanel.switchVersion')}
  83. </div>
  84. <div className='relative'>
  85. {res?.data.versions.map(version => (
  86. <div
  87. key={version.unique_identifier}
  88. className={cn(
  89. 'h-7 px-3 py-1 flex items-center gap-1 rounded-lg hover:bg-state-base-hover cursor-pointer',
  90. currentVersion === version.version && 'opacity-30 cursor-default hover:bg-transparent',
  91. )}
  92. onClick={() => handleSelect({
  93. version: version.version,
  94. unique_identifier: version.unique_identifier,
  95. })}
  96. >
  97. <div className='grow flex items-center'>
  98. <div className='text-text-secondary system-sm-medium'>{version.version}</div>
  99. {currentVersion === version.version && <Badge className='ml-1' text='CURRENT'/>}
  100. </div>
  101. <div className='shrink-0 text-text-tertiary system-xs-regular'>{formatDate(version.created_at, format)}</div>
  102. </div>
  103. ))}
  104. </div>
  105. </div>
  106. </PortalToFollowElemContent>
  107. </PortalToFollowElem>
  108. )
  109. }
  110. export default React.memo(PluginVersionPicker)