浏览代码

Merge pull request #27209 from overleaf/dp-collaborator-colour

Adapt online user and chat user colors based on luminance

GitOrigin-RevId: 1b0c843147ee3dc585866bc491a7c7613cb00e70
David 1 年之前
父节点
当前提交
ae3f63d37f

+ 12 - 7
services/web/frontend/js/features/ide-redesign/components/chat/message.tsx

@@ -1,15 +1,14 @@
 import { MessageProps } from '@/features/chat/components/message'
 import { MessageProps } from '@/features/chat/components/message'
 import { User } from '../../../../../../types/user'
 import { User } from '../../../../../../types/user'
-import { getHueForUserId } from '@/shared/utils/colors'
+import {
+  getBackgroundColorForUserId,
+  hslStringToLuminance,
+} from '@/shared/utils/colors'
 import MessageContent from '@/features/chat/components/message-content'
 import MessageContent from '@/features/chat/components/message-content'
 import classNames from 'classnames'
 import classNames from 'classnames'
 import MaterialIcon from '@/shared/components/material-icon'
 import MaterialIcon from '@/shared/components/material-icon'
 import { t } from 'i18next'
 import { t } from 'i18next'
 
 
-function hue(user?: User) {
-  return user ? getHueForUserId(user.id) : 0
-}
-
 function getAvatarStyle(user?: User) {
 function getAvatarStyle(user?: User) {
   if (!user?.id) {
   if (!user?.id) {
     // Deleted user
     // Deleted user
@@ -20,9 +19,15 @@ function getAvatarStyle(user?: User) {
     }
     }
   }
   }
 
 
+  const backgroundColor = getBackgroundColorForUserId(user.id)
+
   return {
   return {
-    borderColor: `hsl(${hue(user)}, 85%, 40%)`,
-    backgroundColor: `hsl(${hue(user)}, 85%, 40%`,
+    borderColor: backgroundColor,
+    backgroundColor,
+    color:
+      hslStringToLuminance(backgroundColor) < 0.5
+        ? 'var(--content-primary-dark)'
+        : 'var(--content-primary)',
   }
   }
 }
 }
 
 

+ 13 - 2
services/web/frontend/js/features/ide-redesign/components/online-users/online-users-widget.tsx

@@ -7,7 +7,11 @@ import {
   DropdownToggle,
   DropdownToggle,
 } from '@/features/ui/components/bootstrap-5/dropdown-menu'
 } from '@/features/ui/components/bootstrap-5/dropdown-menu'
 import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
 import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
-import { getBackgroundColorForUserId } from '@/shared/utils/colors'
+import {
+  getBackgroundColorForUserId,
+  hslStringToLuminance,
+} from '@/shared/utils/colors'
+import classNames from 'classnames'
 import { useCallback, useMemo } from 'react'
 import { useCallback, useMemo } from 'react'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 
 
@@ -86,9 +90,16 @@ const OnlineUserWidget = ({
 
 
 const OnlineUserCircle = ({ user }: { user: OnlineUser }) => {
 const OnlineUserCircle = ({ user }: { user: OnlineUser }) => {
   const backgroundColor = getBackgroundColorForUserId(user.user_id)
   const backgroundColor = getBackgroundColorForUserId(user.user_id)
+  const luminance = hslStringToLuminance(backgroundColor)
   const [character] = [...user.name]
   const [character] = [...user.name]
   return (
   return (
-    <span className="online-user-circle" style={{ backgroundColor }}>
+    <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}
       {character}
     </span>
     </span>
   )
   )

+ 45 - 0
services/web/frontend/js/shared/utils/colors.ts

@@ -34,6 +34,51 @@ export function getBackgroundColorForUserId(userId?: string) {
   return `hsl(${getHueForUserId(userId)}, 70%, 50%)`
   return `hsl(${getHueForUserId(userId)}, 70%, 50%)`
 }
 }
 
 
+export function hslStringToLuminance(hslString: string): number {
+  // First extract the individual components from the HSL string
+  const hslSplit = hslString.slice(4).split(')')[0].split(',')
+
+  const h = Number(hslSplit[0])
+  const s = Number(hslSplit[1].slice(0, -1)) / 100
+  const l = Number(hslSplit[2].slice(0, -1)) / 100
+
+  // Then we need to convert HSL to RGB
+  const c = (1 - Math.abs(2 * l - 1)) * s
+  const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
+  const m = l - c / 2
+  let r = 0
+  let g = 0
+  let b = 0
+  if (h >= 0 && h < 60) {
+    r = c + m
+    g = x + m
+    b = m
+  } else if (h >= 60 && h < 120) {
+    r = x + m
+    g = c + m
+    b = m
+  } else if (h >= 120 && h < 180) {
+    r = m
+    g = c + m
+    b = x + m
+  } else if (h >= 180 && h < 240) {
+    r = m
+    g = x + m
+    b = c + m
+  } else if (h >= 240 && h < 300) {
+    r = x + m
+    g = m
+    b = c + m
+  } else if (h >= 300 && h < 360) {
+    r = c + m
+    g = m
+    b = x + m
+  }
+
+  // Finally we calculate the luminance
+  return 0.2126 * r + 0.7152 * g + 0.0722 * b
+}
+
 const cachedHues = new Map()
 const cachedHues = new Map()
 
 
 export function getHueForId(id: string) {
 export function getHueForId(id: string) {

+ 8 - 0
services/web/frontend/stylesheets/bootstrap-5/pages/editor/online-users.scss

@@ -124,4 +124,12 @@
     box-sizing: border-box;
     box-sizing: border-box;
     display: inline-block;
     display: inline-block;
   }
   }
+
+  .online-user-circle-light-font {
+    color: var(--content-primary-dark);
+  }
+
+  .online-user-circle-dark-font {
+    color: var(--content-primary);
+  }
 }
 }