UNPKG

communication-react-19

Version:

React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)

99 lines 7.71 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { ContextualMenu, DirectionalHint, Icon, mergeStyles, Persona, PersonaSize, Stack, Text } from '@fluentui/react'; import React, { useMemo, useRef, useState } from 'react'; import { useIdentifiers } from '../identifiers'; import { useLocale } from '../localization'; import { useTheme } from '../theming'; import { displayNoneStyle, iconContainerStyle, iconStyles, meContainerStyle, menuButtonContainerStyle, participantItemContainerStyle, participantStateStringStyles } from './styles/ParticipantItem.styles'; import { _preventDismissOnEvent as preventDismissOnEvent } from "../../../acs-ui-common/src"; import { useId } from '@fluentui/react-hooks'; /** * Component to render a calling or chat participant. * * Displays the participant's avatar, displayName and status as well as optional icons and context menu. * * @public */ export const ParticipantItem = (props) => { var _a, _b, _c; const { userId, displayName, onRenderAvatar, menuItems, onRenderIcon, presence, styles, me, onClick, showParticipantOverflowTooltip } = props; const [itemHovered, setItemHovered] = useState(false); const [menuHidden, setMenuHidden] = useState(true); const containerRef = useRef(null); const theme = useTheme(); const localeStrings = useLocale().strings.participantItem; const ids = useIdentifiers(); const participantItemId = useId(); const participantItemFlyoutId = useId(); const hasFlyout = !!(menuItems && (menuItems === null || menuItems === void 0 ? void 0 : menuItems.length) > 0); const strings = Object.assign(Object.assign({}, localeStrings), props.strings); const participantStateString = formatParticipantStateString(props, strings); const showMenuIcon = !participantStateString && (itemHovered || !menuHidden) && hasFlyout; // For 'me' show empty name so avatar will get 'Person' icon, when there is no name const meAvatarText = (displayName === null || displayName === void 0 ? void 0 : displayName.trim()) || ''; const avatarOptions = { text: me ? meAvatarText : (displayName === null || displayName === void 0 ? void 0 : displayName.trim()) || strings.displayNamePlaceholder, size: PersonaSize.size32, presence: presence, initialsTextColor: 'white', showOverflowTooltip: showParticipantOverflowTooltip, showUnknownPersonaCoin: !me && (!(displayName === null || displayName === void 0 ? void 0 : displayName.trim()) || displayName === strings.displayNamePlaceholder) }; const avatar = onRenderAvatar ? (onRenderAvatar(userId !== null && userId !== void 0 ? userId : '', avatarOptions)) : (React.createElement(Persona, Object.assign({ className: mergeStyles({ // Prevents persona text from being vertically truncated if a global line height is less than 1.15. lineHeight: '1.15rem' }, styles === null || styles === void 0 ? void 0 : styles.avatar) }, avatarOptions))); const meTextStyle = useMemo(() => mergeStyles(meContainerStyle, { color: theme.palette.neutralSecondary }, styles === null || styles === void 0 ? void 0 : styles.me), [theme.palette.neutralSecondary, styles === null || styles === void 0 ? void 0 : styles.me]); const contextualMenuStyle = useMemo(() => mergeStyles({ background: theme.palette.neutralLighterAlt }, styles === null || styles === void 0 ? void 0 : styles.menu), [theme.palette.neutralLighterAlt, styles === null || styles === void 0 ? void 0 : styles.menu]); const infoContainerStyle = useMemo(() => mergeStyles(iconContainerStyle, { color: theme.palette.neutralSecondary, marginLeft: 'auto' }, styles === null || styles === void 0 ? void 0 : styles.iconContainer), [theme.palette.neutralSecondary, styles === null || styles === void 0 ? void 0 : styles.iconContainer]); const onDismissMenu = () => { setItemHovered(false); setMenuHidden(true); }; const menuButton = useMemo(() => (React.createElement(Stack, { horizontal: true, horizontalAlign: "end", className: mergeStyles(menuButtonContainerStyle, { color: theme.palette.neutralPrimary }), title: strings.menuTitle, "aria-controls": participantItemFlyoutId, "data-ui-id": ids.participantItemMenuButton }, React.createElement(Icon, { iconName: "ParticipantItemOptionsHovered", className: mergeStyles(iconStyles, !showMenuIcon ? displayNoneStyle : {}) }))), [ theme.palette.neutralPrimary, strings.menuTitle, participantItemFlyoutId, ids.participantItemMenuButton, showMenuIcon ]); return (React.createElement("div", { ref: containerRef, role: 'menuitem', id: participantItemId, "aria-label": (_b = (hasFlyout ? (_a = props.strings) === null || _a === void 0 ? void 0 : _a.participantItemWithMoreOptionsAriaLabel : undefined)) !== null && _b !== void 0 ? _b : (_c = props.strings) === null || _c === void 0 ? void 0 : _c.participantItemAriaLabel, "aria-labelledby": `${props.ariaLabelledBy} ${participantItemId}`, "aria-expanded": !menuHidden, "aria-disabled": hasFlyout || props.onClick ? false : true, "aria-haspopup": hasFlyout ? true : undefined, "aria-controls": participantItemFlyoutId, "data-is-focusable": hasFlyout, "data-ui-id": "participant-item", className: mergeStyles(participantItemContainerStyle({ clickable: hasFlyout }, theme), styles === null || styles === void 0 ? void 0 : styles.root), onMouseEnter: () => setItemHovered(true), onMouseLeave: () => setItemHovered(false), onClick: () => { if (!participantStateString) { setItemHovered(true); setMenuHidden(false); onClick === null || onClick === void 0 ? void 0 : onClick(props); } if (!menuHidden) { onDismissMenu(); } }, onKeyDown: (event) => { if (event.key === 'Enter' || event.key === ' ') { setMenuHidden(false); } }, tabIndex: hasFlyout ? 0 : undefined }, React.createElement(Stack, { horizontal: true, className: mergeStyles({ flexGrow: 1, maxWidth: '100%', alignItems: 'center' }) }, avatar, me && React.createElement(Text, { className: meTextStyle }, strings.isMeText), React.createElement(Stack, { horizontal: true, className: mergeStyles(infoContainerStyle) }, !showMenuIcon && onRenderIcon && onRenderIcon(props), !me && participantStateString ? (React.createElement(Text, { "data-ui-id": "participant-item-state-string", className: mergeStyles(participantStateStringStyles) }, participantStateString)) : (React.createElement(React.Fragment, null, hasFlyout && (React.createElement(React.Fragment, null, menuButton, React.createElement(ContextualMenu, { id: participantItemFlyoutId, items: menuItems, hidden: menuHidden, target: containerRef, onItemClick: onDismissMenu, onDismiss: onDismissMenu, directionalHint: DirectionalHint.bottomRightEdge, className: contextualMenuStyle, calloutProps: { preventDismissOnEvent } }))))))))); }; /** @private */ export const formatParticipantStateString = (props, strings) => { return props.participantState === 'EarlyMedia' || props.participantState === 'Ringing' ? strings === null || strings === void 0 ? void 0 : strings.participantStateRinging : props.participantState === 'Hold' ? strings === null || strings === void 0 ? void 0 : strings.participantStateHold : undefined; }; //# sourceMappingURL=ParticipantItem.js.map