online-users-widget.jsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import React from 'react'
  2. import PropTypes from 'prop-types'
  3. import { useTranslation } from 'react-i18next'
  4. import {
  5. Dropdown,
  6. DropdownHeader,
  7. DropdownItem,
  8. DropdownMenu,
  9. DropdownToggle,
  10. } from '@/features/ui/components/bootstrap-5/dropdown-menu'
  11. import { getBackgroundColorForUserId } from '@/shared/utils/colors'
  12. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  13. import MaterialIcon from '@/shared/components/material-icon'
  14. function OnlineUsersWidget({ onlineUsers, goToUser }) {
  15. const { t } = useTranslation()
  16. const shouldDisplayDropdown = onlineUsers.length >= 4
  17. if (shouldDisplayDropdown) {
  18. return (
  19. <Dropdown id="online-users" className="online-users" align="end">
  20. <DropdownToggle
  21. as={DropDownToggleButton}
  22. onlineUserCount={onlineUsers.length}
  23. />
  24. <DropdownMenu>
  25. <DropdownHeader aria-hidden="true">
  26. {t('connected_users')}
  27. </DropdownHeader>
  28. {onlineUsers.map((user, index) => (
  29. <li role="none" key={`${user.user_id}_${index}`}>
  30. <DropdownItem
  31. as="button"
  32. tabIndex={-1}
  33. onClick={() => goToUser(user)}
  34. >
  35. <UserIcon user={user} showName />
  36. </DropdownItem>
  37. </li>
  38. ))}
  39. </DropdownMenu>
  40. </Dropdown>
  41. )
  42. } else {
  43. return (
  44. <div className="online-users">
  45. {onlineUsers.map((user, index) => (
  46. <OLTooltip
  47. key={`${user.user_id}_${index}`}
  48. id="online-user"
  49. description={user.name}
  50. overlayProps={{ placement: 'bottom', trigger: ['hover', 'focus'] }}
  51. >
  52. <span>
  53. {/* OverlayTrigger won't fire unless UserIcon is wrapped in a span */}
  54. <UserIcon user={user} onClick={goToUser} />
  55. </span>
  56. </OLTooltip>
  57. ))}
  58. </div>
  59. )
  60. }
  61. }
  62. OnlineUsersWidget.propTypes = {
  63. onlineUsers: PropTypes.arrayOf(
  64. PropTypes.shape({
  65. user_id: PropTypes.string.isRequired,
  66. name: PropTypes.string.isRequired,
  67. })
  68. ).isRequired,
  69. goToUser: PropTypes.func.isRequired,
  70. }
  71. function UserIcon({ user, showName, onClick }) {
  72. const backgroundColor = getBackgroundColorForUserId(user.user_id)
  73. function handleOnClick() {
  74. onClick?.(user)
  75. }
  76. const [character] = [...user.name]
  77. return (
  78. // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
  79. <span onClick={handleOnClick}>
  80. <span className="online-user" style={{ backgroundColor }}>
  81. {character}
  82. </span>
  83. {showName && user.name}
  84. </span>
  85. )
  86. }
  87. UserIcon.propTypes = {
  88. user: PropTypes.shape({
  89. user_id: PropTypes.string.isRequired,
  90. name: PropTypes.string.isRequired,
  91. }),
  92. showName: PropTypes.bool,
  93. onClick: PropTypes.func,
  94. }
  95. const DropDownToggleButton = React.forwardRef((props, ref) => {
  96. const { t } = useTranslation()
  97. return (
  98. <OLTooltip
  99. id="connected-users"
  100. description={t('connected_users')}
  101. overlayProps={{ placement: 'left' }}
  102. >
  103. <button
  104. type="button"
  105. className="online-user online-user-multi"
  106. onClick={props.onClick} // required by Bootstrap Dropdown to trigger an opening
  107. ref={ref}
  108. >
  109. <strong>{props.onlineUserCount}</strong>&nbsp;
  110. <MaterialIcon type="groups" />
  111. </button>
  112. </OLTooltip>
  113. )
  114. })
  115. DropDownToggleButton.displayName = 'DropDownToggleButton'
  116. DropDownToggleButton.propTypes = {
  117. onlineUserCount: PropTypes.number.isRequired,
  118. onClick: PropTypes.func,
  119. }
  120. export default OnlineUsersWidget