You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

menu-dropdown.tsx 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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 type { Placement } from '@floating-ui/react'
  6. import {
  7. RiEqualizer2Line,
  8. } from '@remixicon/react'
  9. import ActionButton from '@/app/components/base/action-button'
  10. import {
  11. PortalToFollowElem,
  12. PortalToFollowElemContent,
  13. PortalToFollowElemTrigger,
  14. } from '@/app/components/base/portal-to-follow-elem'
  15. import Divider from '@/app/components/base/divider'
  16. import ThemeSwitcher from '@/app/components/base/theme-switcher'
  17. import InfoModal from './info-modal'
  18. import type { SiteInfo } from '@/models/share'
  19. import cn from '@/utils/classnames'
  20. type Props = {
  21. data?: SiteInfo
  22. placement?: Placement
  23. }
  24. const MenuDropdown: FC<Props> = ({
  25. data,
  26. placement,
  27. }) => {
  28. const { t } = useTranslation()
  29. const [open, doSetOpen] = useState(false)
  30. const openRef = useRef(open)
  31. const setOpen = useCallback((v: boolean) => {
  32. doSetOpen(v)
  33. openRef.current = v
  34. }, [doSetOpen])
  35. const handleTrigger = useCallback(() => {
  36. setOpen(!openRef.current)
  37. }, [setOpen])
  38. const [show, setShow] = useState(false)
  39. return (
  40. <>
  41. <PortalToFollowElem
  42. open={open}
  43. onOpenChange={setOpen}
  44. placement={placement || 'bottom-end'}
  45. offset={{
  46. mainAxis: 4,
  47. crossAxis: -4,
  48. }}
  49. >
  50. <PortalToFollowElemTrigger onClick={handleTrigger}>
  51. <div>
  52. <ActionButton size='l' className={cn(open && 'bg-state-base-hover')}>
  53. <RiEqualizer2Line className='h-[18px] w-[18px]' />
  54. </ActionButton>
  55. </div>
  56. </PortalToFollowElemTrigger>
  57. <PortalToFollowElemContent className='z-50'>
  58. <div className='w-[224px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm'>
  59. <div className='p-1'>
  60. <div className={cn('system-md-regular flex cursor-pointer items-center rounded-lg py-1.5 pl-3 pr-2 text-text-secondary')}>
  61. <div className='grow'>{t('common.theme.theme')}</div>
  62. <ThemeSwitcher/>
  63. </div>
  64. </div>
  65. <Divider type='horizontal' className='my-0' />
  66. <div className='p-1'>
  67. {data?.privacy_policy && (
  68. <a href={data.privacy_policy} target='_blank' className='system-md-regular flex cursor-pointer items-center rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>
  69. <span className='grow'>{t('share.chat.privacyPolicyMiddle')}</span>
  70. </a>
  71. )}
  72. <div
  73. onClick={() => {
  74. handleTrigger()
  75. setShow(true)
  76. }}
  77. className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'
  78. >{t('common.userProfile.about')}</div>
  79. </div>
  80. </div>
  81. </PortalToFollowElemContent>
  82. </PortalToFollowElem>
  83. {show && (
  84. <InfoModal
  85. isShow={show}
  86. onClose={() => {
  87. setShow(false)
  88. }}
  89. data={data}
  90. />
  91. )}
  92. </>
  93. )
  94. }
  95. export default React.memo(MenuDropdown)