index.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import type { FC } from 'react'
  2. import { useState } from 'react'
  3. import {
  4. RiMoreFill,
  5. } from '@remixicon/react'
  6. import {
  7. PortalToFollowElem,
  8. PortalToFollowElemContent,
  9. PortalToFollowElemTrigger,
  10. } from '@/app/components/base/portal-to-follow-elem'
  11. export type Item = {
  12. value: string | number
  13. text: string | JSX.Element
  14. }
  15. type DropdownProps = {
  16. items: Item[]
  17. secondItems?: Item[]
  18. onSelect: (item: Item) => void
  19. renderTrigger?: (open: boolean) => React.ReactNode
  20. popupClassName?: string
  21. }
  22. const Dropdown: FC<DropdownProps> = ({
  23. items,
  24. onSelect,
  25. secondItems,
  26. renderTrigger,
  27. popupClassName,
  28. }) => {
  29. const [open, setOpen] = useState(false)
  30. const handleSelect = (item: Item) => {
  31. setOpen(false)
  32. onSelect(item)
  33. }
  34. return (
  35. <PortalToFollowElem
  36. open={open}
  37. onOpenChange={setOpen}
  38. placement='bottom-end'
  39. >
  40. <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
  41. {
  42. renderTrigger
  43. ? renderTrigger(open)
  44. : (
  45. <div
  46. className={`
  47. flex items-center justify-center w-6 h-6 cursor-pointer rounded-md
  48. ${open && 'bg-black/5'}
  49. `}
  50. >
  51. <RiMoreFill className='w-4 h-4 text-gray-500' />
  52. </div>
  53. )
  54. }
  55. </PortalToFollowElemTrigger>
  56. <PortalToFollowElemContent className={popupClassName}>
  57. <div className='rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg text-sm text-gray-700'>
  58. {
  59. !!items.length && (
  60. <div className='p-1'>
  61. {
  62. items.map(item => (
  63. <div
  64. key={item.value}
  65. className='flex items-center px-3 h-8 rounded-lg cursor-pointer hover:bg-gray-100'
  66. onClick={() => handleSelect(item)}
  67. >
  68. {item.text}
  69. </div>
  70. ))
  71. }
  72. </div>
  73. )
  74. }
  75. {
  76. (!!items.length && !!secondItems?.length) && (
  77. <div className='h-[1px] bg-gray-100' />
  78. )
  79. }
  80. {
  81. !!secondItems?.length && (
  82. <div className='p-1'>
  83. {
  84. secondItems.map(item => (
  85. <div
  86. key={item.value}
  87. className='flex items-center px-3 h-8 rounded-lg cursor-pointer hover:bg-gray-100'
  88. onClick={() => handleSelect(item)}
  89. >
  90. {item.text}
  91. </div>
  92. ))
  93. }
  94. </div>
  95. )
  96. }
  97. </div>
  98. </PortalToFollowElemContent>
  99. </PortalToFollowElem>
  100. )
  101. }
  102. export default Dropdown