import { typingIndicatorContainerStyle , typingIndicatorStringStyle } from './styles/TypingIndicator.styles' ; import React from 'react' ; import { BaseCustomStyles , CommunicationParticipant } from '../types' ; import { IStyle , mergeStyles , Stack , Text } from '@fluentui/react' ; import { useLocale } from '../localization/LocalizationProvider' ; import { useIdentifiers } from '../identifiers' ; import { _IObjectMap } from '@internal/acs-ui-common' ; /**
 * Fluent styles for  TypingIndicator .
 *
 *  public export interface TypingIndicatorStylesProps extends BaseCustomStyles { /** Styles for each typing user's displayName. */ typingUserDisplayName ? : IStyle ; /** Styles for the typing string. */ typingString ? : IStyle ; } /**
 * Strings of  TypingIndicator that can be overridden.
 *
 *  public export interface TypingIndicatorStrings { example singleUser : string ; example multipleUsers : string ; example multipleUsersAbbreviateOne : string ; example multipleUsersAbbreviateMany : string ; example delimiter : string ; } /**
 * Props for  TypingIndicator .
 *
 *  public export interface TypingIndicatorProps { /** List of the typing users. */ typingUsers : CommunicationParticipant [ ] ; /** Callback to render typing users */ onRenderUser ? : ( users : CommunicationParticipant ) => JSX . Element ; Example styles ? : TypingIndicatorStylesProps ; /**
     * Optional strings to override in component
     */ strings ? : Partial < TypingIndicatorStrings > ; } const MAXIMUM_LENGTH_OF_TYPING_USERS = 35 ; param typingUsers param delimiter param onRenderUser param userDisplayNameStyles returns const getUsersElement = ( typingUsers , delimiter , onRenderUser ? , userDisplayNameStyles ? ) : JSX . Element => { const userElements = [ ] ; typingUsers . forEach ( ( user , index ) => { userElements . push ( onRenderUser ? ( onRenderUser ( user ) ) : ( < Text className = { mergeStyles ( userDisplayNameStyles ) } key = { `user-${ index }` } > { user . displayName } < /Text>)); userElements . push ( < Text key = { `comma-${ index }` } > { `${ delimiter }` } < /Text>); } ) ; userElements . pop ( ) ; return < > { userElements } < />; } ; param typingUsers param delimiter returns const getNamesString = ( typingUsers , delimiter ) : string => { const userNames = [ ] ; typingUsers . forEach ( ( user ) => { if ( user . displayName ) { userNames . push ( user . displayName ) ; } } ) ; return userNames . join ( delimiter ) ; } ; param strings param usersElement param numTypingUsers param numUserNotMentioned returns const getSpanElements = ( strings , usersElement , numTypingUsers , numTypingUsersAbbreviated ) : JSX . Element [ ] => { let variables = { } ; let typingString = '' ; if ( numTypingUsers === 1 ) { typingString = strings . singleUser ; variables = { user : usersElement } ; } else if ( numTypingUsers > 1 && numTypingUsersAbbreviated === 0 ) { typingString = strings . multipleUsers ; variables = { users : usersElement } ; } else if ( numTypingUsers > 1 && numTypingUsersAbbreviated === 1 ) { typingString = strings . multipleUsersAbbreviateOne ; variables = { users : usersElement } ; } else if ( numTypingUsers > 1 && numTypingUsersAbbreviated > 1 ) { typingString = strings . multipleUsersAbbreviateMany ; variables = { users : usersElement , numOthers : < > { numTypingUsersAbbreviated } < /> } ; } return formatInlineElements ( typingString , variables ) ; } ; param strings param namesString param numTypingUsers param numUserNotMentioned returns const getIndicatorString = ( strings , namesString , numTypingUsers , numTypingUsersAbbreviated ) : string | undefined => { if ( numTypingUsers === 1 ) { return strings . singleUser . replace ( '{user}' , namesString ) ; } if ( numTypingUsers > 1 && numTypingUsersAbbreviated === 0 ) { return strings . multipleUsers . replace ( '{users}' , namesString ) ; } if ( numTypingUsers > 1 && numTypingUsersAbbreviated === 1 ) { return strings . multipleUsersAbbreviateOne . replace ( '{users}' , namesString ) ; } if ( numTypingUsers > 1 && numTypingUsersAbbreviated > 1 ) { return strings . multipleUsersAbbreviateMany . replace ( '{users}' , namesString ) . replace ( '{numOthers}' , `${ numTypingUsersAbbreviated }` ) ; } return undefined ; } ; const IndicatorComponent = ( typingUsers , strings , onRenderUser ? , styles ? ) : JSX . Element => { const typingUsersMentioned = [ ] ; let totalCharacterCount = 0 ; const ids = useIdentifiers ( ) ; for ( const typingUser of typingUsers ) { if ( ! typingUser . displayName ) { continue ; } let additionalCharCount = typingUser . displayName . length ; if ( typingUsersMentioned . length > 0 ) { additionalCharCount += strings . delimiter . length ; } if ( totalCharacterCount + additionalCharCount <= MAXIMUM_LENGTH_OF_TYPING_USERS || typingUsersMentioned . length === 0 ) { typingUsersMentioned . push ( typingUser ) ; totalCharacterCount += additionalCharCount ; } else { break ; } } const usersElement = getUsersElement ( typingUsersMentioned , strings . delimiter , onRenderUser , styles ?. typingUserDisplayName ) ; const numUserNotMentioned = typingUsers . length - typingUsersMentioned . length ; const spanElements = getSpanElements ( strings , usersElement , typingUsers . length , numUserNotMentioned ) ; const labelString = getIndicatorString ( strings , getNamesString ( typingUsersMentioned , strings . delimiter ) , typingUsers . length , numUserNotMentioned ) ; return ( < div data - ui - id = { ids . typingIndicator } className = { mergeStyles ( typingIndicatorStringStyle , styles ?. typingString ) } key = "typingStringKey" role = "status" aria - label = { labelString } > { spanElements } < /div>); } ; public export const TypingIndicator = ( props ) : JSX . Element => { const { typingUsers , onRenderUser , styles } = props ; const { strings } = useLocale ( ) ; const typingUsersToRender = typingUsers . filter ( ( typingUser ) => typingUser . displayName !== undefined ) ; const indicatorComponent = IndicatorComponent ( typingUsersToRender , { ... strings . typingIndicator , ... props . strings } , onRenderUser , styles ) ; return < Stack className = { mergeStyles ( typingIndicatorContainerStyle , styles ?. root ) } > { indicatorComponent } < /Stack>; } ; param str param vars returns const formatInlineElements = ( str , vars ) : JSX . Element [ ] => { if ( ! str ) { return [ ] ; } if ( ! vars ) { return [ ] ; } const elements = [ ] ; const placeholdersRegex = /{(\w+)}/g ; const regex = RegExp ( placeholdersRegex ) ; let array = regex . exec ( str ) ; let prev = 0 ; while ( array !== null ) { if ( prev !== array . index ) { elements . push ( < Text key = { elements . length } > { str . substring ( prev , array . index ) } < /Text>); } elements . push ( < Text key = { elements . length } > { vars [ array [ 0 ] . substring ( 1 , array [ 0 ] . length - 1 ) ] } < /Text>); prev = regex . lastIndex ; array = regex . exec ( str ) ; } elements . push ( < Text key = { elements . length } > { str . substring ( prev ) } < /Text>); return elements ; } ;