communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
179 lines • 9.22 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { ContextualMenuItemType, merge } from '@fluentui/react';
import { _formatString } from "../../../acs-ui-common/src";
import copy from 'copy-to-clipboard';
import React, { useCallback, useMemo, useState } from 'react';
import { ParticipantList } from './ParticipantList';
import { participantsButtonMenuPropsStyle } from './styles/ControlBar.styles';
import { useLocale } from '../localization';
import { ControlBarButton } from './ControlBarButton';
import { useIdentifiers } from '../identifiers';
import { _HighContrastAwareIcon } from './HighContrastAwareIcon';
import { _preventDismissOnEvent as preventDismissOnEvent } from "../../../acs-ui-common/src";
import { Announcer } from './Announcer';
/**
* A button to show a menu with calling or chat participants.
*
* Can be used with {@link ControlBar}.
*
* This button contains dropdown menu items defined through its property `menuProps`. By default, it can display the number of remote participants with the full list
* as sub-menu and an option to mute all participants, as well as a copy-to-clipboard button to copy the call invitation URL.
* This `menuProps` can be fully redefined and its property is of type [IContextualMenuProps](https://developer.microsoft.com/fluentui#/controls/web/contextualmenu#IContextualMenuProps).
*
* @public
*/
export const ParticipantsButton = (props) => {
var _a, _b, _c, _d;
const { callInvitationURL, styles, onMuteAll, onRenderIcon, onRenderParticipantList, participants, myUserId, excludeMe, onRenderParticipant, onRenderAvatar, onRemoveParticipant, onFetchParticipantMenuItems, showParticipantOverflowTooltip } = props;
const disabled = props.disabled;
const [copyInviteLinkAnnouncerStrings, setCopyInviteLinkAnnouncerStrings] = useState('');
const onRenderPeopleIcon = () => (React.createElement(_HighContrastAwareIcon, { disabled: disabled, iconName: "ControlButtonParticipants" }));
const ids = useIdentifiers();
const onMuteAllCallback = useCallback(() => {
if (onMuteAll) {
onMuteAll();
}
}, [onMuteAll]);
const defaultParticipantList = useCallback(() => {
var _a;
return (React.createElement(ParticipantList, { participants: participants, myUserId: myUserId, excludeMe: excludeMe, onRenderParticipant: onRenderParticipant, onRenderAvatar: onRenderAvatar, onRemoveParticipant: onRemoveParticipant, onFetchParticipantMenuItems: onFetchParticipantMenuItems, styles: (_a = styles === null || styles === void 0 ? void 0 : styles.menuStyles) === null || _a === void 0 ? void 0 : _a.participantListStyles, showParticipantOverflowTooltip: showParticipantOverflowTooltip }));
}, [
excludeMe,
myUserId,
onRemoveParticipant,
onRenderAvatar,
onRenderParticipant,
participants,
(_a = styles === null || styles === void 0 ? void 0 : styles.menuStyles) === null || _a === void 0 ? void 0 : _a.participantListStyles,
onFetchParticipantMenuItems,
showParticipantOverflowTooltip
]);
const onCopyCallback = useCallback(() => {
if (callInvitationURL) {
return copy(callInvitationURL);
}
return false;
}, [callInvitationURL]);
const localeStrings = useLocale().strings.participantsButton;
const strings = useMemo(() => (Object.assign(Object.assign({}, localeStrings), props.strings)), [localeStrings, props.strings]);
const participantCount = participants.length;
/**
* sets the announcement string for when the link is copied.
*/
const toggleAnnouncerString = useCallback(() => {
setCopyInviteLinkAnnouncerStrings(strings.copyInviteLinkActionedAriaLabel);
/**
* Clears the announcer string after the user clicks the
* copyInviteLink button allowing it to be re-announced.
*/
setTimeout(() => {
setCopyInviteLinkAnnouncerStrings('');
}, 3000);
}, [strings.copyInviteLinkActionedAriaLabel]);
const generateDefaultParticipantsSubMenuProps = useCallback(() => {
var _a;
const items = [];
if (participantCount > 0) {
items.push({
key: 'participantListMenuItemKey',
onRender: onRenderParticipantList !== null && onRenderParticipantList !== void 0 ? onRenderParticipantList : defaultParticipantList
});
items.push({ key: 'participantsDivider1', itemType: ContextualMenuItemType.Divider });
if (onMuteAll) {
items.push({
key: 'muteAllKey',
text: strings.muteAllButtonLabel,
title: strings.muteAllButtonLabel,
styles: (_a = styles === null || styles === void 0 ? void 0 : styles.menuStyles) === null || _a === void 0 ? void 0 : _a.menuItemStyles,
iconProps: { iconName: 'MicOff2' },
onClick: onMuteAllCallback
});
}
}
return items;
}, [
participantCount,
onRenderParticipantList,
defaultParticipantList,
onMuteAll,
strings.muteAllButtonLabel,
(_b = styles === null || styles === void 0 ? void 0 : styles.menuStyles) === null || _b === void 0 ? void 0 : _b.menuItemStyles,
onMuteAllCallback
]);
const defaultMenuProps = useMemo(() => {
var _a, _b;
const menuProps = {
title: strings.menuHeader,
ariaLabel: strings.menuHeader,
styles: merge(participantsButtonMenuPropsStyle, styles === null || styles === void 0 ? void 0 : styles.menuStyles),
items: [],
calloutProps: {
preventDismissOnEvent
}
};
if (participantCount > 0) {
const participantIds = participants.map((p) => p.userId);
let participantCountWithoutMe = participantIds.length;
if (excludeMe) {
participantCountWithoutMe -= 1;
}
menuProps.items.push({
key: 'participantCountKey',
name: _formatString(strings.participantsListButtonLabel, { numParticipants: `${participantCountWithoutMe}` }),
itemProps: { styles: (_a = styles === null || styles === void 0 ? void 0 : styles.menuStyles) === null || _a === void 0 ? void 0 : _a.menuItemStyles },
iconProps: { iconName: 'People' },
subMenuProps: {
items: generateDefaultParticipantsSubMenuProps(),
calloutProps: {
styles: {
root: {
// Confine the menu to the parents bounds.
// More info: https://github.com/microsoft/fluentui/issues/18835
maxWidth: '100%'
}
},
style: {
maxHeight: '20rem'
},
// Disable dismiss on resize to work around a couple Fluent UI bugs
// See reasoning in the props for the parent menu.
preventDismissOnEvent
}
},
'data-ui-id': ids.participantButtonPeopleMenuItem
});
}
if (callInvitationURL) {
menuProps.items.push({
key: 'InviteLinkKey',
name: strings.copyInviteLinkButtonLabel,
title: strings.copyInviteLinkButtonLabel,
itemProps: { styles: (_b = styles === null || styles === void 0 ? void 0 : styles.menuStyles) === null || _b === void 0 ? void 0 : _b.menuItemStyles },
iconProps: { iconName: 'Link' },
onClick: () => {
onCopyCallback();
toggleAnnouncerString();
}
});
}
return menuProps;
}, [
strings.menuHeader,
strings.participantsListButtonLabel,
strings.copyInviteLinkButtonLabel,
styles === null || styles === void 0 ? void 0 : styles.menuStyles,
participantCount,
callInvitationURL,
participants,
excludeMe,
ids.participantButtonPeopleMenuItem,
generateDefaultParticipantsSubMenuProps,
onCopyCallback,
toggleAnnouncerString
]);
return (React.createElement(React.Fragment, null,
React.createElement(Announcer, { announcementString: copyInviteLinkAnnouncerStrings, ariaLive: 'polite' }),
React.createElement(ControlBarButton, Object.assign({}, props, { disabled: disabled, menuProps: (_c = props.menuProps) !== null && _c !== void 0 ? _c : defaultMenuProps, menuIconProps: { hidden: true }, onRenderIcon: onRenderIcon !== null && onRenderIcon !== void 0 ? onRenderIcon : onRenderPeopleIcon, strings: strings, "aria-label": strings.ariaLabel, labelKey: (_d = props.labelKey) !== null && _d !== void 0 ? _d : 'participantsButtonLabel' }))));
};
//# sourceMappingURL=ParticipantsButton.js.map