UNPKG

communication-react-19

Version:

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

126 lines 7.14 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import React, { useEffect, useRef, useState, useCallback } from 'react'; import { Persona, PersonaSize, Stack, mergeStyles, useTheme } from '@fluentui/react'; import { mergeClasses } from '@fluentui/react-components'; import { mentionPopoverContainerStyle, headerStyleThemed, suggestionItemStackStyle, suggestionItemWrapperStyle, useSuggestionListStyle } from './styles/MentionPopover.style'; /* @conditional-compile-remove(mention) */ import { useIdentifiers } from '../identifiers'; import { useLocale } from '../localization'; import { useDefaultStackStyles } from './styles/Stack.style'; /** * Component to render a pop-up of mention suggestions. * * @internal */ export const _MentionPopover = (props) => { const { suggestions, activeSuggestionIndex, title, target, targetPositionOffset, onRenderSuggestionItem, onSuggestionSelected, onDismiss, location } = props; const theme = useTheme(); /* @conditional-compile-remove(mention) */ const ids = useIdentifiers(); const localeStrings = useLocale().strings; const popoverRef = useRef(); const suggestionsListRef = useRef(null); const [position, setPosition] = useState(); const [hoveredSuggestion, setHoveredSuggestion] = useState(undefined); const suggestionListStyle = useSuggestionListStyle(); const defaultStackStyles = useDefaultStackStyles(); const dismissPopoverWhenClickingOutside = useCallback((e) => { const target = e.target; if (popoverRef.current && !popoverRef.current.contains(target)) { onDismiss && onDismiss(); } }, [onDismiss]); useEffect(() => { if (suggestionsListRef.current && activeSuggestionIndex !== undefined && activeSuggestionIndex >= 0) { const selectedItem = suggestionsListRef.current.children[activeSuggestionIndex]; if (selectedItem) { selectedItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } }, [activeSuggestionIndex]); useEffect(() => { window && window.addEventListener('click', dismissPopoverWhenClickingOutside); return () => { window && window.removeEventListener('click', dismissPopoverWhenClickingOutside); }; }, [dismissPopoverWhenClickingOutside]); // Determine popover position useEffect(() => { var _a, _b, _c, _d, _e, _f, _g, _h; const rect = (_a = target === null || target === void 0 ? void 0 : target.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect(); const maxWidth = 200; const finalPosition = { maxWidth }; // Figure out whether it will fit horizontally const leftOffset = (_b = targetPositionOffset === null || targetPositionOffset === void 0 ? void 0 : targetPositionOffset.left) !== null && _b !== void 0 ? _b : 0; if (leftOffset + maxWidth > ((_c = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _c !== void 0 ? _c : 0)) { finalPosition.right = ((_d = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _d !== void 0 ? _d : 0) - leftOffset; } else { finalPosition.left = leftOffset; } // Offset between cursor and mention popover const verticalOffset = 4; if (location === 'below') { finalPosition.top = ((_e = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _e !== void 0 ? _e : 0) + ((_f = targetPositionOffset === null || targetPositionOffset === void 0 ? void 0 : targetPositionOffset.top) !== null && _f !== void 0 ? _f : 0) + verticalOffset; } else { // (location === 'above') finalPosition.bottom = ((_g = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _g !== void 0 ? _g : 0) - ((_h = targetPositionOffset === null || targetPositionOffset === void 0 ? void 0 : targetPositionOffset.top) !== null && _h !== void 0 ? _h : 0) + verticalOffset; } setPosition(finalPosition); }, [location, target, targetPositionOffset]); const handleOnKeyDown = useCallback((e) => { switch (e.key) { case 'Escape': onDismiss && onDismiss(); break; default: break; } }, [onDismiss]); const personaRenderer = useCallback((displayName) => { const avatarOptions = { text: displayName.trim(), size: PersonaSize.size24, initialsColor: theme.palette.neutralLight, initialsTextColor: theme.palette.neutralSecondary, showOverflowTooltip: false, showUnknownPersonaCoin: false }; return React.createElement(Persona, Object.assign({}, avatarOptions)); }, [theme]); const defaultOnRenderSuggestionItem = useCallback((suggestion, onSuggestionSelected, active) => { return (React.createElement("div", { "data-is-focusable": true, "data-ui-id": ids.mentionSuggestionItem, key: suggestion.id, onClick: () => onSuggestionSelected(suggestion), onMouseEnter: () => setHoveredSuggestion(suggestion), onMouseLeave: () => setHoveredSuggestion(undefined), onKeyDown: (e) => { handleOnKeyDown(e); }, className: suggestionItemWrapperStyle(theme) }, React.createElement(Stack, { horizontal: true, className: suggestionItemStackStyle(theme, (hoveredSuggestion === null || hoveredSuggestion === void 0 ? void 0 : hoveredSuggestion.id) === suggestion.id, active) }, personaRenderer(suggestion.displayText)))); }, [ handleOnKeyDown, theme, /* @conditional-compile-remove(mention) */ ids, hoveredSuggestion, personaRenderer ]); const getHeaderTitle = useCallback(() => { if (title) { return title; } /* @conditional-compile-remove(mention) */ return localeStrings.mentionPopover.mentionPopoverHeader; return ''; }, [localeStrings, title]); return (React.createElement("div", { ref: popoverRef }, position && (React.createElement(Stack, { "data-testid": 'mention-suggestion-list-container', className: mergeStyles({ maxHeight: 212, maxWidth: position.maxWidth }, mentionPopoverContainerStyle(theme), Object.assign(Object.assign({}, position), { position: 'absolute' })) }, React.createElement(Stack.Item, { styles: headerStyleThemed(theme), "aria-label": title }, getHeaderTitle()), React.createElement("div", { className: mergeClasses(defaultStackStyles.root, suggestionListStyle.root), "data-ui-id": ids.mentionSuggestionList, ref: suggestionsListRef }, suggestions.map((suggestion, index) => { const active = index === activeSuggestionIndex; return onRenderSuggestionItem ? onRenderSuggestionItem(suggestion, onSuggestionSelected, active) : defaultOnRenderSuggestionItem(suggestion, onSuggestionSelected, active); })))))); }; //# sourceMappingURL=MentionPopover.js.map