online-users-widget.jsx 4.9 KB

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