dropdown-menu.tsx 3.1 KB

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