dropdown-menu.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import React, { forwardRef } from 'react'
  2. import {
  3. Dropdown as BS5Dropdown,
  4. DropdownToggle as BS5DropdownToggle,
  5. DropdownMenu as BS5DropdownMenu,
  6. DropdownItem as BS5DropdownItem,
  7. DropdownDivider as BS5DropdownDivider,
  8. DropdownHeader as BS5DropdownHeader,
  9. } from 'react-bootstrap-5'
  10. import type {
  11. DropdownProps,
  12. DropdownItemProps,
  13. DropdownToggleProps,
  14. DropdownMenuProps,
  15. DropdownDividerProps,
  16. DropdownHeaderProps,
  17. } from '@/features/ui/components/types/dropdown-menu-props'
  18. import MaterialIcon from '@/shared/components/material-icon'
  19. import { fixedForwardRef } from '@/utils/react'
  20. export function Dropdown({ ...props }: DropdownProps) {
  21. return <BS5Dropdown {...props} />
  22. }
  23. function DropdownItem(
  24. {
  25. active,
  26. children,
  27. description,
  28. leadingIcon,
  29. trailingIcon,
  30. ...props
  31. }: DropdownItemProps,
  32. ref: React.ForwardedRef<typeof BS5DropdownItem>
  33. ) {
  34. let leadingIconComponent = null
  35. if (leadingIcon) {
  36. if (typeof leadingIcon === 'string') {
  37. leadingIconComponent = (
  38. <MaterialIcon
  39. className="dropdown-item-leading-icon"
  40. type={leadingIcon}
  41. />
  42. )
  43. } else {
  44. leadingIconComponent = (
  45. <span className="dropdown-item-leading-icon" aria-hidden="true">
  46. {leadingIcon}
  47. </span>
  48. )
  49. }
  50. }
  51. let trailingIconComponent = null
  52. if (trailingIcon) {
  53. if (typeof trailingIcon === 'string') {
  54. const trailingIconType = active ? 'check' : trailingIcon
  55. trailingIconComponent = (
  56. <MaterialIcon
  57. className="dropdown-item-trailing-icon"
  58. type={trailingIconType}
  59. />
  60. )
  61. } else {
  62. trailingIconComponent = (
  63. <span className="dropdown-item-trailing-icon" aria-hidden="true">
  64. {trailingIcon}
  65. </span>
  66. )
  67. }
  68. }
  69. return (
  70. <BS5DropdownItem
  71. active={active}
  72. className={description ? 'dropdown-item-description-container' : ''}
  73. role="menuitem"
  74. {...props}
  75. ref={ref}
  76. >
  77. {leadingIconComponent}
  78. {children}
  79. {trailingIconComponent}
  80. {description && (
  81. <span className="dropdown-item-description">{description}</span>
  82. )}
  83. </BS5DropdownItem>
  84. )
  85. }
  86. function EmptyLeadingIcon() {
  87. return <span className="dropdown-item-leading-icon-empty" />
  88. }
  89. const ForwardReferredDropdownItem = fixedForwardRef(DropdownItem, {
  90. EmptyLeadingIcon,
  91. })
  92. export { ForwardReferredDropdownItem as DropdownItem }
  93. export function DropdownToggle({ ...props }: DropdownToggleProps) {
  94. return <BS5DropdownToggle {...props} />
  95. }
  96. export const DropdownMenu = forwardRef<
  97. typeof BS5DropdownMenu,
  98. DropdownMenuProps
  99. >(({ as = 'ul', ...props }, ref) => {
  100. return <BS5DropdownMenu as={as} role="menu" {...props} ref={ref} />
  101. })
  102. DropdownMenu.displayName = 'DropdownMenu'
  103. export function DropdownDivider({ as = 'li', ...props }: DropdownDividerProps) {
  104. return <BS5DropdownDivider as={as} {...props} />
  105. }
  106. export function DropdownHeader({ as = 'li', ...props }: DropdownHeaderProps) {
  107. return <BS5DropdownHeader as={as} {...props} />
  108. }