| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- import { OnlineUser } from '@/features/ide-react/context/online-users-context'
- import {
- Dropdown,
- DropdownHeader,
- DropdownItem,
- DropdownMenu,
- DropdownToggle,
- } from '@/shared/components/dropdown/dropdown-menu'
- import OLTooltip from '@/shared/components/ol/ol-tooltip'
- import {
- getBackgroundColorForUserId,
- hslStringToLuminance,
- } from '@/shared/utils/colors'
- import classNames from 'classnames'
- import { useCallback, useMemo } from 'react'
- import { useTranslation } from 'react-i18next'
- import { Doc } from '@ol-types/doc'
- import firstCharacter from '@/shared/utils/first-character'
- // Should be kept in sync with $max-user-circles-displayed CSS constant
- const MAX_USER_CIRCLES_DISPLAYED = 5
- // We don't want a +1 circle since we could just show the user instead
- const MAX_USERS_WITH_OVERFLOW_VISIBLE = MAX_USER_CIRCLES_DISPLAYED - 1
- export const OnlineUsersWidget = ({
- onlineUsers,
- goToUser,
- }: {
- onlineUsers: OnlineUser[]
- goToUser: (user: OnlineUser) => Promise<Doc | undefined>
- }) => {
- const hasOverflow = onlineUsers.length > MAX_USER_CIRCLES_DISPLAYED
- const usersBeforeOverflow = useMemo(
- () =>
- hasOverflow
- ? onlineUsers.slice(0, MAX_USERS_WITH_OVERFLOW_VISIBLE)
- : onlineUsers,
- [onlineUsers, hasOverflow]
- )
- const usersInOverflow = useMemo(
- () =>
- hasOverflow ? onlineUsers.slice(MAX_USERS_WITH_OVERFLOW_VISIBLE) : [],
- [onlineUsers, hasOverflow]
- )
- return (
- <div className="online-users-row">
- {usersBeforeOverflow.map((user, index) => (
- <OnlineUserWidget
- key={`${user.user_id}_${index}`}
- user={user}
- goToUser={goToUser}
- id={`online-user-${user.user_id}_${index}`}
- />
- ))}
- {hasOverflow && (
- <OnlineUserOverflow goToUser={goToUser} users={usersInOverflow} />
- )}
- </div>
- )
- }
- const OnlineUserWidget = ({
- user,
- goToUser,
- id,
- }: {
- user: OnlineUser
- goToUser: (user: OnlineUser) => void
- id: string
- }) => {
- const onClick = useCallback(() => {
- goToUser(user)
- }, [goToUser, user])
- return (
- <OLTooltip
- id={id}
- description={user.name}
- overlayProps={{
- placement: 'bottom',
- trigger: ['hover', 'focus'],
- delay: 0,
- }}
- >
- <button className="online-users-row-button" onClick={onClick}>
- <OnlineUserCircle user={user} />
- </button>
- </OLTooltip>
- )
- }
- const OnlineUserCircle = ({ user }: { user: OnlineUser }) => {
- const backgroundColor = getBackgroundColorForUserId(user.user_id)
- const luminance = hslStringToLuminance(backgroundColor)
- const character = firstCharacter(user.name)
- return (
- <span
- className={classNames('online-user-circle', {
- 'online-user-circle-light-font': luminance < 0.5,
- 'online-user-circle-dark-font': luminance >= 0.5,
- })}
- style={{ backgroundColor }}
- >
- {character}
- </span>
- )
- }
- const OnlineUserOverflow = ({
- goToUser,
- users,
- }: {
- goToUser: (user: OnlineUser) => void
- users: OnlineUser[]
- }) => {
- const { t } = useTranslation()
- return (
- <Dropdown align="end">
- <DropdownToggle className="online-users-row-button online-user-overflow-toggle">
- <OLTooltip
- id="connected-users"
- description={t('n_more_collaborators', { count: users.length })}
- overlayProps={{ placement: 'bottom' }}
- >
- <span className="online-user-circle">+{users.length}</span>
- </OLTooltip>
- </DropdownToggle>
- <DropdownMenu className="online-user-overflow-dropdown">
- <DropdownHeader aria-hidden="true">
- {t('connected_users')}
- </DropdownHeader>
- {users.map((user, index) => (
- <li role="none" key={`${user.user_id}_${index}`}>
- <DropdownItem
- as="button"
- tabIndex={-1}
- onClick={() => goToUser(user)}
- >
- <OnlineUserCircle user={user} /> {user.name}
- </DropdownItem>
- </li>
- ))}
- </DropdownMenu>
- </Dropdown>
- )
- }
|